noImplicitReturns: enabled implicit return checking

Fixed all 32 errors by:
- Adding '| undefined' to return type signatures where functions could return undefined
- Changing 'return;' to 'return undefined;' (explicit instead of implicit)
- Adding 'return undefined;' at function ends where needed

Functions updated:
- src/cluster/cluster-setup.ts: getClusterObject, getScryptedClusterMode
- src/fetch/index.ts: getHttpFetchAccept
- src/infer-defaults.ts: inferRoomFromName, getProvidedRoomOrDefault, getDisplayRoom
- src/level.ts: tryGet
- src/plugin/plugin-api.ts: getDeviceById
- src/plugin/plugin-device.ts: findMethod, findMixin
- src/plugin/plugin-host-api.ts: setDeviceProperty
- src/plugin/system.ts: getOwnPropertyDescriptor
- src/rpc.ts: getIteratorNext
- src/runtime.ts: getAccessControlAllowOrigin, getDeviceLogger, getEndpointPluginData, getAccessControls, invalidatePluginDevice, rebuildPluginDeviceMixinTable, installNpm, getPluginHostForDevice, getDevice
- src/scrypted-server-main.ts: getDefaultAuthentication, checkValidUserToken
- src/services/addresses.ts: getLocalAddresses
- src/state.ts: setState
This commit is contained in:
Koushik Dutta
2026-04-02 13:33:09 -07:00
parent fd0b3a0b8f
commit dd59ac40b6
14 changed files with 59 additions and 53 deletions

View File

