@scrypted/server 0.6.24 → 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 (51) hide show
  1. package/dist/http-interfaces.js +4 -1
  2. package/dist/http-interfaces.js.map +1 -1
  3. package/dist/listen-zero.js +5 -2
  4. package/dist/listen-zero.js.map +1 -1
  5. package/dist/plugin/media.js +25 -20
  6. package/dist/plugin/media.js.map +1 -1
  7. package/dist/plugin/plugin-console.js +157 -4
  8. package/dist/plugin/plugin-console.js.map +1 -1
  9. package/dist/plugin/plugin-device.js +2 -0
  10. package/dist/plugin/plugin-device.js.map +1 -1
  11. package/dist/plugin/plugin-host.js +5 -0
  12. package/dist/plugin/plugin-host.js.map +1 -1
  13. package/dist/plugin/plugin-remote-stats.js +30 -0
  14. package/dist/plugin/plugin-remote-stats.js.map +1 -0
  15. package/dist/plugin/plugin-remote-worker.js +69 -149
  16. package/dist/plugin/plugin-remote-worker.js.map +1 -1
  17. package/dist/plugin/plugin-repl.js +4 -1
  18. package/dist/plugin/plugin-repl.js.map +1 -1
  19. package/dist/plugin/runtime/python-worker.js +1 -0
  20. package/dist/plugin/runtime/python-worker.js.map +1 -1
  21. package/dist/plugin/system.js +4 -0
  22. package/dist/plugin/system.js.map +1 -1
  23. package/dist/rpc.js +183 -45
  24. package/dist/rpc.js.map +1 -1
  25. package/dist/runtime.js +3 -0
  26. package/dist/runtime.js.map +1 -1
  27. package/dist/threading.js +1 -0
  28. package/dist/threading.js.map +1 -1
  29. package/package.json +3 -4
  30. package/python/plugin_remote.py +134 -51
  31. package/python/rpc-iterator-test.py +45 -0
  32. package/python/rpc.py +168 -50
  33. package/python/rpc_reader.py +57 -60
  34. package/src/http-interfaces.ts +5 -1
  35. package/src/listen-zero.ts +6 -2
  36. package/src/plugin/media.ts +38 -35
  37. package/src/plugin/plugin-api.ts +4 -1
  38. package/src/plugin/plugin-console.ts +154 -6
  39. package/src/plugin/plugin-device.ts +3 -0
  40. package/src/plugin/plugin-host.ts +5 -0
  41. package/src/plugin/plugin-remote-stats.ts +36 -0
  42. package/src/plugin/plugin-remote-worker.ts +77 -178
  43. package/src/plugin/plugin-remote.ts +1 -1
  44. package/src/plugin/plugin-repl.ts +4 -1
  45. package/src/plugin/runtime/python-worker.ts +2 -0
  46. package/src/plugin/system.ts +6 -0
  47. package/src/rpc.ts +230 -52
  48. package/src/runtime.ts +3 -0
  49. package/src/threading.ts +2 -0
  50. package/test/rpc-iterator-test.ts +46 -0
  51. 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,21 +291,29 @@ 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>();
312
+ onProxyTypeSerialization = new Map<string, (value: any) => void>();
313
+ onProxySerialization: (value: any, proxyId: string) => any;
240
314
  constructorSerializerMap = new Map<any, string>();
241
315
  transportSafeArgumentTypes = RpcPeer.getDefaultTransportSafeArgumentTypes();
242
- killed: Promise<void>;
316
+ killed: Promise<string>;
243
317
  killedDeferred: Deferred;
244
318
  tags: any = {};
245
319
 
@@ -280,6 +354,37 @@ export class RpcPeer {
280
354
  }
281
355
  }
282
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';
283
388
  static readonly PROPERTY_PROXY_ID = '__proxy_id';
284
389
  static readonly PROPERTY_PROXY_ONEWAY_METHODS = '__proxy_oneway_methods';
285
390
  static readonly PROPERTY_JSON_DISABLE_SERIALIZATION = '__json_disable_serialization';
@@ -298,15 +403,22 @@ export class RpcPeer {
298
403
  ]);
299
404
 
300
405
  constructor(public selfName: string, public peerName: string, public send: (message: RpcMessage, reject?: (e: Error) => void, serializationContext?: any) => void) {
301
- this.killed = new Promise((resolve, reject) => {
406
+ this.killed = new Promise<string>((resolve, reject) => {
302
407
  this.killedDeferred = { resolve, reject };
303
- });
304
- this.killed.catch(() => { });
408
+ }).catch(e => e.message || 'Unknown Error');
409
+ }
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));
305
417
  }
