@scrypted/server 0.144.1 → 0.144.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/dist/cert.js +1 -1
  2. package/dist/cert.js.map +1 -1
  3. package/dist/cluster/cluster-labels.d.ts +3 -3
  4. package/dist/cluster/cluster-labels.js +2 -2
  5. package/dist/cluster/cluster-labels.js.map +1 -1
  6. package/dist/cluster/cluster-setup.d.ts +3 -3
  7. package/dist/cluster/cluster-setup.js +3 -3
  8. package/dist/cluster/cluster-setup.js.map +1 -1
  9. package/dist/db-types.d.ts +4 -4
  10. package/dist/db-types.js.map +1 -1
  11. package/dist/event-registry.d.ts +2 -2
  12. package/dist/event-registry.js +9 -7
  13. package/dist/event-registry.js.map +1 -1
  14. package/dist/fetch/index.d.ts +2 -2
  15. package/dist/fetch/index.js +3 -6
  16. package/dist/fetch/index.js.map +1 -1
  17. package/dist/http-interfaces.js +1 -1
  18. package/dist/http-interfaces.js.map +1 -1
  19. package/dist/infer-defaults.d.ts +4 -4
  20. package/dist/infer-defaults.js +8 -4
  21. package/dist/infer-defaults.js.map +1 -1
  22. package/dist/ip.js +2 -1
  23. package/dist/ip.js.map +1 -1
  24. package/dist/level.d.ts +3 -3
  25. package/dist/level.js +1 -0
  26. package/dist/level.js.map +1 -1
  27. package/dist/listen-zero.js.map +1 -1
  28. package/dist/mixin/mixin-cycle.js +6 -6
  29. package/dist/mixin/mixin-cycle.js.map +1 -1
  30. package/dist/plugin/acl.js.map +1 -1
  31. package/dist/plugin/cluster.js.map +1 -1
  32. package/dist/plugin/descriptor.js +1 -1
  33. package/dist/plugin/descriptor.js.map +1 -1
  34. package/dist/plugin/device.d.ts +6 -6
  35. package/dist/plugin/device.js.map +1 -1
  36. package/dist/plugin/endpoint.js.map +1 -1
  37. package/dist/plugin/media.js.map +1 -1
  38. package/dist/plugin/mediaobject.d.ts +1 -1
  39. package/dist/plugin/mediaobject.js.map +1 -1
  40. package/dist/plugin/plugin-api.d.ts +6 -6
  41. package/dist/plugin/plugin-api.js +2 -2
  42. package/dist/plugin/plugin-api.js.map +1 -1
  43. package/dist/plugin/plugin-console.js +11 -10
  44. package/dist/plugin/plugin-console.js.map +1 -1
  45. package/dist/plugin/plugin-device.d.ts +3 -3
  46. package/dist/plugin/plugin-device.js +2 -0
  47. package/dist/plugin/plugin-device.js.map +1 -1
  48. package/dist/plugin/plugin-host-api.d.ts +1 -1
  49. package/dist/plugin/plugin-host-api.js +19 -5
  50. package/dist/plugin/plugin-host-api.js.map +1 -1
  51. package/dist/plugin/plugin-host.js +3 -2
  52. package/dist/plugin/plugin-host.js.map +1 -1
  53. package/dist/plugin/plugin-http.d.ts +1 -1
  54. package/dist/plugin/plugin-http.js +5 -1
  55. package/dist/plugin/plugin-http.js.map +1 -1
  56. package/dist/plugin/plugin-lazy-remote.js.map +1 -1
  57. package/dist/plugin/plugin-remote-websocket.js.map +1 -1
  58. package/dist/plugin/plugin-remote-worker.js +5 -2
  59. package/dist/plugin/plugin-remote-worker.js.map +1 -1
  60. package/dist/plugin/plugin-remote.js +1 -1
  61. package/dist/plugin/plugin-remote.js.map +1 -1
  62. package/dist/plugin/plugin-repl.js +6 -2
  63. package/dist/plugin/plugin-repl.js.map +1 -1
  64. package/dist/plugin/runtime/child-process-worker.d.ts +4 -4
  65. package/dist/plugin/runtime/child-process-worker.js +1 -2
  66. package/dist/plugin/runtime/child-process-worker.js.map +1 -1
  67. package/dist/plugin/runtime/cluster-fork-worker.d.ts +1 -1
  68. package/dist/plugin/runtime/cluster-fork-worker.js +2 -0
  69. package/dist/plugin/runtime/cluster-fork-worker.js.map +1 -1
  70. package/dist/plugin/runtime/custom-worker.js.map +1 -1
  71. package/dist/plugin/runtime/node-fork-worker.d.ts +1 -1
  72. package/dist/plugin/runtime/node-fork-worker.js.map +1 -1
  73. package/dist/plugin/runtime/node-thread-worker.js.map +1 -1
  74. package/dist/plugin/runtime/python-worker.js +1 -1
  75. package/dist/plugin/runtime/python-worker.js.map +1 -1
  76. package/dist/plugin/runtime/runtime-worker.d.ts +1 -1
  77. package/dist/plugin/system.d.ts +1 -1
  78. package/dist/plugin/system.js +1 -0
  79. package/dist/plugin/system.js.map +1 -1
  80. package/dist/promise-utils.d.ts +1 -1
  81. package/dist/promise-utils.js.map +1 -1
  82. package/dist/rpc-buffer-serializer.d.ts +1 -1
  83. package/dist/rpc-serializer.js.map +1 -1
  84. package/dist/rpc.d.ts +10 -9
  85. package/dist/rpc.js +15 -8
  86. package/dist/rpc.js.map +1 -1
  87. package/dist/runtime.d.ts +11 -11
  88. package/dist/runtime.js +17 -18
  89. package/dist/runtime.js.map +1 -1
  90. package/dist/scrypted-cluster-main.js +3 -2
  91. package/dist/scrypted-cluster-main.js.map +1 -1
  92. package/dist/scrypted-plugin-main.js +1 -1
  93. package/dist/scrypted-plugin-main.js.map +1 -1
  94. package/dist/scrypted-server-main.js +8 -5
  95. package/dist/scrypted-server-main.js.map +1 -1
  96. package/dist/server-settings.d.ts +1 -1
  97. package/dist/server-settings.js +4 -4
  98. package/dist/server-settings.js.map +1 -1
  99. package/dist/services/addresses.d.ts +1 -1
  100. package/dist/services/addresses.js +2 -2
  101. package/dist/services/addresses.js.map +1 -1
  102. package/dist/services/alerts.js +2 -2
  103. package/dist/services/alerts.js.map +1 -1
  104. package/dist/services/cluster-fork.js.map +1 -1
  105. package/dist/services/plugin.d.ts +5 -5
  106. package/dist/services/plugin.js.map +1 -1
  107. package/dist/services/service-control.js.map +1 -1
  108. package/dist/services/users.d.ts +2 -2
  109. package/dist/services/users.js.map +1 -1
  110. package/dist/state.d.ts +7 -7
  111. package/dist/state.js +4 -4
  112. package/dist/state.js.map +1 -1
  113. package/dist/threading.js.map +1 -1
  114. package/dist/usertoken.d.ts +2 -2
  115. package/dist/usertoken.js.map +1 -1
  116. package/package.json +2 -2
  117. package/python/cluster_setup.py +13 -4
  118. package/python/plugin_remote.py +22 -18
  119. package/python/rpc_reader.py +3 -5
  120. package/src/cert.ts +2 -2
  121. package/src/cluster/cluster-labels.ts +4 -4
  122. package/src/cluster/cluster-setup.ts +20 -20
  123. package/src/db-types.ts +16 -16
  124. package/src/event-registry.ts +13 -11
  125. package/src/fetch/index.ts +4 -7
  126. package/src/http-interfaces.ts +2 -2
  127. package/src/infer-defaults.ts +12 -8
  128. package/src/ip.ts +2 -1
  129. package/src/level.ts +10 -9
  130. package/src/listen-zero.ts +1 -1
  131. package/src/mixin/mixin-cycle.ts +6 -6
  132. package/src/plugin/acl.ts +1 -1
  133. package/src/plugin/cluster.ts +2 -2
  134. package/src/plugin/descriptor.ts +2 -2
  135. package/src/plugin/device.ts +16 -16
  136. package/src/plugin/endpoint.ts +4 -4
  137. package/src/plugin/media.ts +15 -15
  138. package/src/plugin/mediaobject.ts +3 -3
  139. package/src/plugin/plugin-api.ts +7 -7
  140. package/src/plugin/plugin-console.ts +13 -12
  141. package/src/plugin/plugin-device.ts +20 -18
  142. package/src/plugin/plugin-host-api.ts +27 -13
  143. package/src/plugin/plugin-host.ts +26 -25
  144. package/src/plugin/plugin-http.ts +9 -5
  145. package/src/plugin/plugin-lazy-remote.ts +1 -1
  146. package/src/plugin/plugin-remote-websocket.ts +1 -1
  147. package/src/plugin/plugin-remote-worker.ts +22 -20
  148. package/src/plugin/plugin-remote.ts +14 -14
  149. package/src/plugin/plugin-repl.ts +6 -2
  150. package/src/plugin/runtime/child-process-worker.ts +11 -12
  151. package/src/plugin/runtime/cluster-fork-worker.ts +2 -0
  152. package/src/plugin/runtime/custom-worker.ts +6 -6
  153. package/src/plugin/runtime/node-fork-worker.ts +3 -3
  154. package/src/plugin/runtime/node-thread-worker.ts +3 -3
  155. package/src/plugin/runtime/python-worker.ts +14 -14
  156. package/src/plugin/runtime/runtime-worker.ts +1 -1
  157. package/src/plugin/system.ts +24 -23
  158. package/src/promise-utils.ts +4 -4
  159. package/src/rpc-serializer.ts +3 -3
  160. package/src/rpc.ts +25 -17
  161. package/src/runtime.ts +59 -60
  162. package/src/scrypted-cluster-main.ts +19 -18
  163. package/src/scrypted-plugin-main.ts +2 -2
  164. package/src/scrypted-server-main.ts +17 -14
  165. package/src/server-settings.ts +5 -5
  166. package/src/services/addresses.ts +3 -3
  167. package/src/services/alerts.ts +2 -2
  168. package/src/services/cluster-fork.ts +9 -9
  169. package/src/services/plugin.ts +15 -15
  170. package/src/services/service-control.ts +1 -1
  171. package/src/services/users.ts +3 -3
  172. package/src/state.ts +21 -21
  173. package/src/threading.ts +5 -5
  174. package/src/usertoken.ts +1 -1