@@ -18,10 +18,10 @@ export function isClusterAddress(address: string) {
return !address || address === process.env.SCRYPTED_CLUSTER_ADDRESS;
}
export function getClusterObject(clusterId: string, value: any) {
export function getClusterObject(clusterId: string, value: any): ClusterObject | undefined {
const clusterObject: ClusterObject = value?.__cluster;
if (clusterObject?.id !== clusterId)
return;
return undefined;
return clusterObject;
}
@@ -400,7 +400,7 @@ export function setupCluster(peer: RpcPeer) {
export type InitializeCluster = (cluster: { clusterId: string, clusterSecret: string, clusterWorkerId: string, }) => Promise<void>;
export function getScryptedClusterMode(): ['server' | 'client', string, number] {
export function getScryptedClusterMode(): ['server' | 'client', string, number] | undefined {
const mode = process.env.SCRYPTED_CLUSTER_MODE as 'server' | 'client';
if (!mode) {
@@ -416,7 +416,7 @@ export function getScryptedClusterMode(): ['server' | 'client', string, number]
console.warn('SCRYPTED_CLUSTER_SECRET is set but SCRYPTED_CLUSTER_MODE is not set. This setting will be ignored.');
delete process.env.SCRYPTED_CLUSTER_SECRET;
}
return;
return undefined;
}
if (!['server', 'client'].includes(mode))

View File

@@ -66,14 +66,14 @@ export type fetcher<B, M> = <T extends HttpFetchOptions<B>>(options: T) => Promi
>>;
export function getHttpFetchAccept(responseType: HttpFetchResponseType | undefined) {
export function getHttpFetchAccept(responseType: HttpFetchResponseType | undefined): string | undefined {
switch (responseType) {
case 'json':
return 'application/json';
case 'text':
return 'text/plain';
}
return;
return undefined;
}
export function hasHeader(headers: [string, string][], key: string) {

View File

@@ -96,24 +96,24 @@ const roomHints: { [hint: string]: string } = {
'Laundry': 'Laundry Room',
}
export function inferRoomFromName(name: string): string {
export function inferRoomFromName(name: string): string | undefined {
if (!name)
return;
return undefined;
for (const hint of Object.keys(roomHints)) {
if (name.includes(hint))
return roomHints[hint];
}
return undefined;
}
export function getProvidedRoomOrDefault(pluginDevice: PluginDevice): string {
export function getProvidedRoomOrDefault(pluginDevice: PluginDevice): string | undefined {
const providedRoom = getState(pluginDevice, ScryptedInterfaceProperty.providedRoom);
if (providedRoom)
return providedRoom;
const room = inferRoomFromName(getDisplayName(pluginDevice));
return room;
return inferRoomFromName(getDisplayName(pluginDevice));
}
export function getDisplayRoom(pluginDevice: PluginDevice): string {
export function getDisplayRoom(pluginDevice: PluginDevice): string | undefined {
const room = getState(pluginDevice, ScryptedInterfaceProperty.room);
if (room)
return room;

View File

@@ -31,7 +31,7 @@ export class WrappedLevel extends Level<string, string | number> {
}
async tryGet<T>(documentConstructor: new () => T, _id: any, options?: GetOptions<string, string | number>): Promise<T> {
async tryGet<T>(documentConstructor: new () => T, _id: any, options?: GetOptions<string, string | number>): Promise<T | undefined> {
try {
const _documentType = documentConstructor.name;
const key = `${_documentType}/${_id}`;
@@ -39,6 +39,7 @@ export class WrappedLevel extends Level<string, string | number> {
return createLevelDocument(documentConstructor, json);
}
catch (e) {
return undefined;
}
}

View File

@@ -108,9 +108,9 @@ export class PluginAPIProxy extends PluginAPIManagedListeners implements PluginA
this.acl?.deny();
return this.api.setStorage(nativeId, storage);
}
getDeviceById(id: string): Promise<ScryptedDevice> {
getDeviceById(id: string): Promise<ScryptedDevice | undefined> {
if (this.acl?.shouldRejectDevice(id))
return;
return undefined;
return this.api.getDeviceById(id);
}
setDeviceProperty(id: string, property: ScryptedInterfaceProperty, value: any): Promise<void> {

View File

@@ -382,7 +382,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
throw new PluginError(`${method} not implemented`)
}
async findMethod(method: string) {
async findMethod(method: string): Promise<{ mixin: MixinTable, entry: MixinTableEntry } | undefined> {
for (const mixin of this.mixinTable) {
const entry = await mixin.entry;
if (!entry.methods) {
@@ -402,9 +402,10 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
return { mixin, entry };
}
}
return undefined;
}
async findMixin(iface: string) {
async findMixin(iface: string): Promise<{ mixin: MixinTable, entry: MixinTableEntry } | undefined> {
for (const mixin of this.mixinTable) {
const entry = await mixin.entry;
const { interfaces } = entry;
@@ -412,6 +413,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
return { mixin, entry };
}
}
return undefined;
}
async apply(target: any, thisArg: any, argArray?: any): Promise<any> {

View File

@@ -103,7 +103,7 @@ export class PluginHostAPI extends PluginAPIManagedListeners implements PluginAP
return this.scrypted.getComponent(id);
}
setDeviceProperty(id: string, property: ScryptedInterfaceProperty, value: any): Promise<void> {
async setDeviceProperty(id: string, property: ScryptedInterfaceProperty, value: any): Promise<void> {
switch (property) {
case ScryptedInterfaceProperty.room:
case ScryptedInterfaceProperty.type:

View File

@@ -26,7 +26,7 @@ class DeviceProxyHandler implements PrimitiveProxyHandler<any> {
return [...methods, ...properties];
}
getOwnPropertyDescriptor(target: any, p: string | symbol): PropertyDescriptor {
getOwnPropertyDescriptor(target: any, p: string | symbol): PropertyDescriptor | undefined {
const interfaces = new Set<string>(this.systemManager.state[this.id].interfaces.value);
const methods = getInterfaceMethods(this.systemManager.descriptors || ScryptedInterfaceDescriptors, interfaces);
const prop = p.toString();
@@ -42,6 +42,7 @@ class DeviceProxyHandler implements PrimitiveProxyHandler<any> {
value: this.systemManager.state[this.id][prop]?.value
}
}
return undefined;
}
deleteProperty(target: any, p: string | symbol): boolean {

View File

@@ -350,9 +350,9 @@ export class RpcPeer {
// return value?.[RpcPeer.PROPERTY_PROXY_PROPERTIES];
// }
static getIteratorNext(target: any): string {
static getIteratorNext(target: any): string | undefined {
if (!target[Symbol.asyncIterator])
return;
return undefined;
const proxyProps = target[this.PROPERTY_PROXY_PROPERTIES]?.[Symbol.asyncIterator.toString()];
return proxyProps?.next || 'next';
}

View File

@@ -196,7 +196,7 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With, Access-Control-Request-Method');
}
getAccessControlAllowOrigin(headers: http.IncomingHttpHeaders) {
getAccessControlAllowOrigin(headers: http.IncomingHttpHeaders): string | undefined {
let { origin, referer } = headers;
if (!origin && referer) {
try {
@@ -204,22 +204,22 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
origin = u.origin;
}
catch (e) {
return;
return undefined;
}
}
if (!origin)
return;
return undefined;
const servers: string[] = process.env.SCRYPTED_ACCESS_CONTROL_ALLOW_ORIGINS?.split(',') || [];
servers.push(...Object.values(this.corsControl.origins).flat());
if (!servers.includes(origin))
return;
return undefined;
return origin;
}
getDeviceLogger(device: PluginDevice): Logger {
getDeviceLogger(device: PluginDevice): Logger | undefined {
if (!device)
return;
return undefined;
return this.devicesLogger.getLogger(device._id, getState(device, ScryptedInterfaceProperty.name));
}
@@ -273,7 +273,7 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
this.connectRPCObjectIO.handleRequest(reqany, res);
}
async getEndpointPluginData(req: Request, endpoint: string, isUpgrade: boolean, isEngineIOEndpoint: boolean): Promise<HttpPluginData> {
async getEndpointPluginData(req: Request, endpoint: string, isUpgrade: boolean, isEngineIOEndpoint: boolean): Promise<HttpPluginData | undefined> {
const ret = await this.getPluginForEndpoint(endpoint);
if (req.url.indexOf('/engine.io/api') !== -1)
return ret;
@@ -283,12 +283,12 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
// check if upgrade requests can be handled. must be websocket.
if (isUpgrade) {
if (!pluginDevice?.state.interfaces.value.includes(ScryptedInterface.EngineIOHandler)) {
return;
return undefined;
}
}
else {
if (!isEngineIOEndpoint && !pluginDevice?.state.interfaces.value.includes(ScryptedInterface.HttpRequestHandler)) {
return;
return undefined;
}
}
@@ -380,18 +380,19 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
return packageJson;
}
async getAccessControls(username: string) {
async getAccessControls(username: string): Promise<AccessControls | undefined> {
if (!username)
return;
return undefined;
const user = await this.datastore.tryGet(ScryptedUser, username);
if (user?.aclId) {
const accessControl = this.getDevice<SU>(user.aclId);
const acls = await accessControl.getScryptedUserAccessControl();
if (!acls)
return;
return undefined;
return new AccessControls(acls);
}
return undefined;
}
async handleEngineIOEndpoint(req: Request, res: ServerResponse & { locals: any }, endpointRequest: HttpRequest, pluginData: HttpPluginData) {
@@ -472,20 +473,18 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
this.invalidatePluginMixins(pluginId);
}
// should this be async?
invalidatePluginDevice(id: string) {
invalidatePluginDevice(id: string): DeviceProxyPair | undefined {
const proxyPair = this.devices[id];
if (!proxyPair)
return;
return undefined;
proxyPair.handler.invalidate();
return proxyPair;
}
// should this be async?
rebuildPluginDeviceMixinTable(id: string) {
rebuildPluginDeviceMixinTable(id: string): DeviceProxyPair | undefined {
const proxyPair = this.devices[id];
if (!proxyPair)
return;
return undefined;
proxyPair.handler.rebuildMixinTable();
return proxyPair;
}
@@ -541,11 +540,11 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
return ret;
}
async installNpm(pkg: string, version?: string, installedSet?: Set<string>): Promise<PluginHost> {
async installNpm(pkg: string, version?: string, installedSet?: Set<string>): Promise<PluginHost | undefined> {
if (!installedSet)
installedSet = new Set();
if (installedSet.has(pkg))
return;
return undefined;
installedSet.add(pkg);
const registry = await getNpmPackageInfo(pkg);
@@ -774,21 +773,21 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
return Object.values(this.pluginDevices).filter(e => e.state && e.pluginId === pluginId)
}
getPluginHostForDeviceId(id: string): PluginHost {
getPluginHostForDeviceId(id: string): PluginHost | undefined {
const device = this.pluginDevices[id];
if (!device)
return;
return undefined;
return this.plugins[device.pluginId];
}
getDevice<T>(id: string): T & ScryptedDevice {
getDevice<T>(id: string): (T & ScryptedDevice) | undefined {
const device = this.devices[id];
if (device)
return device.proxy as any;
if (!this.pluginDevices[id]) {
console.warn('device not found', id);
return;
return undefined;
}
const handler = new PluginDeviceProxyHandler(this, id);

View File

@@ -236,7 +236,7 @@ async function start(mainFilename: string, options?: {
};
}
const getDefaultAuthentication = (req: Request) => {
const getDefaultAuthentication = (req: Request): ScryptedUser | undefined => {
const defaultAuthentication = !req.query.disableDefaultAuthentication && process.env.SCRYPTED_DEFAULT_AUTHENTICATION;
if (defaultAuthentication) {
const referer = req.headers.referer;
@@ -244,7 +244,7 @@ async function start(mainFilename: string, options?: {
try {
const u = new URL(referer);
if (u.searchParams.has('disableDefaultAuthentication'))
return;
return undefined;
}
catch (e) {
// no/invalid referer, allow the default auth
@@ -252,6 +252,7 @@ async function start(mainFilename: string, options?: {
}
return scrypted.usersService.users.get(defaultAuthentication);
}
return undefined;
}
app.use(async (req, res, next) => {
@@ -544,9 +545,9 @@ async function start(mainFilename: string, options?: {
return req.secure ? 'login_user_token' : 'login_user_token_insecure';
};
const checkValidUserToken = (token: string) => {
const checkValidUserToken = (token: string): UserToken | undefined => {
if (!token)
return;
return undefined;
try {
const userToken = UserToken.validateToken(token);
if (scrypted.usersService.users.has(userToken.username))
@@ -555,6 +556,7 @@ async function start(mainFilename: string, options?: {
catch (e) {
// console.warn('invalid token', e.message);
}
return undefined;
}
const getSignedLoginUserToken = (req: Request<any>) => {

View File

@@ -25,11 +25,11 @@ export class AddressSettings {
await this.scrypted.datastore.upsert(localAddresses);
}
async getLocalAddresses(raw?: boolean): Promise<string[]> {
async getLocalAddresses(raw?: boolean): Promise<string[] | undefined> {
const settings = await this.scrypted.datastore.tryGet(Settings, 'localAddresses');
if (!settings?.value?.[0])
return;
return undefined;
const ret: string[] = [];
const networkInterfaces = os.networkInterfaces();

View File

@@ -266,10 +266,10 @@ function isSameValue(value1: any, value2: any) {
return value1 === value2 || JSON.stringify(value1) === JSON.stringify(value2);
}
export function setState(pluginDevice: PluginDevice, property: string, value: any): boolean {
export function setState(pluginDevice: PluginDevice, property: string, value: any): boolean | undefined {
// device may have been deleted.
if (!pluginDevice.state)
return;
return undefined;
if (!pluginDevice.state[property])
pluginDevice.state[property] = {};
const state = pluginDevice.state[property];

View File

@@ -11,6 +11,7 @@
"strictFunctionTypes": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"outDir": "./dist",
"esModuleInterop": true,
"moduleResolution": "NodeNext",