@scrypted/server 0.0.108 → 0.0.112

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.

Potentially problematic release.


This version of @scrypted/server might be problematic. Click here for more details.

package/src/rpc.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import vm from 'vm';
2
2
 
3
+ const finalizerIdSymbol = Symbol('rpcFinalizerId');
4
+
3
5
  function getDefaultTransportSafeArgumentTypes() {
4
6
  const jsonSerializable = new Set<string>();
5
7
  jsonSerializable.add(Number.name);
@@ -40,6 +42,7 @@ interface RpcOob extends RpcMessage {
40
42
 
41
43
  interface RpcRemoteProxyValue {
42
44
  __remote_proxy_id: string;
45
+ __remote_proxy_finalizer_id: string;
43
46
  __remote_constructor_name: string;
44
47
  __remote_proxy_props: any;
45
48
  __remote_proxy_oneway_methods: string[];
@@ -52,6 +55,7 @@ interface RpcLocalProxyValue {
52
55
 
53
56
  interface RpcFinalize extends RpcMessage {
54
57
  __local_proxy_id: string;
58
+ __local_proxy_finalizer_id: string;
55
59
  }
56
60
 
57
61
  interface Deferred {
@@ -78,20 +82,17 @@ export const PROPERTY_PROXY_PROPERTIES = '__proxy_props';
78
82
  export const PROPERTY_JSON_COPY_SERIALIZE_CHILDREN = '__json_copy_serialize_children';
79
83
 
80
84
  class RpcProxy implements ProxyHandler<any> {
85
+
81
86
  constructor(public peer: RpcPeer,
82
- public id: string,
87
+ public entry: LocalProxiedEntry,
83
88
  public constructorName: string,
84
89
  public proxyProps: any,
85
90
  public proxyOneWayMethods: string[]) {
86
- this.peer = peer;
87
- this.id = id;
88
- this.constructorName = constructorName;
89
- this.proxyProps = proxyProps;
90
91
  }
91
92
 
92
93
  get(target: any, p: PropertyKey, receiver: any): any {
93
94
  if (p === '__proxy_id')
94
- return this.id;
95
+ return this.entry.id;
95
96
  if (p === '__proxy_constructor')
96
97
  return this.constructorName;
97
98
  if (p === '__proxy_peer')
@@ -114,8 +115,17 @@ class RpcProxy implements ProxyHandler<any> {
114
115
  return new Proxy(() => p, this);
115
116
  }
116
117
 
118
+ set(target: any, p: string | symbol, value: any, receiver: any): boolean {
119
+ if (p === finalizerIdSymbol)
120
+ this.entry.finalizerId = value;
121
+ return true;
122
+ }
123
+
117
124
  apply(target: any, thisArg: any, argArray?: any): any {
118
- const method = target();
125
+ // rpc objects can be functions. if the function is a oneway method,
126
+ // it will have a null in the oneway method list. this is because
127
+ // undefined is not JSON serializable.
128
+ const method = target() || null;
119
129
  const args: any[] = [];
120
130
  for (const arg of (argArray || [])) {
121
131
  args.push(this.peer.serialize(arg));
@@ -124,7 +134,7 @@ class RpcProxy implements ProxyHandler<any> {
124
134
  const rpcApply: RpcApply = {
125
135
  type: "apply",
126
136
  id: undefined,
127
- proxyId: this.id,
137
+ proxyId: this.entry.id,
128
138
  args,
129
139
  method,
130
140
  };
@@ -187,16 +197,21 @@ export interface RpcSerializer {
187
197
  deserialize(serialized: any): any;
188
198
  }
189
199
 
200
+ interface LocalProxiedEntry {
201
+ id: string;
202
+ finalizerId: string;
203
+ }
204
+
190
205
  export class RpcPeer {
191
206
  idCounter = 1;
192
207
  onOob: (oob: any) => void;
193
208
  params: { [name: string]: any } = {};
194
209
  pendingResults: { [id: string]: Deferred } = {};
195
210
  proxyCounter = 1;
196
- localProxied = new Map<any, string>();
211
+ localProxied = new Map<any, LocalProxiedEntry>();
197
212
  localProxyMap: { [id: string]: any } = {};
198
213
  remoteWeakProxies: { [id: string]: WeakRef<any> } = {};
199
- finalizers = new FinalizationRegistry(id => this.finalize(id as string));
214
+ finalizers = new FinalizationRegistry(entry => this.finalize(entry as LocalProxiedEntry));
200
215
  nameDeserializerMap = new Map<string, RpcSerializer>();
201
216
  constructorSerializerMap = new Map<string, string>();
202
217
  transportSafeArgumentTypes = getDefaultTransportSafeArgumentTypes();
@@ -238,10 +253,11 @@ export class RpcPeer {
238
253
  this.constructorSerializerMap.set(ctr, name);
239
254
  }
240
255
 
241
- finalize(id: string) {
242
- delete this.remoteWeakProxies[id];
256
+ finalize(entry: LocalProxiedEntry) {
257
+ delete this.remoteWeakProxies[entry.id];
243
258
  const rpcFinalize: RpcFinalize = {
244
- __local_proxy_id: id,
259
+ __local_proxy_id: entry.id,
260
+ __local_proxy_finalizer_id: entry.finalizerId,
245
261
  type: 'finalize',
246
262
  }
247
263
  this.send(rpcFinalize);
@@ -294,9 +310,12 @@ export class RpcPeer {
294
310
  return ret;
295
311
  }
296
312
 
297
- const { __remote_proxy_id, __local_proxy_id, __remote_constructor_name, __serialized_value, __remote_proxy_props, __remote_proxy_oneway_methods } = value;
313
+ const { __remote_proxy_id, __remote_proxy_finalizer_id, __local_proxy_id, __remote_constructor_name, __serialized_value, __remote_proxy_props, __remote_proxy_oneway_methods } = value;
298
314
  if (__remote_proxy_id) {
299
- const proxy = this.remoteWeakProxies[__remote_proxy_id]?.deref() || this.newProxy(__remote_proxy_id, __remote_constructor_name, __remote_proxy_props, __remote_proxy_oneway_methods);
315
+ let proxy = this.remoteWeakProxies[__remote_proxy_id]?.deref();
316
+ if (!proxy)
317
+ proxy = this.newProxy(__remote_proxy_id, __remote_constructor_name, __remote_proxy_props, __remote_proxy_oneway_methods);
318
+ proxy[finalizerIdSymbol] = __remote_proxy_finalizer_id;
300
319
  return proxy;
301
320
  }
302
321
 
@@ -329,10 +348,13 @@ export class RpcPeer {
329
348
 
330
349
  let __remote_constructor_name = value.__proxy_constructor || value.constructor?.name?.toString();
331
350
 
332
- let proxyId = this.localProxied.get(value);
333
- if (proxyId) {
351
+ let proxiedEntry = this.localProxied.get(value);
352
+ if (proxiedEntry) {
353
+ const __remote_proxy_finalizer_id = (this.proxyCounter++).toString();
354
+ proxiedEntry.finalizerId = __remote_proxy_finalizer_id;
334
355
  const ret: RpcRemoteProxyValue = {
335
- __remote_proxy_id: proxyId,
356
+ __remote_proxy_id: proxiedEntry.id,
357
+ __remote_proxy_finalizer_id,
336
358
  __remote_constructor_name,
337
359
  __remote_proxy_props: value?.[PROPERTY_PROXY_PROPERTIES],
338
360
  __remote_proxy_oneway_methods: value?.[PROPERTY_PROXY_ONEWAY_METHODS],
@@ -355,6 +377,7 @@ export class RpcPeer {
355
377
  const serialized = serializer.serialize(value);
356
378
  const ret: RpcRemoteProxyValue = {
357
379
  __remote_proxy_id: undefined,
380
+ __remote_proxy_finalizer_id: undefined,
358
381
  __remote_constructor_name,
359
382
  __remote_proxy_props: value?.[PROPERTY_PROXY_PROPERTIES],
360
383
  __remote_proxy_oneway_methods: value?.[PROPERTY_PROXY_ONEWAY_METHODS],
@@ -363,12 +386,17 @@ export class RpcPeer {
363
386
  return ret;
364
387
  }
365
388
 
366
- proxyId = (this.proxyCounter++).toString();
367
- this.localProxied.set(value, proxyId);
368
- this.localProxyMap[proxyId] = value;
389
+ const __remote_proxy_id = (this.proxyCounter++).toString();
390
+ proxiedEntry = {
391
+ id: __remote_proxy_id,
392
+ finalizerId: __remote_proxy_id,
393
+ };
394
+ this.localProxied.set(value, proxiedEntry);
395
+ this.localProxyMap[__remote_proxy_id] = value;
369
396
 
370
397
  const ret: RpcRemoteProxyValue = {
371
- __remote_proxy_id: proxyId,
398
+ __remote_proxy_id,
399
+ __remote_proxy_finalizer_id: __remote_proxy_id,
372
400
  __remote_constructor_name,
373
401
  __remote_proxy_props: value?.[PROPERTY_PROXY_PROPERTIES],
374
402
  __remote_proxy_oneway_methods: value?.[PROPERTY_PROXY_ONEWAY_METHODS],
@@ -378,12 +406,16 @@ export class RpcPeer {
378
406
  }
379
407
 
380
408
  newProxy(proxyId: string, proxyConstructorName: string, proxyProps: any, proxyOneWayMethods: string[]) {
381
- const rpc = new RpcProxy(this, proxyId, proxyConstructorName, proxyProps, proxyOneWayMethods);
409
+ const localProxiedEntry: LocalProxiedEntry = {
410
+ id: proxyId,
411
+ finalizerId: undefined,
412
+ }
413
+ const rpc = new RpcProxy(this, localProxiedEntry, proxyConstructorName, proxyProps, proxyOneWayMethods);
382
414
  const target = proxyConstructorName === 'Function' || proxyConstructorName === 'AsyncFunction' ? function () { } : rpc;
383
415
  const proxy = new Proxy(target, rpc);
384
416
  const weakref = new WeakRef(proxy);
385
417
  this.remoteWeakProxies[proxyId] = weakref;
386
- this.finalizers.register(rpc, proxyId);
418
+ this.finalizers.register(rpc, localProxiedEntry);
387
419
  global.gc?.();
388
420
  return proxy;
389
421
  }
@@ -460,8 +492,16 @@ export class RpcPeer {
460
492
  case 'finalize': {
461
493
  const rpcFinalize = message as RpcFinalize;
462
494
  const local = this.localProxyMap[rpcFinalize.__local_proxy_id];
463
- delete this.localProxyMap[rpcFinalize.__local_proxy_id];
464
- this.localProxied.delete(local);
495
+ if (local) {
496
+ const localProxiedEntry = this.localProxied.get(local);
497
+ // if a finalizer id is specified, it must match.
498
+ if (rpcFinalize.__local_proxy_finalizer_id && rpcFinalize.__local_proxy_finalizer_id !== localProxiedEntry?.finalizerId) {
499
+ console.error(this.selfName, this.peerName, 'finalizer mismatch')
500
+ break;
501
+ }
502
+ delete this.localProxyMap[rpcFinalize.__local_proxy_id];
503
+ this.localProxied.delete(local);
504
+ }
465
505
  break;
466
506
  }
467
507
  case 'oob': {
package/src/state.ts CHANGED
@@ -99,7 +99,7 @@ export class ScryptedStateManager extends EventRegistry {
99
99
  return systemState;
100
100
  }
101
101
 
102
- listenDevice(id: string, options: string | EventListenerOptions, callback: (eventDetails: EventDetails, eventData: object) => void): EventListenerRegister {
102
+ listenDevice(id: string, options: string | EventListenerOptions, callback: (eventDetails: EventDetails, eventData: any) => void): EventListenerRegister {
103
103
  let { denoise, event, watch } = (options || {}) as EventListenerOptions;
104
104
  if (!event && typeof options === 'string')
105
105
  event = options as string;
@@ -124,7 +124,7 @@ export class ScryptedStateManager extends EventRegistry {
124
124
  }
125
125
 
126
126
  let lastData: any = undefined;
127
- let cb = (eventDetails: EventDetails, eventData: object) => {
127
+ let cb = (eventDetails: EventDetails, eventData: any) => {
128
128
  if (denoise && lastData === eventData)
129
129
  return;
130
130
  callback(eventDetails, eventData);