From dd59ac40b6b19cd9cde22c1eb85c3fcf6b75bae9 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 2 Apr 2026 13:33:09 -0700 Subject: [PATCH] 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 --- server/src/cluster/cluster-setup.ts | 8 ++--- server/src/fetch/index.ts | 4 +-- server/src/infer-defaults.ts | 12 +++---- server/src/level.ts | 3 +- server/src/plugin/plugin-api.ts | 4 +-- server/src/plugin/plugin-device.ts | 6 ++-- server/src/plugin/plugin-host-api.ts | 2 +- server/src/plugin/system.ts | 3 +- server/src/rpc.ts | 4 +-- server/src/runtime.ts | 47 ++++++++++++++-------------- server/src/scrypted-server-main.ts | 10 +++--- server/src/services/addresses.ts | 4 +-- server/src/state.ts | 4 +-- server/tsconfig.json | 1 + 14 files changed, 59 insertions(+), 53 deletions(-) diff --git a/server/src/cluster/cluster-setup.ts b/server/src/cluster/cluster-setup.ts index a37695205..17ab2f2ea 100644 --- a/server/src/cluster/cluster-setup.ts +++ b/server/src/cluster/cluster-setup.ts @@ -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; -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)) diff --git a/server/src/fetch/index.ts b/server/src/fetch/index.ts index 9a8bb3f56..85206436d 100644 --- a/server/src/fetch/index.ts +++ b/server/src/fetch/index.ts @@ -66,14 +66,14 @@ export type fetcher = >(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) { diff --git a/server/src/infer-defaults.ts b/server/src/infer-defaults.ts index 17a097b75..d38eeb8e5 100644 --- a/server/src/infer-defaults.ts +++ b/server/src/infer-defaults.ts @@ -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; diff --git a/server/src/level.ts b/server/src/level.ts index 0c8ef4415..f19a06784 100644 --- a/server/src/level.ts +++ b/server/src/level.ts @@ -31,7 +31,7 @@ export class WrappedLevel extends Level { } - async tryGet(documentConstructor: new () => T, _id: any, options?: GetOptions): Promise { + async tryGet(documentConstructor: new () => T, _id: any, options?: GetOptions): Promise { try { const _documentType = documentConstructor.name; const key = `${_documentType}/${_id}`; @@ -39,6 +39,7 @@ export class WrappedLevel extends Level { return createLevelDocument(documentConstructor, json); } catch (e) { + return undefined; } } diff --git a/server/src/plugin/plugin-api.ts b/server/src/plugin/plugin-api.ts index 4854a81a2..80def9673 100644 --- a/server/src/plugin/plugin-api.ts +++ b/server/src/plugin/plugin-api.ts @@ -108,9 +108,9 @@ export class PluginAPIProxy extends PluginAPIManagedListeners implements PluginA this.acl?.deny(); return this.api.setStorage(nativeId, storage); } - getDeviceById(id: string): Promise { + getDeviceById(id: string): Promise { if (this.acl?.shouldRejectDevice(id)) - return; + return undefined; return this.api.getDeviceById(id); } setDeviceProperty(id: string, property: ScryptedInterfaceProperty, value: any): Promise { diff --git a/server/src/plugin/plugin-device.ts b/server/src/plugin/plugin-device.ts index f528ef79d..0b6f7fcb7 100644 --- a/server/src/plugin/plugin-device.ts +++ b/server/src/plugin/plugin-device.ts @@ -382,7 +382,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler { 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 { 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 { return { mixin, entry }; } } + return undefined; } async apply(target: any, thisArg: any, argArray?: any): Promise { diff --git a/server/src/plugin/plugin-host-api.ts b/server/src/plugin/plugin-host-api.ts index 401df4ff1..448dc6dea 100644 --- a/server/src/plugin/plugin-host-api.ts +++ b/server/src/plugin/plugin-host-api.ts @@ -103,7 +103,7 @@ export class PluginHostAPI extends PluginAPIManagedListeners implements PluginAP return this.scrypted.getComponent(id); } - setDeviceProperty(id: string, property: ScryptedInterfaceProperty, value: any): Promise { + async setDeviceProperty(id: string, property: ScryptedInterfaceProperty, value: any): Promise { switch (property) { case ScryptedInterfaceProperty.room: case ScryptedInterfaceProperty.type: diff --git a/server/src/plugin/system.ts b/server/src/plugin/system.ts index 11561a1bf..e5b78cf39 100644 --- a/server/src/plugin/system.ts +++ b/server/src/plugin/system.ts @@ -26,7 +26,7 @@ class DeviceProxyHandler implements PrimitiveProxyHandler { return [...methods, ...properties]; } - getOwnPropertyDescriptor(target: any, p: string | symbol): PropertyDescriptor { + getOwnPropertyDescriptor(target: any, p: string | symbol): PropertyDescriptor | undefined { const interfaces = new Set(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 { value: this.systemManager.state[this.id][prop]?.value } } + return undefined; } deleteProperty(target: any, p: string | symbol): boolean { diff --git a/server/src/rpc.ts b/server/src/rpc.ts index 2232d8c0e..344fc02cb 100644 --- a/server/src/rpc.ts +++ b/server/src/rpc.ts @@ -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'; } diff --git a/server/src/runtime.ts b/server/src/runtime.ts index f7d121666..f4775af9a 100644 --- a/server/src/runtime.ts +++ b/server/src/runtime.ts @@ -196,7 +196,7 @@ export class ScryptedRuntime extends PluginHttp { 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 { 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 { this.connectRPCObjectIO.handleRequest(reqany, res); } - async getEndpointPluginData(req: Request, endpoint: string, isUpgrade: boolean, isEngineIOEndpoint: boolean): Promise { + async getEndpointPluginData(req: Request, endpoint: string, isUpgrade: boolean, isEngineIOEndpoint: boolean): Promise { 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 { // 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 { return packageJson; } - async getAccessControls(username: string) { + async getAccessControls(username: string): Promise { if (!username) - return; + return undefined; const user = await this.datastore.tryGet(ScryptedUser, username); if (user?.aclId) { const accessControl = this.getDevice(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 { 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 { return ret; } - async installNpm(pkg: string, version?: string, installedSet?: Set): Promise { + async installNpm(pkg: string, version?: string, installedSet?: Set): Promise { 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 { 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(id: string): T & ScryptedDevice { + getDevice(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); diff --git a/server/src/scrypted-server-main.ts b/server/src/scrypted-server-main.ts index 75797c8b3..c4ff9fe5c 100644 --- a/server/src/scrypted-server-main.ts +++ b/server/src/scrypted-server-main.ts @@ -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) => { diff --git a/server/src/services/addresses.ts b/server/src/services/addresses.ts index 05e2c592f..aea884adb 100644 --- a/server/src/services/addresses.ts +++ b/server/src/services/addresses.ts @@ -25,11 +25,11 @@ export class AddressSettings { await this.scrypted.datastore.upsert(localAddresses); } - async getLocalAddresses(raw?: boolean): Promise { + async getLocalAddresses(raw?: boolean): Promise { const settings = await this.scrypted.datastore.tryGet(Settings, 'localAddresses'); if (!settings?.value?.[0]) - return; + return undefined; const ret: string[] = []; const networkInterfaces = os.networkInterfaces(); diff --git a/server/src/state.ts b/server/src/state.ts index 7d009cea0..1c79ee0b0 100644 --- a/server/src/state.ts +++ b/server/src/state.ts @@ -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]; diff --git a/server/tsconfig.json b/server/tsconfig.json index 05ec2e4c9..c1e266312 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -11,6 +11,7 @@ "strictFunctionTypes": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true, + "noImplicitReturns": true, "outDir": "./dist", "esModuleInterop": true, "moduleResolution": "NodeNext",