@@ -102,7 +102,7 @@ export function prepareConsoles(getConsoleName: () => string, systemManager: ()
102
102
  return ret;
103
103
  }
104
104
 
105
- const mixinConsoles = new Map<string, Map<string, Console>>();
105
+ const mixinConsoles = new Map<ScryptedNativeId, Map<string, Console>>();
106
106
 
107
107
  function getMixinConsole(mixinId: string, nativeId: ScryptedNativeId) {
108
108
  let nativeIdConsoles = mixinConsoles.get(nativeId);
@@ -179,7 +179,7 @@ export function prepareConsoles(getConsoleName: () => string, systemManager: ()
179
179
  }
180
180
 
181
181
  export async function createConsoleServer(remoteStdout: Readable, remoteStderr: Readable, header: string) {
182
- const outputs = new Map<string, StdPassThroughs>();
182
+ const outputs = new Map<ScryptedNativeId, StdPassThroughs>();
183
183
 
184
184
  const addHeader = (pts: StdPassThroughs) => {
185
185
  pts.buffers.push(Buffer.from(header));
@@ -199,6 +199,7 @@ export async function createConsoleServer(remoteStdout: Readable, remoteStderr:
199
199
  buffers: [],
200
200
  }
201
201
  outputs.set(nativeId, pts);
202
+ const ptsRef = pts;
202
203
 
203
204
  let writeTimestamp = true;
204
205
  let timestampTimer: NodeJS.Timeout;
@@ -206,7 +207,7 @@ export async function createConsoleServer(remoteStdout: Readable, remoteStderr:
206
207
  stderr.on('close', () => clearTimeout(timestampTimer));
207
208
 
208
209
  const appendOutput = (data: Buffer) => {
209
- const { buffers } = pts;
210
+ const { buffers } = ptsRef;
210
211
 
211
212
  if (writeTimestamp) {
212
213
  writeTimestamp = false;
@@ -220,7 +221,7 @@ export async function createConsoleServer(remoteStdout: Readable, remoteStderr:
220
221
  // when we're over 4000 lines or whatever these buffer are,
221
222
  // truncate down to 2000.
222
223
  if (buffers.length > 4000)
223
- pts.buffers = buffers.slice(buffers.length - 2000);
224
+ ptsRef.buffers = buffers.slice(buffers.length - 2000);
224
225
  };
225
226
 
226
227
  stdout.on('data', appendOutput);
@@ -279,15 +280,14 @@ export async function createConsoleServer(remoteStdout: Readable, remoteStderr:
279
280
  const { server: writeServer, port: writePort } = await clusterListenZero(async (socket) => {
280
281
  sockets.add(socket);
281
282
  const [data] = await once(socket, 'data');
282
- let filter: string = data.toString();
283
- const newline = filter.indexOf('\n');
283
+ let filterStr: string = data.toString();
284
+ const newline = filterStr.indexOf('\n');
284
285
  if (newline !== -1) {
285
- socket.unshift(Buffer.from(filter.substring(newline + 1)));
286
+ socket.unshift(Buffer.from(filterStr.substring(newline + 1)));
286
287
  }
287
- filter = filter.substring(0, newline);
288
+ filterStr = filterStr.substring(0, newline);
288
289
 
289
- if (filter === 'undefined')
290
- filter = undefined;
290
+ const filter = filterStr === 'undefined' ? undefined : filterStr;
291
291
 
292
292
  const { stdout } = getPassthroughs(filter);
293
293
  socket.pipe(stdout, { end: false });
@@ -306,9 +306,10 @@ export async function createConsoleServer(remoteStdout: Readable, remoteStderr:
306
306
  return {
307
307
  clear(nativeId: ScryptedNativeId) {
308
308
  const pt = outputs.get(nativeId);
309
- if (pt)
309
+ if (pt) {
310
310
  pt.buffers = [];
311
- addHeader(pt);
311
+ addHeader(pt);
312
+ }
312
313
  },
313
314
  destroy() {
314
315
  for (const socket of sockets) {
@@ -31,7 +31,7 @@ export const QueryInterfaceSymbol = Symbol("ScryptedPluginDeviceQueryInterface")
31
31
  export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
32
32
  scrypted: ScryptedRuntime;
33
33
  id: string;
34
- mixinTable: MixinTable[];
34
+ mixinTable!: MixinTable[];
35
35
  releasing = new Set<any>();
36
36
 
37
37
  static sortInterfaces(interfaces: string[]): string[] {
@@ -65,8 +65,8 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
65
65
  async getMixinProviderId(id: string, mixinDevice: any) {
66
66
  if (this.releasing.has(mixinDevice))
67
67
  return true;
68
- await this.scrypted.devices[id].handler.ensureProxy();
69
- for (const mixin of this.scrypted.devices[id].handler.mixinTable) {
68
+ await this.scrypted.devices[id]!.handler.ensureProxy();
69
+ for (const mixin of this.scrypted.devices[id]!.handler.mixinTable) {
70
70
  const { proxy } = await mixin.entry;
71
71
  if (proxy === mixinDevice) {
72
72
  return mixin.mixinProviderId || id;
@@ -78,7 +78,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
78
78
  // should this be async?
79
79
  invalidate() {
80
80
  const mixinTable = this.mixinTable;
81
- this.mixinTable = undefined;
81
+ this.mixinTable = undefined!;
82
82
  for (const mixinEntry of (mixinTable || [])) {
83
83
  this.invalidateEntry(mixinEntry);
84
84
  }
@@ -102,7 +102,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
102
102
  const mixins = getState(pluginDevice, ScryptedInterfaceProperty.mixins) || [];
103
103
  // iterate the new mixin table to find the last good mixin,
104
104
  // and resume creation from there.
105
- let lastValidMixinId: string;
105
+ let lastValidMixinId: string | undefined;
106
106
  for (const mixinId of mixins) {
107
107
  if (!previousMixinIds.length) {
108
108
  // reached of the previous mixin table, meaning
@@ -121,7 +121,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
121
121
 
122
122
  // invalidate and remove everything up to lastValidMixinId
123
123
  while (true) {
124
- const entry = this.mixinTable[0];
124
+ const entry = this.mixinTable[0]!;
125
125
  if (entry.mixinProviderId === lastValidMixinId)
126
126
  break;
127
127
  this.mixinTable.shift();
@@ -149,7 +149,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
149
149
  try {
150
150
  if (!pluginDevice.nativeId) {
151
151
  const plugin = this.scrypted.plugins[pluginDevice.pluginId];
152
- proxy = await plugin.module;
152
+ proxy = await plugin!.module;
153
153
  }
154
154
  else {
155
155
  const providerId = getState(pluginDevice, ScryptedInterfaceProperty.providerId);
@@ -174,7 +174,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
174
174
  })();
175
175
 
176
176
  this.mixinTable.unshift({
177
- mixinProviderId: undefined,
177
+ mixinProviderId: undefined!,
178
178
  entry,
179
179
  });
180
180
  }
@@ -202,7 +202,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
202
202
  });
203
203
  }
204
204
 
205
- return this.mixinTable[0].entry.then(entry => {
205
+ return this.mixinTable[0]!.entry.then(entry => {
206
206
  if (entry.error) {
207
207
  console.error('Mixin device creation completed with error. Merging with previous interface set to retain device descriptor.');
208
208
  const previousInterfaces = getState(pluginDevice, ScryptedInterfaceProperty.interfaces) as string[] || [];
@@ -219,7 +219,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
219
219
 
220
220
  async rebuildEntry(pluginDevice: PluginDevice, mixinId: string, wrappedMixinTablePromise: Promise<MixinTable[]>): Promise<MixinTableEntry> {
221
221
  const wrappedMixinTable = await wrappedMixinTablePromise;
222
- const previousEntry = wrappedMixinTable[0].entry;
222
+ const previousEntry = wrappedMixinTable[0]!.entry;
223
223
 
224
224
  const type = getDisplayType(pluginDevice);
225
225
 
@@ -255,7 +255,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
255
255
  passthrough: true,
256
256
  allInterfaces,
257
257
  interfaces: new Set<string>(),
258
- proxy: undefined as any,
258
+ proxy: undefined!,
259
259
  };
260
260
  }
261
261
 
@@ -269,7 +269,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
269
269
  const wrappedProxy = new Proxy(wrappedHandler, wrappedHandler);
270
270
 
271
271
  const implementer = await (mixinProvider as any)[QueryInterfaceSymbol](ScryptedInterface.MixinProvider);
272
- const host = this.scrypted.getPluginHostForDeviceId(implementer);
272
+ const host = this.scrypted.getPluginHostForDeviceId(implementer)!;
273
273
  const propertyInterfaces = getPropertyInterfaces(host.api.descriptors || ScryptedInterfaceDescriptors);
274
274
  // todo: remove this and pass the setter directly.
275
275
  const deviceState = await host.remote.createDeviceState(this.id,
@@ -304,8 +304,8 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
304
304
  passthrough: false,
305
305
  allInterfaces,
306
306
  interfaces: new Set<string>(),
307
- error: e,
308
- proxy: undefined as any,
307
+ error: e as Error,
308
+ proxy: undefined!,
309
309
  };
310
310
  }
311
311
  }
@@ -328,7 +328,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
328
328
  if (p === RefreshSymbol || p === QueryInterfaceSymbol)
329
329
  return new Proxy(() => p, this);
330
330
 
331
- if (ScryptedInterfaceDescriptors[ScryptedInterface.ScryptedDevice].methods.includes(prop))
331
+ if (ScryptedInterfaceDescriptors[ScryptedInterface.ScryptedDevice]!.methods.includes(prop))
332
332
  return (this as any)[p].bind(this);
333
333
 
334
334
  return new Proxy(() => prop, this);
@@ -382,7 +382,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
382
382
  throw new PluginError(`${method} not implemented`)
383
383
  }
384
384
 
385
- async findMethod(method: string) {
385
+ async findMethod(method: string): Promise<{ mixin: MixinTable, entry: MixinTableEntry } | undefined> {
386
386
  for (const mixin of this.mixinTable) {
387
387
  const entry = await mixin.entry;
388
388
  if (!entry.methods) {
@@ -402,9 +402,10 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
402
402
  return { mixin, entry };
403
403
  }
404
404
  }
405
+ return undefined;
405
406
  }
406
407
 
407
- async findMixin(iface: string) {
408
+ async findMixin(iface: string): Promise<{ mixin: MixinTable, entry: MixinTableEntry } | undefined> {
408
409
  for (const mixin of this.mixinTable) {
409
410
  const entry = await mixin.entry;
410
411
  const { interfaces } = entry;
@@ -412,6 +413,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
412
413
  return { mixin, entry };
413
414
  }
414
415
  }
416
+ return undefined;
415
417
  }
416
418
 
417
419
  async apply(target: any, thisArg: any, argArray?: any): Promise<any> {
@@ -443,7 +445,7 @@ export class PluginDeviceProxyHandler implements PrimitiveProxyHandler<any> {
443
445
  if (method === 'getReadmeMarkdown') {
444
446
  const pluginDevice = this.scrypted.findPluginDeviceById(this.id);
445
447
  if (pluginDevice && !pluginDevice.nativeId) {
446
- const plugin = this.scrypted.plugins[pluginDevice.pluginId];
448
+ const plugin = this.scrypted.plugins[pluginDevice.pluginId]!;
447
449
  if (!plugin.packageJson.scrypted.interfaces.includes(ScryptedInterface.Readme)) {
448
450
  const readmePath = path.join(plugin.unzippedPath, 'README.md');
449
451
  if (fs.existsSync(readmePath)) {
@@ -12,9 +12,9 @@ import { checkProperty } from './plugin-state-check';
12
12
 
13
13
  export class PluginHostAPI extends PluginAPIManagedListeners implements PluginAPI {
14
14
  pluginId: string;
15
- typesVersion: string;
16
- descriptors: { [scryptedInterface: string]: ScryptedInterfaceDescriptor };
17
- propertyInterfaces: ReturnType<typeof getPropertyInterfaces>;
15
+ typesVersion!: string;
16
+ descriptors!: { [scryptedInterface: string]: ScryptedInterfaceDescriptor };
17
+ propertyInterfaces!: ReturnType<typeof getPropertyInterfaces>;
18
18
 
19
19
  [RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS] = [
20
20
  'onMixinEvent',
@@ -56,12 +56,16 @@ export class PluginHostAPI extends PluginAPIManagedListeners implements PluginAP
56
56
  const nativeId: string = nativeIdOrMixinDevice;
57
57
  // todo: deprecate this code path
58
58
  const mixinProvider = this.scrypted.findPluginDevice(this.pluginId, nativeId);
59
+ if (!mixinProvider)
60
+ throw new Error(`mixin provider not found for native id ${nativeId}`);
59
61
  const mixins: string[] = getState(device, ScryptedInterfaceProperty.mixins) || [];
60
62
  if (!mixins.includes(mixinProvider._id))
61
63
  throw new Error(`${mixinProvider._id} is not a mixin provider for ${id}`);
62
64
 
63
65
  this.scrypted.findPluginDevice(this.pluginId, nativeId);
64
- const tableEntry = this.scrypted.devices[device._id].handler.mixinTable.find(entry => entry.mixinProviderId === mixinProvider._id);
66
+ const tableEntry = this.scrypted.devices[device._id]?.handler?.mixinTable?.find((entry: any) => entry.mixinProviderId === mixinProvider._id);
67
+ if (!tableEntry)
68
+ throw new Error(`no mixin table entry found for ${mixinProvider._id} on ${id}`);
65
69
  const { interfaces } = await tableEntry.entry;
66
70
  if (!interfaces.has(eventInterface))
67
71
  throw new Error(`${mixinProvider._id} does not mixin ${eventInterface} for ${id}`);
@@ -87,7 +91,7 @@ export class PluginHostAPI extends PluginAPIManagedListeners implements PluginAP
87
91
 
88
92
  async getLogger(nativeId: ScryptedNativeId): Promise<Logger> {
89
93
  const device = this.scrypted.findPluginDevice(this.pluginId, nativeId);
90
- return this.scrypted.getDeviceLogger(device);
94
+ return this.scrypted.getDeviceLogger(device!)!;
91
95
  }
92
96
 
93
97
  async getComponent(id: string): Promise<any> {
@@ -103,7 +107,7 @@ export class PluginHostAPI extends PluginAPIManagedListeners implements PluginAP
103
107
  return this.scrypted.getComponent(id);
104
108
  }
105
109
 
106
- setDeviceProperty(id: string, property: ScryptedInterfaceProperty, value: any): Promise<void> {
110
+ async setDeviceProperty(id: string, property: ScryptedInterfaceProperty, value: any): Promise<void> {
107
111
  switch (property) {
108
112
  case ScryptedInterfaceProperty.room:
109
113
  case ScryptedInterfaceProperty.type:
@@ -127,22 +131,32 @@ export class PluginHostAPI extends PluginAPIManagedListeners implements PluginAP
127
131
  }
128
132
 
129
133
  async setStorage(nativeId: ScryptedNativeId, storage: { [key: string]: string }) {
130
- const device = this.scrypted.findPluginDevice(this.pluginId, nativeId)
134
+ const device = this.scrypted.findPluginDevice(this.pluginId, nativeId)!;
131
135
  device.storage = storage;
132
136
  this.scrypted.datastore.upsert(device);
133
137
  }
134
138
 
135
139
  async onDevicesChanged(deviceManifest: DeviceManifest) {
136
140
  const provider = this.scrypted.findPluginDevice(this.pluginId, deviceManifest.providerNativeId);
137
- const existing = this.scrypted.findPluginDevices(this.pluginId).filter(p => p.state[ScryptedInterfaceProperty.providerId].value === provider._id);
138
- const newIds = deviceManifest.devices.map(device => device.nativeId);
141
+ if (!provider)
142
+ throw new Error(`provider not found for plugin id ${this.pluginId} native id ${deviceManifest.providerNativeId}`);
143
+ const existing = this.scrypted.findPluginDevices(this.pluginId).filter(p => {
144
+ const state = p.state[ScryptedInterfaceProperty.providerId];
145
+ if (!state)
146
+ throw new Error(`providerId state not found for device ${p._id}`);
147
+ return state.value === provider._id;
148
+ });
149
+ if (!deviceManifest.devices)
150
+ throw new Error(`onDevicesChanged called without devices for provider ${deviceManifest.providerNativeId}`);
151
+ const devices = deviceManifest.devices;
152
+ const newIds = devices.map(device => device.nativeId);
139
153
  const toRemove = existing.filter(e => e.nativeId && !newIds.includes(e.nativeId));
140
154
 
141
155
  for (const remove of toRemove) {
142
156
  await this.scrypted.removeDevice(remove);
143
157
  }
144
158
 
145
- for (const upsert of deviceManifest.devices) {
159
+ for (const upsert of devices) {
146
160
  upsert.providerNativeId = deviceManifest.providerNativeId;
147
161
  const id = await this.pluginHost.upsertDevice(upsert);
148
162
  this.scrypted.getDevice(id)?.probe().catch(() => { });
@@ -156,16 +170,16 @@ export class PluginHostAPI extends PluginAPIManagedListeners implements PluginAP
156
170
  }
157
171
 
158
172
  async onDeviceRemoved(nativeId: string) {
159
- await this.scrypted.removeDevice(this.scrypted.findPluginDevice(this.pluginId, nativeId))
173
+ await this.scrypted.removeDevice(this.scrypted.findPluginDevice(this.pluginId, nativeId)!)
160
174
  }
161
175
 
162
176
  async onDeviceEvent(nativeId: any, eventInterface: any, eventData?: any) {
163
- const plugin = this.scrypted.findPluginDevice(this.pluginId, nativeId);
177
+ const plugin = this.scrypted.findPluginDevice(this.pluginId, nativeId)!;
164
178
  this.scrypted.stateManager.notifyInterfaceEventFromMixin(plugin, eventInterface, eventData, plugin._id);
165
179
  }
166
180
 
167
181
  async getDeviceById<T>(id: string): Promise<T & ScryptedDevice> {
168
- return this.scrypted.getDevice(id);
182
+ return this.scrypted.getDevice(id)!;
169
183
  }
170
184
  async listen(callback: (id: string, eventDetails: EventDetails, eventData: any) => void): Promise<EventListenerRegister> {
171
185
  return this.manageListener(this.scrypted.stateManager.listen(callback));
@@ -36,8 +36,8 @@ export class UnsupportedRuntimeError extends Error {
36
36
  }
37
37
 
38
38
  export class PluginHost {
39
- worker: RuntimeWorker;
40
- peer: RpcPeer;
39
+ worker!: RuntimeWorker;
40
+ peer!: RpcPeer;
41
41
  pluginId: string;
42
42
  module: Promise<any>;
43
43
  scrypted: ScryptedRuntime;
@@ -50,7 +50,7 @@ export class PluginHost {
50
50
  perMessageDeflate: true,
51
51
  cors: (req, callback) => {
52
52
  const header = this.scrypted.getAccessControlAllowOrigin(req.headers);
53
- callback(undefined, {
53
+ callback(undefined!, {
54
54
  origin: header,
55
55
  credentials: true,
56
56
  })
@@ -61,11 +61,11 @@ export class PluginHost {
61
61
  pluginName: string;
62
62
  packageJson: any;
63
63
  killed = false;
64
- consoleServer: Promise<ConsoleServer>;
64
+ consoleServer!: Promise<ConsoleServer>;
65
65
  zipHash: string;
66
66
  zipFile: string;
67
67
  unzippedPath: string;
68
- clusterWorkerId: Promise<string>;
68
+ clusterWorkerId!: Promise<string>;
69
69
 
70
70
  kill() {
71
71
  this.killed = true;
@@ -103,10 +103,11 @@ export class PluginHost {
103
103
  if (!needInvalidate) {
104
104
  // may also need to invalidate if the the plugin did not previously return a device
105
105
  // because it had not yet completed the discovery process.
106
- const device = this.scrypted.devices[pi._id];
106
+ const device = this.scrypted.devices[pi._id]!;
107
107
  try {
108
- if (device.handler?.mixinTable)
109
- needInvalidate = !(await device.handler.mixinTable?.[device.handler.mixinTable.length - 1].entry).proxy;
108
+ const handler = device.handler;
109
+ if (handler?.mixinTable)
110
+ needInvalidate = !(await handler.mixinTable?.[handler.mixinTable.length - 1]!.entry).proxy;
110
111
  }
111
112
  catch (e) {
112
113
  // device retrieval had previously failed, fetch again.
@@ -125,16 +126,16 @@ export class PluginHost {
125
126
  this.pluginName = plugin.packageJson?.name;
126
127
  this.packageJson = plugin.packageJson;
127
128
 
128
- const pluginDeviceId = scrypted.findPluginDevice(this.pluginId)._id;
129
- const logger = scrypted.getDeviceLogger(scrypted.findPluginDevice(this.pluginId));
129
+ const pluginDeviceId = scrypted.findPluginDevice(this.pluginId)!._id;
130
+ const logger = scrypted.getDeviceLogger(scrypted.findPluginDevice(this.pluginId)!)!;
130
131
 
131
132
  const volume = getScryptedVolume();
132
133
  const pluginVolume = ensurePluginVolume(this.pluginId);
133
134
 
134
135
  {
135
- const zipBuffer = Buffer.from(plugin.zip, 'base64');
136
+ const zipBuffer = Buffer.from(plugin.zip!, 'base64');
136
137
  // allow garbage collection of the base 64 contents
137
- plugin = undefined;
138
+ (plugin as any) = undefined;
138
139
  const hash = crypto.createHash('md5').update(zipBuffer).digest().toString('hex');
139
140
  this.zipHash = hash;
140
141
 
@@ -146,7 +147,7 @@ export class PluginHost {
146
147
  const peerPromise = this.startPluginHost(logger, {
147
148
  SCRYPTED_VOLUME: volume,
148
149
  SCRYPTED_PLUGIN_VOLUME: pluginVolume,
149
- }, pluginDebug);
150
+ }, pluginDebug!);
150
151
 
151
152
  this.io.on('connection', async (socket) => {
152
153
  try {
@@ -157,8 +158,8 @@ export class PluginHost {
157
158
  } = (socket.request as any).scrypted;
158
159
 
159
160
  try {
160
- if (socket.request.url.indexOf('/engine.io/api') !== -1) {
161
- if (socket.request.url.indexOf('/public') !== -1) {
161
+ if (socket.request.url!.indexOf('/engine.io/api') !== -1) {
162
+ if (socket.request.url!.indexOf('/public') !== -1) {
162
163
  socket.close();
163
164
  return;
164
165
  }
@@ -205,13 +206,13 @@ export class PluginHost {
205
206
  ? new MediaManagerHostImpl(pluginDeviceId, () => scrypted.stateManager.getSystemState(), console, id => scrypted.getDevice(id))
206
207
  : undefined;
207
208
 
208
- this.api = new PluginHostAPI(scrypted, this.pluginId, this, mediaManager);
209
+ this.api = new PluginHostAPI(scrypted, this.pluginId, this, mediaManager!);
209
210
 
210
211
  logger.log('i', `loading ${this.pluginName}`);
211
212
  logger.log('i', 'pid ' + this.worker?.pid);
212
213
 
213
- const remotePromise = this.prepareRemote(peerPromise, logger, pluginDebug);
214
- const init = this.initializeRemote(remotePromise, logger, pluginDebug);
214
+ const remotePromise = this.prepareRemote(peerPromise, logger, pluginDebug!);
215
+ const init = this.initializeRemote(remotePromise, logger, pluginDebug!);
215
216
 
216
217
  init.catch(e => {
217
218
  console.error('plugin failed to load', e);
@@ -280,7 +281,7 @@ export class PluginHost {
280
281
  }
281
282
  catch (e) {
282
283
  logger.log('e', 'plugin failed to start ' + e);
283
- throw new RPCResultError(this.peer, 'cluster plugin start failed', e);
284
+ throw new RPCResultError(this.peer, 'cluster plugin start failed', e as Error | undefined);
284
285
  }
285
286
 
286
287
  const startupTime = Date.now();
@@ -333,8 +334,8 @@ export class PluginHost {
333
334
  let { runtime } = this.packageJson.scrypted;
334
335
  runtime ||= 'node';
335
336
 
336
- const pluginDevice = this.scrypted.findPluginDevice(this.pluginId);
337
- const customRuntime = pluginDevice.state.interfaces.value.includes(ScryptedInterface.ScryptedPluginRuntime);
337
+ const pluginDevice = this.scrypted.findPluginDevice(this.pluginId)!;
338
+ const customRuntime = pluginDevice.state.interfaces!.value.includes(ScryptedInterface.ScryptedPluginRuntime);
338
339
  if (customRuntime) {
339
340
  runtime = 'custom';
340
341
  }
@@ -375,7 +376,7 @@ export class PluginHost {
375
376
 
376
377
  this.worker.stdout.on('data', data => console.log(data.toString()));
377
378
  this.worker.stderr.on('data', data => console.error(data.toString()));
378
- this.clusterWorkerId = Promise.resolve(undefined);
379
+ this.clusterWorkerId = Promise.resolve(undefined!);
379
380
  }
380
381
  else {
381
382
  const scrypted: ClusterForkOptions = JSON.parse(JSON.stringify(this.packageJson.scrypted));
@@ -414,7 +415,7 @@ export class PluginHost {
414
415
  peer.killedSafe.finally(() => originalPeer.kill());
415
416
  }).catch(() => { });
416
417
 
417
- this.clusterWorkerId = clusterWorkerId;
418
+ this.clusterWorkerId = clusterWorkerId as Promise<string>;
418
419
  clusterWorkerId.then(clusterWorkerId => {
419
420
  console.log('cluster worker id', clusterWorkerId);
420
421
  }).catch(() => {
@@ -483,7 +484,7 @@ export class PluginHost {
483
484
  serializer.sendMessage(message, reject, serializationContext);
484
485
  }
485
486
  catch (e) {
486
- reject?.(e);
487
+ reject?.(e as Error);
487
488
  }
488
489
  });
489
490
  rpcPeer.tags.acl = accessControls;
@@ -500,6 +501,6 @@ export class PluginHost {
500
501
  socket.on('close', kill);
501
502
  socket.on('error', kill);
502
503
 
503
- return setupPluginRemote(rpcPeer, api, null, { serverVersion }, () => this.scrypted.stateManager.getSystemState());
504
+ return setupPluginRemote(rpcPeer, api, null!, { serverVersion }, () => this.scrypted.stateManager.getSystemState());
504
505
  }
505
506
  }
@@ -6,7 +6,7 @@ import WebSocket, { Server as WebSocketServer } from "ws";
6
6
 
7
7
  export function isConnectionUpgrade(headers: IncomingHttpHeaders) {
8
8
  // connection:'keep-alive, Upgrade'
9
- return headers.connection?.toLowerCase().includes('upgrade');
9
+ return headers.connection?.toLowerCase().includes('upgrade') ?? false;
10
10
  }
11
11
 
12
12
  export abstract class PluginHttp<T> {
@@ -38,7 +38,7 @@ export abstract class PluginHttp<T> {
38
38
 
39
39
  abstract handleEngineIOEndpoint(req: Request, res: ServerResponse, endpointRequest: HttpRequest, pluginData: T): void;
40
40
  abstract handleRequestEndpoint(req: Request, res: Response, endpointRequest: HttpRequest, pluginData: T): void;
41
- abstract getEndpointPluginData(req: Request, endpoint: string, isUpgrade: boolean, isEngineIOEndpoint: boolean): Promise<T>;
41
+ abstract getEndpointPluginData(req: Request, endpoint: string, isUpgrade: boolean, isEngineIOEndpoint: boolean): Promise<T | undefined>;
42
42
  abstract handleWebSocket(endpoint: string, httpRequest: HttpRequest, ws: WebSocket, pluginData: T): Promise<void>;
43
43
  abstract checkUpgrade(req: Request, res: Response, pluginData: T): void;
44
44
 
@@ -49,7 +49,7 @@ export abstract class PluginHttp<T> {
49
49
 
50
50
  const end = (code: number, message: string) => {
51
51
  if (isUpgrade) {
52
- const socket = res.socket;
52
+ const socket = res.socket!;
53
53
  socket.write(`HTTP/1.1 ${code} ${message}\r\n` +
54
54
  '\r\n');
55
55
  socket.destroy();
@@ -65,6 +65,10 @@ export abstract class PluginHttp<T> {
65
65
  let endpoint = pkg;
66
66
  if (owner)
67
67
  endpoint = `@${owner}/${endpoint}`;
68
+ if (!endpoint) {
69
+ end(400, 'Bad Request');
70
+ return;
71
+ }
68
72
  const pluginData = await this.getEndpointPluginData(req, endpoint, isUpgrade, isEngineIOEndpoint);
69
73
 
70
74
  if (!pluginData) {
@@ -125,7 +129,7 @@ export abstract class PluginHttp<T> {
125
129
  catch (e) {
126
130
  res.header('Content-Type', 'text/plain');
127
131
  res.status(500);
128
- res.send(e.toString());
132
+ res.send((e as Error).toString());
129
133
  console.error(e);
130
134
  }
131
135
  }
@@ -136,7 +140,7 @@ export abstract class PluginHttp<T> {
136
140
  catch (e) {
137
141
  res.header('Content-Type', 'text/plain');
138
142
  res.status(500);
139
- res.send(e.toString());
143
+ res.send((e as Error).toString());
140
144
  console.error(e);
141
145
  }
142
146
  }
@@ -8,7 +8,7 @@ import { PluginRemote, PluginRemoteLoadZipOptions, PluginZipAPI } from './plugin
8
8
  * execution order of state reporting may fail.
9
9
  */
10
10
  export class LazyRemote implements PluginRemote {
11
- remote: PluginRemote;
11
+ remote!: PluginRemote;
12
12
 
13
13
  constructor(public remotePromise: Promise<PluginRemote>, public remoteReadyPromise: Promise<PluginRemote>) {
14
14
  this.remoteReadyPromise = (async () => {
@@ -74,7 +74,7 @@ export function createWebSocketClass(__websocketConnect: WebSocketConnect): any
74
74
 
75
75
  class WebSocket extends WebSocketEventTarget {
76
76
  _url: string;
77
- _protocols: string[];
77
+ _protocols: string[] | undefined;
78
78
  readyState: number;
79
79
 
80
80
  constructor(public connection: WebSocketConnection, protocols?: string[]) {