306
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();
@@ -322,6 +434,8 @@ export class RpcPeer {
322
434
  }
323
435
 
324
436
  kill(message?: string) {
437
+ if (Object.isFrozen(this.pendingResults))
438
+ return;
325
439
  const error = new RPCResultError(this, message || 'peer was killed');
326
440
  this.killedDeferred.reject(error);
327
441
  for (const result of Object.values(this.pendingResults)) {
@@ -330,7 +444,7 @@ export class RpcPeer {
330
444
  this.pendingResults = Object.freeze({});
331
445
  this.params = Object.freeze({});
332
446
  this.remoteWeakProxies = Object.freeze({});
333
- this.localProxyMap = Object.freeze({});
447
+ this.localProxyMap.clear()
334
448
  this.localProxied.clear();
335
449
  }
336
450
 
@@ -381,10 +495,16 @@ export class RpcPeer {
381
495
  return value;
382
496
  }
383
497
 
384
- createErrorResult(result: RpcResult, e: any) {
385
- result.stack = e.stack || 'no stack';
386
- 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;
387
506
  result.message = (e as Error).message || 'no message';
507
+ result.stack = e.stack || 'no stack';
388
508
  }
389
509
 
390
510
  deserialize(value: any, deserializationContext: any): any {
@@ -401,6 +521,9 @@ export class RpcPeer {
401
521
  }
402
522
 
403
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
+
404
527
  if (__remote_proxy_id) {
405
528
  let proxy = this.remoteWeakProxies[__remote_proxy_id]?.deref();
406
529
  if (!proxy)
@@ -416,7 +539,7 @@ export class RpcPeer {
416
539
  }
417
540
 
418
541
  if (__local_proxy_id) {
419
- const ret = this.localProxyMap[__local_proxy_id];
542
+ const ret = this.localProxyMap.get(__local_proxy_id);
420
543
  if (!ret)
421
544
  throw new RPCResultError(this, `invalid local proxy id ${__local_proxy_id}`);
422
545
  return ret;
@@ -430,6 +553,28 @@ export class RpcPeer {
430
553
  return value;
431
554
  }
432
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
+
433
578
  serialize(value: any, serializationContext: any): any {
434
579
  if (value?.[RpcPeer.PROPERTY_JSON_COPY_SERIALIZE_CHILDREN] === true) {
435
580
  const ret: any = {};
@@ -439,12 +584,33 @@ export class RpcPeer {
439
584
  return ret;
440
585
  }
441
586
 
442
- if (!value || (!value[RpcPeer.PROPERTY_JSON_DISABLE_SERIALIZATION] && this.transportSafeArgumentTypes.has(value.constructor?.name))) {
587
+ if (this.isTransportSafe(value)) {
443
588
  return value;
444
589
  }
445
590
 
446
591
  let __remote_constructor_name = value.__proxy_constructor || value.constructor?.name?.toString();
447
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
+ }
613
+
448
614
  let proxiedEntry = this.localProxied.get(value);
449
615
  if (proxiedEntry) {
450
616
  const __remote_proxy_finalizer_id = (this.proxyCounter++).toString();
@@ -453,7 +619,7 @@ export class RpcPeer {
453
619
  __remote_proxy_id: proxiedEntry.id,
454
620
  __remote_proxy_finalizer_id,
455
621
  __remote_constructor_name,
456
- __remote_proxy_props: value?.[RpcPeer.PROPERTY_PROXY_PROPERTIES],
622
+ __remote_proxy_props: RpcPeer.prepareProxyProperties(value),
457
623
  __remote_proxy_oneway_methods: value?.[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS],
458
624
  }
459
625
  return ret;
@@ -467,23 +633,7 @@ export class RpcPeer {
467
633
  return ret;
468
634
  }
469
635
 
470
- const serializerMapName = this.constructorSerializerMap.get(value.constructor);
471
- if (serializerMapName) {
472
- __remote_constructor_name = serializerMapName;
473
- const serializer = this.nameDeserializerMap.get(serializerMapName);
474
- if (!serializer)
475
- throw new Error('serializer not found for ' + serializerMapName);
476
- const serialized = serializer.serialize(value, serializationContext);
477
- const ret: RpcRemoteProxyValue = {
478
- __remote_proxy_id: undefined,
479
- __remote_proxy_finalizer_id: undefined,
480
- __remote_constructor_name,
481
- __remote_proxy_props: value?.[RpcPeer.PROPERTY_PROXY_PROPERTIES],
482
- __remote_proxy_oneway_methods: value?.[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS],
483
- __serialized_value: serialized,
484
- }
485
- return ret;
486
- }
636
+ this.onProxyTypeSerialization.get(__remote_constructor_name)?.(value);
487
637
 
488
638
  const __remote_proxy_id = (this.proxyCounter++).toString();
489
639
  proxiedEntry = {
@@ -491,13 +641,15 @@ export class RpcPeer {
491
641
  finalizerId: __remote_proxy_id,
492
642
  };
493
643
  this.localProxied.set(value, proxiedEntry);
494
- 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);
495
647
 
496
648
  const ret: RpcRemoteProxyValue = {
497
649
  __remote_proxy_id,
498
650
  __remote_proxy_finalizer_id: __remote_proxy_id,
499
651
  __remote_constructor_name,
500
- __remote_proxy_props: value?.[RpcPeer.PROPERTY_PROXY_PROPERTIES],
652
+ __remote_proxy_props,
501
653
  __remote_proxy_oneway_methods: value?.[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS],
502
654
  }
503
655
 
@@ -540,11 +692,19 @@ export class RpcPeer {
540
692
  case 'param': {
541
693
  const rpcParam = message as RpcParam;
542
694
  const serializationContext: any = {};
543
- const result: RpcResult = {
544
- type: 'result',
545
- id: rpcParam.id,
546
- result: this.serialize(this.params[rpcParam.param], serializationContext)
547
- };
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
+
548
708
  this.send(result, undefined, serializationContext);
549
709
  break;
550
710
  }
@@ -557,7 +717,7 @@ export class RpcPeer {
557
717
  const serializationContext: any = {};
558
718
 
559
719
  try {
560
- const target = this.localProxyMap[rpcApply.proxyId];
720
+ const target = this.localProxyMap.get(rpcApply.proxyId);
561
721
  if (!target)
562
722
  throw new Error(`proxy id ${rpcApply.proxyId} not found`);
563
723
 
@@ -572,6 +732,19 @@ export class RpcPeer {
572
732
  if (!method)
573
733
  throw new Error(`target ${target?.constructor?.name} does not have method ${rpcApply.method}`);
574
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
+ }
575
748
  }
576
749
  else {
577
750
  value = await target(...args);
@@ -589,12 +762,13 @@ export class RpcPeer {
589
762
  break;
590
763
  }
591
764
  case 'result': {
765
+ // console.log(message)
592
766
  const rpcResult = message as RpcResult;
593
767
  const deferred = this.pendingResults[rpcResult.id];
594
768
  delete this.pendingResults[rpcResult.id];
595
769
  if (!deferred)
596
770
  throw new Error(`unknown result ${rpcResult.id}`);
597
- if (rpcResult.message || rpcResult.stack) {
771
+ if ((rpcResult.message || rpcResult.stack) && !rpcResult.throw) {
598
772
  const e = new RPCResultError(this, rpcResult.message || 'no message', undefined, {
599
773
  name: rpcResult.result,
600
774
  stack: rpcResult.stack,
@@ -602,19 +776,23 @@ export class RpcPeer {
602
776
  deferred.reject(e);
603
777
  return;
604
778
  }
605
- 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);
606
784
  break;
607
785
  }
608
786
  case 'finalize': {
609
787
  const rpcFinalize = message as RpcFinalize;
610
- const local = this.localProxyMap[rpcFinalize.__local_proxy_id];
788
+ const local = this.localProxyMap.get(rpcFinalize.__local_proxy_id);
611
789
  if (local) {
612
790
  const localProxiedEntry = this.localProxied.get(local);
613
791
  // if a finalizer id is specified, it must match.
614
792
  if (rpcFinalize.__local_proxy_finalizer_id && rpcFinalize.__local_proxy_finalizer_id !== localProxiedEntry?.finalizerId) {
615
793
  break;
616
794
  }
617
- delete this.localProxyMap[rpcFinalize.__local_proxy_id];
795
+ this.localProxyMap.delete(rpcFinalize.__local_proxy_id);
618
796
  this.localProxied.delete(local);
619
797
  }
620
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();