mirror of
https://github.com/koush/scrypted.git
synced 2026-05-05 22:00:27 +01:00
plugin: add type assertions for strictNullChecks in plugin-device and remote modules
Fix strictNullChecks: - plugin-device.ts: consolidate entry/host assertions at declarations, use undefined! for proxy values, add definite assignment for mixinTable - plugin-remote.ts: add assertions for callbacks and nativeIds access - plugin-remote-worker.ts: fix clusterWorkerId as Promise<string | undefined>, add assertions for worker and options properties
This commit is contained in:
@@ -31,7 +31,7 @@ export const QueryInterfaceSymbol = Symbol("ScryptedPluginDeviceQueryInterface")
|
||||
export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
scrypted: ScryptedRuntime;
|
||||
id: string;
|
||||
mixinTable: MixinTable[];
|
||||
mixinTable!: MixinTable[];
|
||||
releasing = new Set<any>();
|
||||
|
||||
static sortInterfaces(interfaces: string[]): string[] {
|
||||
@@ -65,8 +65,8 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
async getMixinProviderId(id: string, mixinDevice: any) {
|
||||
if (this.releasing.has(mixinDevice))
|
||||
return true;
|
||||
await this.scrypted.devices[id].handler.ensureProxy();
|
||||
for (const mixin of this.scrypted.devices[id].handler.mixinTable) {
|
||||
await this.scrypted.devices[id]!.handler.ensureProxy();
|
||||
for (const mixin of this.scrypted.devices[id]!.handler.mixinTable) {
|
||||
const { proxy } = await mixin.entry;
|
||||
if (proxy === mixinDevice) {
|
||||
return mixin.mixinProviderId || id;
|
||||
@@ -78,7 +78,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
// should this be async?
|
||||
invalidate() {
|
||||
const mixinTable = this.mixinTable;
|
||||
this.mixinTable = undefined;
|
||||
this.mixinTable = undefined!;
|
||||
for (const mixinEntry of (mixinTable || [])) {
|
||||
this.invalidateEntry(mixinEntry);
|
||||
}
|
||||
@@ -102,7 +102,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
const mixins = getState(pluginDevice, ScryptedInterfaceProperty.mixins) || [];
|
||||
// iterate the new mixin table to find the last good mixin,
|
||||
// and resume creation from there.
|
||||
let lastValidMixinId: string;
|
||||
let lastValidMixinId: string | undefined;
|
||||
for (const mixinId of mixins) {
|
||||
if (!previousMixinIds.length) {
|
||||
// reached of the previous mixin table, meaning
|
||||
@@ -121,7 +121,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
|
||||
// invalidate and remove everything up to lastValidMixinId
|
||||
while (true) {
|
||||
const entry = this.mixinTable[0];
|
||||
const entry = this.mixinTable[0]!;
|
||||
if (entry.mixinProviderId === lastValidMixinId)
|
||||
break;
|
||||
this.mixinTable.shift();
|
||||
@@ -149,7 +149,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
try {
|
||||
if (!pluginDevice.nativeId) {
|
||||
const plugin = this.scrypted.plugins[pluginDevice.pluginId];
|
||||
proxy = await plugin.module;
|
||||
proxy = await plugin!.module;
|
||||
}
|
||||
else {
|
||||
const providerId = getState(pluginDevice, ScryptedInterfaceProperty.providerId);
|
||||
@@ -174,7 +174,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
})();
|
||||
|
||||
this.mixinTable.unshift({
|
||||
mixinProviderId: undefined,
|
||||
mixinProviderId: undefined!,
|
||||
entry,
|
||||
});
|
||||
}
|
||||
@@ -202,7 +202,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
});
|
||||
}
|
||||
|
||||
return this.mixinTable[0].entry.then(entry => {
|
||||
return this.mixinTable[0]!.entry.then(entry => {
|
||||
if (entry.error) {
|
||||
console.error('Mixin device creation completed with error. Merging with previous interface set to retain device descriptor.');
|
||||
const previousInterfaces = getState(pluginDevice, ScryptedInterfaceProperty.interfaces) as string[] || [];
|
||||
@@ -219,7 +219,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
|
||||
async rebuildEntry(pluginDevice: PluginDevice, mixinId: string, wrappedMixinTablePromise: Promise<MixinTable[]>): Promise<MixinTableEntry> {
|
||||
const wrappedMixinTable = await wrappedMixinTablePromise;
|
||||
const previousEntry = wrappedMixinTable[0].entry;
|
||||
const previousEntry = wrappedMixinTable[0]!.entry;
|
||||
|
||||
const type = getDisplayType(pluginDevice);
|
||||
|
||||
@@ -255,7 +255,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
passthrough: true,
|
||||
allInterfaces,
|
||||
interfaces: new Set<string>(),
|
||||
proxy: undefined as any,
|
||||
proxy: undefined!,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
const wrappedProxy = new Proxy(wrappedHandler, wrappedHandler);
|
||||
|
||||
const implementer = await (mixinProvider as any)[QueryInterfaceSymbol](ScryptedInterface.MixinProvider);
|
||||
const host = this.scrypted.getPluginHostForDeviceId(implementer);
|
||||
const host = this.scrypted.getPluginHostForDeviceId(implementer)!;
|
||||
const propertyInterfaces = getPropertyInterfaces(host.api.descriptors || ScryptedInterfaceDescriptors);
|
||||
// todo: remove this and pass the setter directly.
|
||||
const deviceState = await host.remote.createDeviceState(this.id,
|
||||
@@ -304,8 +304,8 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
passthrough: false,
|
||||
allInterfaces,
|
||||
interfaces: new Set<string>(),
|
||||
error: e,
|
||||
proxy: undefined as any,
|
||||
error: e as Error,
|
||||
proxy: undefined!,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -328,7 +328,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
if (p === RefreshSymbol || p === QueryInterfaceSymbol)
|
||||
return new Proxy(() => p, this);
|
||||
|
||||
if (ScryptedInterfaceDescriptors[ScryptedInterface.ScryptedDevice].methods.includes(prop))
|
||||
if (ScryptedInterfaceDescriptors[ScryptedInterface.ScryptedDevice]!.methods.includes(prop))
|
||||
return (this as any)[p].bind(this);
|
||||
|
||||
return new Proxy(() => prop, this);
|
||||
@@ -445,7 +445,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
|
||||
if (method === 'getReadmeMarkdown') {
|
||||
const pluginDevice = this.scrypted.findPluginDeviceById(this.id);
|
||||
if (pluginDevice && !pluginDevice.nativeId) {
|
||||
const plugin = this.scrypted.plugins[pluginDevice.pluginId];
|
||||
const plugin = this.scrypted.plugins[pluginDevice.pluginId]!;
|
||||
if (!plugin.packageJson.scrypted.interfaces.includes(ScryptedInterface.Readme)) {
|
||||
const readmePath = path.join(plugin.unzippedPath, 'README.md');
|
||||
if (fs.existsSync(readmePath)) {
|
||||
|
||||
@@ -85,7 +85,7 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS] = (_api as any)[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS];
|
||||
|
||||
override setStorage(nativeId: string, storage: { [key: string]: any; }): Promise<void> {
|
||||
const id = deviceManager.nativeIds.get(nativeId).id;
|
||||
const id = deviceManager.nativeIds.get(nativeId)!.id;
|
||||
for (const r of forks) {
|
||||
r.setNativeId(nativeId, id, storage);
|
||||
}
|
||||
@@ -104,7 +104,7 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
if (name === 'repl') {
|
||||
if (!replPort)
|
||||
throw new Error('REPL unavailable: Plugin not loaded.')
|
||||
return [await replPort, process.env.SCRYPTED_CLUSTER_ADDRESS];
|
||||
return [await replPort, process.env.SCRYPTED_CLUSTER_ADDRESS!] as [number, string];
|
||||
}
|
||||
throw new Error(`unknown service ${name}`);
|
||||
},
|
||||
@@ -183,11 +183,11 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
|
||||
process.on('uncaughtException', e => {
|
||||
getPluginConsole().error('uncaughtException', e);
|
||||
scrypted.log.e('uncaughtException ' + (e.stack || e?.toString()));
|
||||
scrypted.log!.e('uncaughtException ' + (e.stack || e?.toString()));
|
||||
});
|
||||
process.on('unhandledRejection', e => {
|
||||
getPluginConsole().error('unhandledRejection', e);
|
||||
scrypted.log.e('unhandledRejection ' + ((e as Error).stack || e?.toString()));
|
||||
scrypted.log!.e('unhandledRejection ' + ((e as Error).stack || e?.toString()));
|
||||
});
|
||||
|
||||
installSourceMapSupport({
|
||||
@@ -209,10 +209,10 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
await installOptionalDependencies(getPluginConsole(), packageJson);
|
||||
|
||||
const main = await pluginReader(mainNodejs);
|
||||
const script = main.toString();
|
||||
const script = main!.toString();
|
||||
|
||||
scrypted.connect = (socket, options) => {
|
||||
process.send(options, socket);
|
||||
process.send!(options, socket);
|
||||
}
|
||||
|
||||
const pluginRemoteAPI: PluginRemote = scrypted.pluginRemoteAPI;
|
||||
@@ -221,12 +221,12 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
let forkPeer: Promise<RpcPeer>;
|
||||
let runtimeWorker: RuntimeWorker;
|
||||
let nativeWorker: child_process.ChildProcess | worker_threads.Worker;
|
||||
let clusterWorkerId: Promise<string>;
|
||||
let clusterWorkerId: Promise<string> | undefined;
|
||||
|
||||
const runtimeWorkerOptions: RuntimeWorkerOptions = {
|
||||
packageJson,
|
||||
env: undefined,
|
||||
pluginDebug: undefined,
|
||||
pluginDebug: undefined!,
|
||||
zipFile,
|
||||
unzippedPath,
|
||||
zipHash,
|
||||
@@ -234,13 +234,15 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
|
||||
// if running in a cluster, fork to a matching cluster worker only if necessary.
|
||||
if (utilizesClusterForkWorker(options)) {
|
||||
({ runtimeWorker, forkPeer, clusterWorkerId } = createClusterForkWorker(
|
||||
const result = createClusterForkWorker(
|
||||
runtimeWorkerOptions,
|
||||
options,
|
||||
options!,
|
||||
api.getComponent('cluster-fork'),
|
||||
() => zipAPI.getZip(),
|
||||
scrypted.connectRPCObject)
|
||||
);
|
||||
scrypted.connectRPCObject);
|
||||
runtimeWorker = result.runtimeWorker;
|
||||
forkPeer = result.forkPeer;
|
||||
clusterWorkerId = result.clusterWorkerId as Promise<string>;
|
||||
}
|
||||
else {
|
||||
if (options?.runtime) {
|
||||
@@ -248,10 +250,10 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
const runtime = builtins.get(options.runtime);
|
||||
if (!runtime)
|
||||
throw new Error('unknown runtime ' + options.runtime);
|
||||
runtimeWorker = runtime(mainFilename, runtimeWorkerOptions, undefined);
|
||||
runtimeWorker = runtime(mainFilename, runtimeWorkerOptions, undefined!);
|
||||
|
||||
if (runtimeWorker instanceof ChildProcessWorker) {
|
||||
nativeWorker = runtimeWorker.childProcess;
|
||||
nativeWorker = runtimeWorker.childProcess!;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -261,7 +263,7 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
const ntw = new NodeThreadWorker(mainFilename, pluginId, {
|
||||
packageJson,
|
||||
env: undefined,
|
||||
pluginDebug: undefined,
|
||||
pluginDebug: undefined!,
|
||||
zipFile,
|
||||
unzippedPath,
|
||||
zipHash,
|
||||
@@ -317,7 +319,7 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS] = (api as any)[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS];
|
||||
|
||||
override setStorage(nativeId: string, storage: { [key: string]: any; }): Promise<void> {
|
||||
const id = deviceManager.nativeIds.get(nativeId).id;
|
||||
const id = deviceManager.nativeIds.get(nativeId)!.id;
|
||||
pluginRemoteAPI.setNativeId(nativeId, id, storage);
|
||||
for (const r of forks) {
|
||||
if (r === remote)
|
||||
@@ -361,7 +363,7 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
removeListener(event, listener) {
|
||||
return runtimeWorker.removeListener(event as any, listener);
|
||||
},
|
||||
nativeWorker,
|
||||
nativeWorker: nativeWorker!,
|
||||
};
|
||||
return {
|
||||
[Symbol.dispose]() {
|
||||
@@ -376,7 +378,7 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
try {
|
||||
const isModule = packageJson.type === 'module';
|
||||
const filename = zipOptions?.debug ? pluginMainNodeJs : pluginIdMainNodeJs;
|
||||
const sdkVersion = await pluginReader('sdk.json').then(b => JSON.parse(b.toString()).version).catch(() => { });
|
||||
const sdkVersion = await pluginReader('sdk.json').then(b => JSON.parse(b!.toString()).version).catch(() => { });
|
||||
const mainNodeJsOnFilesystem = path.join(unzippedPath, mainNodejs);
|
||||
if (sdkVersion) {
|
||||
// todo: remove this, only existed in prerelease versions
|
||||
|
||||
@@ -51,7 +51,7 @@ export async function setupPluginRemote(peer: RpcPeer, api: PluginAPI, pluginId:
|
||||
delete state[id];
|
||||
continue;
|
||||
}
|
||||
state[id] = getAccessControlDeviceState(id, state[id]);
|
||||
state[id] = getAccessControlDeviceState(id, state[id]!)!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,11 +67,11 @@ export async function setupPluginRemote(peer: RpcPeer, api: PluginAPI, pluginId:
|
||||
if (eventDetails.eventInterface === ScryptedInterface.ScryptedDevice) {
|
||||
if (eventDetails.property === ScryptedInterfaceProperty.id) {
|
||||
// a change on the id property means device was deleted
|
||||
remote.updateDeviceState(eventData, undefined);
|
||||
remote.updateDeviceState(eventData, undefined!);
|
||||
}
|
||||
else {
|
||||
// a change on anything else is a descriptor update
|
||||
remote.updateDeviceState(id, getAccessControlDeviceState(id));
|
||||
remote.updateDeviceState(id, getAccessControlDeviceState(id)!);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -127,7 +127,7 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
|
||||
|
||||
ioSockets[id] = callbacks;
|
||||
|
||||
callbacks.connect(undefined, {
|
||||
callbacks.connect(undefined!, {
|
||||
close: (message) => connection.close(message),
|
||||
send: (message) => connection.send(message),
|
||||
});
|
||||
@@ -140,14 +140,14 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
|
||||
api = await options?.onGetRemote?.(api, pluginId) || api;
|
||||
|
||||
const systemManager = new SystemManagerImpl();
|
||||
const deviceManager = new DeviceManagerImpl(systemManager, getDeviceConsole, getMixinConsole);
|
||||
const deviceManager = new DeviceManagerImpl(systemManager, getDeviceConsole!, getMixinConsole!);
|
||||
const endpointManager = new EndpointManagerImpl();
|
||||
const clusterManager = new ClusterManagerImpl(undefined, api, undefined);
|
||||
const clusterManager = new ClusterManagerImpl(undefined, api, undefined!);
|
||||
const hostMediaManager = await api.getMediaManager();
|
||||
if (!hostMediaManager) {
|
||||
peer.params['createMediaManager'] = async () => createMediaManager(systemManager, deviceManager);
|
||||
peer.params['createMediaManager'] = async () => createMediaManager!(systemManager, deviceManager);
|
||||
}
|
||||
const mediaManager = hostMediaManager || await createMediaManager(systemManager, deviceManager);
|
||||
const mediaManager = hostMediaManager || await createMediaManager!(systemManager, deviceManager);
|
||||
peer.params['mediaManager'] = mediaManager;
|
||||
|
||||
systemManager.api = api;
|
||||
@@ -163,11 +163,11 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
|
||||
clusterManager,
|
||||
log,
|
||||
pluginHostAPI: api,
|
||||
pluginRemoteAPI: undefined,
|
||||
pluginRemoteAPI: undefined!,
|
||||
serverVersion: hostInfo?.serverVersion,
|
||||
connect: undefined,
|
||||
fork: undefined,
|
||||
connectRPCObject: undefined,
|
||||
connect: undefined!,
|
||||
fork: undefined!,
|
||||
connectRPCObject: undefined!,
|
||||
};
|
||||
|
||||
delete peer.params.getRemote;
|
||||
@@ -188,7 +188,7 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
|
||||
'ioEvent',
|
||||
'setNativeId',
|
||||
],
|
||||
getServicePort,
|
||||
getServicePort: getServicePort!,
|
||||
async createDeviceState(id: string, setState: (property: string, value: any) => Promise<void>) {
|
||||
return deviceManager.createDeviceState(id, setState);
|
||||
},
|
||||
@@ -300,7 +300,7 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
|
||||
params.pluginRuntimeAPI = ret;
|
||||
|
||||
try {
|
||||
return await options.onLoadZip(ret, params, packageJson, zipAPI, zipOptions);
|
||||
return await options!.onLoadZip!(ret, params, packageJson, zipAPI, zipOptions!);
|
||||
}
|
||||
catch (e) {
|
||||
console.error('plugin start/fork failed', e)
|
||||
|
||||
Reference in New Issue
Block a user