@scrypted/server 0.6.26 → 0.7.2

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.

Files changed (48) hide show
  1. package/dist/http-interfaces.js +4 -1
  2. package/dist/http-interfaces.js.map +1 -1
  3. package/dist/plugin/media.js +15 -17
  4. package/dist/plugin/media.js.map +1 -1
  5. package/dist/plugin/plugin-console.js +157 -4
  6. package/dist/plugin/plugin-console.js.map +1 -1
  7. package/dist/plugin/plugin-device.js +2 -0
  8. package/dist/plugin/plugin-device.js.map +1 -1
  9. package/dist/plugin/plugin-host.js +5 -0
  10. package/dist/plugin/plugin-host.js.map +1 -1
  11. package/dist/plugin/plugin-remote-stats.js +30 -0
  12. package/dist/plugin/plugin-remote-stats.js.map +1 -0
  13. package/dist/plugin/plugin-remote-worker.js +69 -149
  14. package/dist/plugin/plugin-remote-worker.js.map +1 -1
  15. package/dist/plugin/plugin-repl.js +4 -1
  16. package/dist/plugin/plugin-repl.js.map +1 -1
  17. package/dist/plugin/runtime/python-worker.js +1 -0
  18. package/dist/plugin/runtime/python-worker.js.map +1 -1
  19. package/dist/plugin/system.js +4 -0
  20. package/dist/plugin/system.js.map +1 -1
  21. package/dist/rpc.js +180 -45
  22. package/dist/rpc.js.map +1 -1
  23. package/dist/runtime.js +3 -0
  24. package/dist/runtime.js.map +1 -1
  25. package/dist/threading.js +1 -0
  26. package/dist/threading.js.map +1 -1
  27. package/package.json +3 -4
  28. package/python/plugin_remote.py +133 -50
  29. package/python/rpc-iterator-test.py +45 -0
  30. package/python/rpc.py +168 -50
  31. package/python/rpc_reader.py +57 -60
  32. package/src/http-interfaces.ts +5 -1
  33. package/src/plugin/media.ts +16 -17
  34. package/src/plugin/plugin-api.ts +4 -1
  35. package/src/plugin/plugin-console.ts +154 -6
  36. package/src/plugin/plugin-device.ts +3 -0
  37. package/src/plugin/plugin-host.ts +5 -0
  38. package/src/plugin/plugin-remote-stats.ts +36 -0
  39. package/src/plugin/plugin-remote-worker.ts +77 -178
  40. package/src/plugin/plugin-remote.ts +1 -1
  41. package/src/plugin/plugin-repl.ts +4 -1
  42. package/src/plugin/runtime/python-worker.ts +2 -0
  43. package/src/plugin/system.ts +6 -0
  44. package/src/rpc.ts +225 -50
  45. package/src/runtime.ts +3 -0
  46. package/src/threading.ts +2 -0
  47. package/test/rpc-iterator-test.ts +46 -0
  48. package/test/rpc-python-test.ts +44 -0
package/src/rpc.ts CHANGED
@@ -48,8 +48,18 @@ interface RpcApply extends RpcMessage {
48
48
 
49
49
  interface RpcResult extends RpcMessage {
50
50
  id: string;
51
+ // TODO 3/2/2023
52
+ // deprecate these properties from rpc protocol. treat error results like any other result
53
+ // and auto serialize them.
54
+ /**
55
+ * @deprecated
56
+ */
51
57
  stack?: string;
58
+ /**
59
+ * @deprecated
60
+ */
52
61
  message?: string;
62
+ throw?: boolean;
53
63
  result?: any;
54
64
  }
55
65
 
@@ -81,6 +91,12 @@ export interface PrimitiveProxyHandler<T extends object> extends ProxyHandler<T>
81
91
  }
82
92
 
83
93
  class RpcProxy implements PrimitiveProxyHandler<any> {
94
+ static iteratorMethods = new Set([
95
+ 'next',
96
+ 'throw',
97
+ 'return',
98
+ ]);
99
+
84
100
  constructor(public peer: RpcPeer,
85
101
  public entry: LocalProxiedEntry,
86
102
  public constructorName: string,
@@ -94,6 +110,18 @@ class RpcProxy implements PrimitiveProxyHandler<any> {
94
110
  }
95
111
 
96
112
  get(target: any, p: PropertyKey, receiver: any): any {
113
+ if (p === Symbol.asyncIterator) {
114
+ if (!this.proxyProps?.[Symbol.asyncIterator.toString()])
115
+ return;
116
+ return () => {
117
+ return new Proxy(() => { }, this);
118
+ };
119
+ }
120
+ if (RpcProxy.iteratorMethods.has(p?.toString())) {
121
+ const asyncIteratorMethod = this.proxyProps?.[Symbol.asyncIterator.toString()]?.[p];
122
+ if (asyncIteratorMethod)
123
+ return new Proxy(() => asyncIteratorMethod, this);
124
+ }
97
125
  if (p === RpcPeer.PROPERTY_PROXY_ID)
98
126
  return this.entry.id;
99
127
  if (p === '__proxy_constructor')
@@ -131,13 +159,18 @@ class RpcProxy implements PrimitiveProxyHandler<any> {
131
159
  }
132
160
 
133
161
  apply(target: any, thisArg: any, argArray?: any): any {
134
- if (Object.isFrozen(this.peer.pendingResults))
135
- return Promise.reject(new RPCResultError(this.peer, 'RpcPeer has been killed'));
162
+ const method = target() || null;
163
+ const oneway = this.proxyOneWayMethods?.includes?.(method);
164
+
165
+ if (Object.isFrozen(this.peer.pendingResults)) {
166
+ if (oneway)
167
+ return Promise.resolve();
168
+ return Promise.reject(new RPCResultError(this.peer, 'RpcPeer has been killed (apply) ' + target()));
169
+ }
136
170
 
137
171
  // rpc objects can be functions. if the function is a oneway method,
138
172
  // it will have a null in the oneway method list. this is because
139
173
  // undefined is not JSON serializable.
140
- const method = target() || null;
141
174
  const args: any[] = [];
142
175
  const serializationContext: any = {};
143
176
  for (const arg of (argArray || [])) {
@@ -152,7 +185,7 @@ class RpcProxy implements PrimitiveProxyHandler<any> {
152
185
  method,
153
186
  };
154
187
 
155
- if (this.proxyOneWayMethods?.includes?.(method)) {
188
+ if (oneway) {
156
189
  rpcApply.oneway = true;
157
190
  // a oneway callable object doesn't need to be in the JSON payload.
158
191
  if (method === null)
@@ -161,13 +194,46 @@ class RpcProxy implements PrimitiveProxyHandler<any> {
161
194
  return Promise.resolve();
162
195
  }
163
196
 
164
- return this.peer.createPendingResult((id, reject) => {
197
+ const pendingResult = this.peer.createPendingResult((id, reject) => {
165
198
  rpcApply.id = id;
166
199
  this.peer.send(rpcApply, reject, serializationContext);
167
- })
200
+ });
201
+
202
+ const asyncIterator = this.proxyProps?.[Symbol.asyncIterator.toString()];
203
+ if (!asyncIterator || (method !== asyncIterator.next && method !== asyncIterator.return))
204
+ return pendingResult;
205
+
206
+ return pendingResult
207
+ .then(value => {
208
+ if (method === asyncIterator.return) {
209
+ return {
210
+ done: true,
211
+ value: undefined,
212
+ }
213
+ }
214
+ return ({
215
+ value,
216
+ done: false,
217
+ });
218
+ })
219
+ .catch(e => {
220
+ if (e.name === 'StopAsyncIteration') {
221
+ return {
222
+ done: true,
223
+ value: undefined,
224
+ }
225
+ }
226
+ throw e;
227
+ })
168
228
  }
169
229
  }
170
230
 
231
+ interface SerialiedRpcResultError {
232
+ name: string;
233
+ stack: string;
234
+ message: string;
235
+ }
236
+
171
237
  // todo: error constructor adds a "cause" variable in Chrome 93, Node v??
172
238
  export class RPCResultError extends Error {
173
239
  constructor(peer: RpcPeer, message: string, public cause?: Error, options?: { name: string, stack: string | undefined }) {
@@ -177,7 +243,7 @@ export class RPCResultError extends Error {
177
243
  this.name = options?.name;
178
244
  }
179
245
  if (options?.stack) {
180
- this.stack = `${peer.peerName}:${peer.selfName}\n${cause?.stack || options.stack}`;
246
+ this.stack = `${cause?.stack || options.stack}\n${peer.peerName}:${peer.selfName}`;
181
247
  }
182
248
  }
183
249
  }
@@ -188,10 +254,10 @@ function compileFunction(code: string, params?: ReadonlyArray<string>, options?:
188
254
  return eval(f);
189
255
  }
190
256
 
191
- declare class WeakRef {
192
- target: any;
257
+ declare class WeakRef<T> {
258
+ target: T;
193
259
  constructor(target: any);
194
- deref(): any;
260
+ deref(): T;
195
261
  }
196
262
 
197
263
  try {
@@ -225,19 +291,26 @@ interface LocalProxiedEntry {
225
291
  finalizerId: string | undefined;
226
292
  }
227
293
 
294
+ interface ErrorType {
295
+ name: string;
296
+ message: string;
297
+ stack?: string;
298
+ }
299
+
228
300
  export class RpcPeer {
229
301
  idCounter = 1;
230
302
  params: { [name: string]: any } = {};
231
303
  pendingResults: { [id: string]: Deferred } = {};
232
304
  proxyCounter = 1;
233
305
  localProxied = new Map<any, LocalProxiedEntry>();
234
- localProxyMap: { [id: string]: any } = {};
306
+ localProxyMap = new Map<string, any>();
235
307
  // @ts-ignore
236
308
  remoteWeakProxies: { [id: string]: WeakRef<any> } = {};
237
309
  // @ts-ignore
238
310
  finalizers = new FinalizationRegistry(entry => this.finalize(entry as LocalProxiedEntry));
239
311
  nameDeserializerMap = new Map<string, RpcSerializer>();
240
- onSerialization = new Map<string, (value: any) => void>();
312
+ onProxyTypeSerialization = new Map<string, (value: any) => void>();
313
+ onProxySerialization: (value: any, proxyId: string) => any;
241
314
  constructorSerializerMap = new Map<any, string>();
242
315
  transportSafeArgumentTypes = RpcPeer.getDefaultTransportSafeArgumentTypes();
243
316
  killed: Promise<string>;
@@ -281,6 +354,37 @@ export class RpcPeer {
281
354
  }
282
355
  }
283
356
 
357
+ // static setProxyProperties(value: any, properties: any) {
358
+ // value[RpcPeer.PROPERTY_PROXY_PROPERTIES] = properties;
359
+ // }
360
+
361
+ // static getProxyProperties(value: any) {
362
+ // return value?.[RpcPeer.PROPERTY_PROXY_PROPERTIES];
363
+ // }
364
+
365
+ static getIteratorNext(target: any): string {
366
+ if (!target[Symbol.asyncIterator])
367
+ return;
368
+ const proxyProps = target[this.PROPERTY_PROXY_PROPERTIES]?.[Symbol.asyncIterator.toString()];
369
+ return proxyProps?.next || 'next';
370
+ }
371
+
372
+ static prepareProxyProperties(value: any) {
373
+ let props = value?.[RpcPeer.PROPERTY_PROXY_PROPERTIES];
374
+ if (!value[Symbol.asyncIterator])
375
+ return props;
376
+ props ||= {};
377
+ if (!props[Symbol.asyncIterator.toString()]) {
378
+ props[Symbol.asyncIterator.toString()] = {
379
+ next: 'next',
380
+ throw: 'throw',
381
+ return: 'return',
382
+ };
383
+ }
384
+ return props;
385
+ }
386
+
387
+ static readonly RPC_RESULT_ERROR_NAME = 'RPCResultError';
284
388
  static readonly PROPERTY_PROXY_ID = '__proxy_id';
285
389
  static readonly PROPERTY_PROXY_ONEWAY_METHODS = '__proxy_oneway_methods';
286
390
  static readonly PROPERTY_JSON_DISABLE_SERIALIZATION = '__json_disable_serialization';
@@ -304,9 +408,17 @@ export class RpcPeer {
304
408
  }).catch(e => e.message || 'Unknown Error');
305
409
  }
306
410
 
411
+ static isTransportSafe(value: any) {
412
+ return !value || (!value[RpcPeer.PROPERTY_JSON_DISABLE_SERIALIZATION] && this.getDefaultTransportSafeArgumentTypes().has(value.constructor?.name));
413
+ }
414
+
415
+ isTransportSafe(value: any) {
416
+ return !value || (!value[RpcPeer.PROPERTY_JSON_DISABLE_SERIALIZATION] && this.transportSafeArgumentTypes.has(value.constructor?.name));
417
+ }
418
+
307
419
  createPendingResult(cb: (id: string, reject: (e: Error) => void) => void): Promise<any> {
308
420
  if (Object.isFrozen(this.pendingResults))
309
- return Promise.reject(new RPCResultError(this, 'RpcPeer has been killed'));
421
+ return Promise.reject(new RPCResultError(this, 'RpcPeer has been killed (createPendingResult)'));
310
422
 
311
423
  const promise = new Promise((resolve, reject) => {
312
424
  const id = (this.idCounter++).toString();
@@ -332,7 +444,7 @@ export class RpcPeer {
332
444
  this.pendingResults = Object.freeze({});
333
445
  this.params = Object.freeze({});
334
446
  this.remoteWeakProxies = Object.freeze({});
335
- this.localProxyMap = Object.freeze({});
447
+ this.localProxyMap.clear()
336
448
  this.localProxied.clear();
337
449
  }
338
450
 
@@ -383,10 +495,16 @@ export class RpcPeer {
383
495
  return value;
384
496
  }
385
497
 
386
- createErrorResult(result: RpcResult, e: any) {
387
- result.stack = e.stack || 'no stack';
388
- result.result = (e as Error).name || 'no name';
498
+ /**
499
+ * @deprecated
500
+ * @param result
501
+ * @param e
502
+ */
503
+ createErrorResult(result: RpcResult, e: ErrorType) {
504
+ result.result = this.serializeError(e);
505
+ result.throw = true;
389
506
  result.message = (e as Error).message || 'no message';
507
+ result.stack = e.stack || 'no stack';
390
508
  }
391
509
 
392
510
  deserialize(value: any, deserializationContext: any): any {
@@ -403,6 +521,9 @@ export class RpcPeer {
403
521
  }
404
522
 
405
523
  const { __remote_proxy_id, __remote_proxy_finalizer_id, __local_proxy_id, __remote_constructor_name, __serialized_value, __remote_proxy_props, __remote_proxy_oneway_methods } = value;
524
+ if (__remote_constructor_name === RpcPeer.RPC_RESULT_ERROR_NAME)
525
+ return this.deserializeError(__serialized_value);
526
+
406
527
  if (__remote_proxy_id) {
407
528
  let proxy = this.remoteWeakProxies[__remote_proxy_id]?.deref();
408
529
  if (!proxy)
@@ -418,7 +539,7 @@ export class RpcPeer {
418
539
  }
419
540
 
420
541
  if (__local_proxy_id) {
421
- const ret = this.localProxyMap[__local_proxy_id];
542
+ const ret = this.localProxyMap.get(__local_proxy_id);
422
543
  if (!ret)
423
544
  throw new RPCResultError(this, `invalid local proxy id ${__local_proxy_id}`);
424
545
  return ret;
@@ -432,6 +553,28 @@ export class RpcPeer {
432
553
  return value;
433
554
  }
434
555
 
556
+ deserializeError(e: SerialiedRpcResultError): RPCResultError {
557
+ const { name, stack, message } = e;
558
+ return new RPCResultError(this, message, undefined, { name, stack });
559
+ }
560
+
561
+ serializeError(e: ErrorType): RpcRemoteProxyValue {
562
+ const __serialized_value: SerialiedRpcResultError = {
563
+ stack: e.stack || '[no stack]',
564
+ name: e.name || '[no name]',
565
+ message: e.message || '[no message]',
566
+ }
567
+ return {
568
+ // probably not safe to use constructor.name
569
+ __remote_constructor_name: RpcPeer.RPC_RESULT_ERROR_NAME,
570
+ __remote_proxy_id: undefined,
571
+ __remote_proxy_finalizer_id: undefined,
572
+ __remote_proxy_oneway_methods: undefined,
573
+ __remote_proxy_props: undefined,
574
+ __serialized_value,
575
+ };
576
+ }
577
+
435
578
  serialize(value: any, serializationContext: any): any {
436
579
  if (value?.[RpcPeer.PROPERTY_JSON_COPY_SERIALIZE_CHILDREN] === true) {
437
580
  const ret: any = {};
@@ -441,12 +584,32 @@ export class RpcPeer {
441
584
  return ret;
442
585
  }
443
586
 
444
- if (!value || (!value[RpcPeer.PROPERTY_JSON_DISABLE_SERIALIZATION] && this.transportSafeArgumentTypes.has(value.constructor?.name))) {
587
+ if (this.isTransportSafe(value)) {
445
588
  return value;
446
589
  }
447
590
 
448
591
  let __remote_constructor_name = value.__proxy_constructor || value.constructor?.name?.toString();
449
- this.onSerialization.get(__remote_constructor_name)?.(value);
592
+
593
+ if (value instanceof Error)
594
+ return this.serializeError(value);
595
+
596
+ const serializerMapName = this.constructorSerializerMap.get(value.constructor);
597
+ if (serializerMapName) {
598
+ __remote_constructor_name = serializerMapName;
599
+ const serializer = this.nameDeserializerMap.get(serializerMapName);
600
+ if (!serializer)
601
+ throw new Error('serializer not found for ' + serializerMapName);
602
+ const serialized = serializer.serialize(value, serializationContext);
603
+ const ret: RpcRemoteProxyValue = {
604
+ __remote_proxy_id: undefined,
605
+ __remote_proxy_finalizer_id: undefined,
606
+ __remote_constructor_name,
607
+ __remote_proxy_props: RpcPeer.prepareProxyProperties(value),
608
+ __remote_proxy_oneway_methods: value?.[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS],
609
+ __serialized_value: serialized,
610
+ }
611
+ return ret;
612
+ }
450
613
 
451
614
  let proxiedEntry = this.localProxied.get(value);
452
615
  if (proxiedEntry) {
@@ -456,7 +619,7 @@ export class RpcPeer {
456
619
  __remote_proxy_id: proxiedEntry.id,
457
620
  __remote_proxy_finalizer_id,
458
621
  __remote_constructor_name,
459
- __remote_proxy_props: value?.[RpcPeer.PROPERTY_PROXY_PROPERTIES],
622
+ __remote_proxy_props: RpcPeer.prepareProxyProperties(value),
460
623
  __remote_proxy_oneway_methods: value?.[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS],
461
624
  }
462
625
  return ret;
@@ -470,23 +633,7 @@ export class RpcPeer {
470
633
  return ret;
471
634
  }
472
635
 
473
- const serializerMapName = this.constructorSerializerMap.get(value.constructor);
474
- if (serializerMapName) {
475
- __remote_constructor_name = serializerMapName;
476
- const serializer = this.nameDeserializerMap.get(serializerMapName);
477
- if (!serializer)
478
- throw new Error('serializer not found for ' + serializerMapName);
479
- const serialized = serializer.serialize(value, serializationContext);
480
- const ret: RpcRemoteProxyValue = {
481
- __remote_proxy_id: undefined,
482
- __remote_proxy_finalizer_id: undefined,
483
- __remote_constructor_name,
484
- __remote_proxy_props: value?.[RpcPeer.PROPERTY_PROXY_PROPERTIES],
485
- __remote_proxy_oneway_methods: value?.[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS],
486
- __serialized_value: serialized,
487
- }
488
- return ret;
489
- }
636
+ this.onProxyTypeSerialization.get(__remote_constructor_name)?.(value);
490
637
 
491
638
  const __remote_proxy_id = (this.proxyCounter++).toString();
492
639
  proxiedEntry = {
@@ -494,13 +641,15 @@ export class RpcPeer {
494
641
  finalizerId: __remote_proxy_id,
495
642
  };
496
643
  this.localProxied.set(value, proxiedEntry);
497
- this.localProxyMap[__remote_proxy_id] = value;
644
+ this.localProxyMap.set(__remote_proxy_id, value);
645
+
646
+ const __remote_proxy_props = this.onProxySerialization ? this.onProxySerialization(value, __remote_proxy_id) : RpcPeer.prepareProxyProperties(value);
498
647
 
499
648
  const ret: RpcRemoteProxyValue = {
500
649
  __remote_proxy_id,
501
650
  __remote_proxy_finalizer_id: __remote_proxy_id,
502
651
  __remote_constructor_name,
503
- __remote_proxy_props: value?.[RpcPeer.PROPERTY_PROXY_PROPERTIES],
652
+ __remote_proxy_props,
504
653
  __remote_proxy_oneway_methods: value?.[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS],
505
654
  }
506
655
 
@@ -543,11 +692,19 @@ export class RpcPeer {
543
692
  case 'param': {
544
693
  const rpcParam = message as RpcParam;
545
694
  const serializationContext: any = {};
546
- const result: RpcResult = {
547
- type: 'result',
548
- id: rpcParam.id,
549
- result: this.serialize(this.params[rpcParam.param], serializationContext)
550
- };
695
+ let result: RpcResult;
696
+ try {
697
+ result = {
698
+ type: 'result',
699
+ id: rpcParam.id,
700
+ result: this.serialize(this.params[rpcParam.param], serializationContext)
701
+ };
702
+ }
703
+ catch (e) {
704
+ // console.error('failure', rpcApply.method, e);
705
+ this.createErrorResult(result, e);
706
+ }
707
+
551
708
  this.send(result, undefined, serializationContext);
552
709
  break;
553
710
  }
@@ -560,7 +717,7 @@ export class RpcPeer {
560
717
  const serializationContext: any = {};
561
718
 
562
719
  try {
563
- const target = this.localProxyMap[rpcApply.proxyId];
720
+ const target = this.localProxyMap.get(rpcApply.proxyId);
564
721
  if (!target)
565
722
  throw new Error(`proxy id ${rpcApply.proxyId} not found`);
566
723
 
@@ -575,6 +732,19 @@ export class RpcPeer {
575
732
  if (!method)
576
733
  throw new Error(`target ${target?.constructor?.name} does not have method ${rpcApply.method}`);
577
734
  value = await target[rpcApply.method](...args);
735
+
736
+ if (RpcPeer.getIteratorNext(target) === rpcApply.method) {
737
+ if (value.done) {
738
+ const errorType: ErrorType = {
739
+ name: 'StopAsyncIteration',
740
+ message: undefined,
741
+ };
742
+ throw errorType;
743
+ }
744
+ else {
745
+ value = value.value;
746
+ }
747
+ }
578
748
  }
579
749
  else {
580
750
  value = await target(...args);
@@ -592,12 +762,13 @@ export class RpcPeer {
592
762
  break;
593
763
  }
594
764
  case 'result': {
765
+ // console.log(message)
595
766
  const rpcResult = message as RpcResult;
596
767
  const deferred = this.pendingResults[rpcResult.id];
597
768
  delete this.pendingResults[rpcResult.id];
598
769
  if (!deferred)
599
770
  throw new Error(`unknown result ${rpcResult.id}`);
600
- if (rpcResult.message || rpcResult.stack) {
771
+ if ((rpcResult.message || rpcResult.stack) && !rpcResult.throw) {
601
772
  const e = new RPCResultError(this, rpcResult.message || 'no message', undefined, {
602
773
  name: rpcResult.result,
603
774
  stack: rpcResult.stack,
@@ -605,19 +776,23 @@ export class RpcPeer {
605
776
  deferred.reject(e);
606
777
  return;
607
778
  }
608
- deferred.resolve(this.deserialize(rpcResult.result, deserializationContext));
779
+ const deserialized = this.deserialize(rpcResult.result, deserializationContext);
780
+ if (rpcResult.throw)
781
+ deferred.reject(deserialized);
782
+ else
783
+ deferred.resolve(deserialized);
609
784
  break;
610
785
  }
611
786
  case 'finalize': {
612
787
  const rpcFinalize = message as RpcFinalize;
613
- const local = this.localProxyMap[rpcFinalize.__local_proxy_id];
788
+ const local = this.localProxyMap.get(rpcFinalize.__local_proxy_id);
614
789
  if (local) {
615
790
  const localProxiedEntry = this.localProxied.get(local);
616
791
  // if a finalizer id is specified, it must match.
617
792
  if (rpcFinalize.__local_proxy_finalizer_id && rpcFinalize.__local_proxy_finalizer_id !== localProxiedEntry?.finalizerId) {
618
793
  break;
619
794
  }
620
- delete this.localProxyMap[rpcFinalize.__local_proxy_id];
795
+ this.localProxyMap.delete(rpcFinalize.__local_proxy_id);
621
796
  this.localProxied.delete(local);
622
797
  }
623
798
  break;
package/src/runtime.ts CHANGED
@@ -39,6 +39,7 @@ import { PluginComponent } from './services/plugin';
39
39
  import { ServiceControl } from './services/service-control';
40
40
  import { UsersService } from './services/users';
41
41
  import { getState, ScryptedStateManager, setState } from './state';
42
+ import crypto from 'crypto';
42
43
 
43
44
  interface DeviceProxyPair {
44
45
  handler: PluginDeviceProxyHandler;
@@ -54,6 +55,8 @@ interface HttpPluginData {
54
55
  }
55
56
 
56
57
  export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
58
+ clusterId = crypto.randomBytes(3).toString('hex');
59
+ clusterSecret = crypto.randomBytes(16).toString('hex');
57
60
  datastore: Level;
58
61
  plugins: { [id: string]: PluginHost } = {};
59
62
  pluginDevices: { [id: string]: PluginDevice } = {};
package/src/threading.ts CHANGED
@@ -1,3 +1,5 @@
1
+ // WARNING: threading.ts does not work because RpcPeer transpilation does not include readonly static properties in the class definition
2
+
1
3
  import worker_threads from 'worker_threads';
2
4
  import { getEvalSource, RpcPeer } from './rpc';
3
5
  import v8 from 'v8';
@@ -0,0 +1,46 @@
1
+ import { RpcPeer } from "../src/rpc";
2
+ import { sleep } from '../src/sleep';
3
+
4
+ const p1 = new RpcPeer('p1', 'p2', message => {
5
+ // console.log('message p1 p2', message);
6
+ p2.handleMessage(message);
7
+ });
8
+
9
+ const p2 = new RpcPeer('p2', 'p1', message => {
10
+ // console.log('message p2 p1', message);
11
+ p1.handleMessage(message);
12
+ });
13
+
14
+ async function* generator() {
15
+ try {
16
+ yield 2;
17
+ yield 3;
18
+ }
19
+ catch (e) {
20
+ console.log('caught', e)
21
+ }
22
+ }
23
+
24
+ p1.params['thing'] = generator();
25
+
26
+ async function test() {
27
+ const foo = await p2.getParam('thing') as AsyncGenerator<number>;
28
+ if (true) {
29
+ for await (const c of foo) {
30
+ console.log(c);
31
+ }
32
+ }
33
+ else {
34
+ await sleep(0);
35
+ console.log(await foo.next());
36
+ await sleep(0);
37
+ // await foo.throw(new Error('barf'));
38
+ await foo.return(44);
39
+ await sleep(0);
40
+ console.log(await foo.next());
41
+ console.log(await foo.next());
42
+ }
43
+
44
+ }
45
+
46
+ test();
@@ -0,0 +1,44 @@
1
+ import child_process from 'child_process';
2
+ import path from 'path';
3
+ import type { Readable, Writable } from "stream";
4
+ import { createDuplexRpcPeer } from '../src/rpc-serializer';
5
+ import assert from 'assert';
6
+ import net from 'net';
7
+
8
+ async function main() {
9
+ const server = net.createServer(client => {
10
+ console.log('got client');
11
+ client.on('data', b => console.log('data', b.toString()));
12
+ });
13
+ server.listen(6666);
14
+
15
+ const cp = child_process.spawn('python3', [path.join(__dirname, '../python/rpc-iterator-test.py')], {
16
+ stdio: ['pipe', 'inherit', 'inherit', 'pipe', 'pipe'],
17
+ });
18
+
19
+ cp.on('exit', code => console.log('exited', code))
20
+
21
+ const rpcPeer = createDuplexRpcPeer('node', 'python', cp.stdio[3] as Readable, cp.stdio[4] as Writable);
22
+
23
+ async function* test() {
24
+ yield 1;
25
+ yield 2;
26
+ yield 3;
27
+ }
28
+
29
+ rpcPeer.params['test'] = test();
30
+
31
+ // const foo = await rpcPeer.getParam('foo');
32
+ // assert.equal(foo, 3);
33
+
34
+ // const bar = await rpcPeer.getParam('bar');
35
+ // console.log(bar);
36
+
37
+ // const ticker = await rpcPeer.getParam('ticker');
38
+ // for await (const v of ticker) {
39
+ // console.log(v);
40
+ // }
41
+ // process.exit();
42
+ }
43
+
44
+ main();