@actuallab/rpc 12.1.62 → 12.1.69
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.
- package/dist/index.cjs +501 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +180 -31
- package/dist/index.d.ts +180 -31
- package/dist/index.js +484 -50
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -31,14 +31,20 @@ __export(index_exports, {
|
|
|
31
31
|
RpcInboundCall: () => RpcInboundCall,
|
|
32
32
|
RpcInboundCallTracker: () => RpcInboundCallTracker,
|
|
33
33
|
RpcMessageChannelConnection: () => RpcMessageChannelConnection,
|
|
34
|
+
RpcObjectKind: () => RpcObjectKind,
|
|
34
35
|
RpcOutboundCall: () => RpcOutboundCall,
|
|
35
36
|
RpcOutboundCallTracker: () => RpcOutboundCallTracker,
|
|
36
37
|
RpcPeer: () => RpcPeer,
|
|
37
38
|
RpcPeerConnectionKind: () => RpcPeerConnectionKind,
|
|
38
39
|
RpcPeerStateKind: () => RpcPeerStateKind,
|
|
39
40
|
RpcPeerStateMonitor: () => RpcPeerStateMonitor,
|
|
41
|
+
RpcRemoteObjectTracker: () => RpcRemoteObjectTracker,
|
|
40
42
|
RpcServerPeer: () => RpcServerPeer,
|
|
41
43
|
RpcServiceHost: () => RpcServiceHost,
|
|
44
|
+
RpcSharedObjectTracker: () => RpcSharedObjectTracker,
|
|
45
|
+
RpcStream: () => RpcStream,
|
|
46
|
+
RpcStreamSender: () => RpcStreamSender,
|
|
47
|
+
RpcSystemCallHandler: () => RpcSystemCallHandler,
|
|
42
48
|
RpcSystemCallSender: () => RpcSystemCallSender,
|
|
43
49
|
RpcSystemCalls: () => RpcSystemCalls,
|
|
44
50
|
RpcType: () => RpcType,
|
|
@@ -52,9 +58,10 @@ __export(index_exports, {
|
|
|
52
58
|
getMethodsMeta: () => getMethodsMeta,
|
|
53
59
|
getServiceMeta: () => getServiceMeta,
|
|
54
60
|
getStateDescription: () => getStateDescription,
|
|
55
|
-
handleSystemCall: () => handleSystemCall,
|
|
56
61
|
isConnected: () => isConnected,
|
|
57
62
|
likelyConnected: () => likelyConnected,
|
|
63
|
+
parseStreamRef: () => parseStreamRef,
|
|
64
|
+
resolveStreamRefs: () => resolveStreamRefs,
|
|
58
65
|
rpcMethod: () => rpcMethod,
|
|
59
66
|
rpcService: () => rpcService,
|
|
60
67
|
serializeFrame: () => serializeFrame,
|
|
@@ -76,7 +83,12 @@ var RpcSystemCalls = {
|
|
|
76
83
|
error: "$sys.Error:1",
|
|
77
84
|
cancel: "$sys.Cancel:0",
|
|
78
85
|
keepAlive: "$sys.KeepAlive:1",
|
|
79
|
-
reconnect: "$sys.Reconnect:3"
|
|
86
|
+
reconnect: "$sys.Reconnect:3",
|
|
87
|
+
ack: "$sys.Ack:2",
|
|
88
|
+
ackEnd: "$sys.AckEnd:1",
|
|
89
|
+
item: "$sys.I:2",
|
|
90
|
+
batch: "$sys.B:2",
|
|
91
|
+
end: "$sys.End:2"
|
|
80
92
|
};
|
|
81
93
|
var ENVELOPE_DELIMITER = "\n";
|
|
82
94
|
var ARG_DELIMITER = "";
|
|
@@ -236,18 +248,18 @@ function createMessageChannelPair() {
|
|
|
236
248
|
var RpcType = {
|
|
237
249
|
object: /* @__PURE__ */ Symbol("object"),
|
|
238
250
|
stream: /* @__PURE__ */ Symbol("stream"),
|
|
251
|
+
noWait: /* @__PURE__ */ Symbol("noWait"),
|
|
239
252
|
void: /* @__PURE__ */ Symbol("void")
|
|
240
253
|
};
|
|
241
254
|
function wireMethodName(def) {
|
|
242
255
|
return `${def.serviceName}.${def.name}:${def.wireArgCount}`;
|
|
243
256
|
}
|
|
244
|
-
function defineRpcService(name, methods
|
|
245
|
-
const ctOffset = options?.ctOffset ?? 0;
|
|
257
|
+
function defineRpcService(name, methods) {
|
|
246
258
|
const map = /* @__PURE__ */ new Map();
|
|
247
259
|
for (const [key, input] of Object.entries(methods)) {
|
|
248
260
|
const colonIndex = key.indexOf(":");
|
|
249
261
|
const cleanName = colonIndex >= 0 ? key.substring(0, colonIndex) : key;
|
|
250
|
-
const wireArgCount = input.args.length +
|
|
262
|
+
const wireArgCount = input.wireArgCount ?? input.args.length + 1;
|
|
251
263
|
const mapKey = `${cleanName}:${wireArgCount}`;
|
|
252
264
|
map.set(mapKey, {
|
|
253
265
|
name: cleanName,
|
|
@@ -255,8 +267,8 @@ function defineRpcService(name, methods, options) {
|
|
|
255
267
|
argCount: input.args.length,
|
|
256
268
|
wireArgCount,
|
|
257
269
|
callTypeId: input.callTypeId ?? 0,
|
|
258
|
-
stream: input.
|
|
259
|
-
noWait: input.
|
|
270
|
+
stream: input.returns === RpcType.stream,
|
|
271
|
+
noWait: input.returns === RpcType.noWait
|
|
260
272
|
});
|
|
261
273
|
}
|
|
262
274
|
return { name, methods: map };
|
|
@@ -361,40 +373,263 @@ var RpcInboundCallTracker = class {
|
|
|
361
373
|
}
|
|
362
374
|
};
|
|
363
375
|
|
|
364
|
-
// src/rpc-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
376
|
+
// src/rpc-stream.ts
|
|
377
|
+
var import_core4 = require("@actuallab/core");
|
|
378
|
+
|
|
379
|
+
// src/rpc-object.ts
|
|
380
|
+
var RpcObjectKind = { Local: 0, Remote: 1 };
|
|
381
|
+
|
|
382
|
+
// src/rpc-stream.ts
|
|
383
|
+
function parseStreamRef(value) {
|
|
384
|
+
if (typeof value !== "string") return null;
|
|
385
|
+
const parts = value.split(",");
|
|
386
|
+
if (parts.length !== 4) return null;
|
|
387
|
+
const hostId = parts[0];
|
|
388
|
+
const localId = parseInt(parts[1], 10);
|
|
389
|
+
const ackPeriod = parseInt(parts[2], 10);
|
|
390
|
+
const ackAdvance = parseInt(parts[3], 10);
|
|
391
|
+
if (isNaN(localId) || isNaN(ackPeriod) || isNaN(ackAdvance)) return null;
|
|
392
|
+
return { hostId, localId, ackPeriod, ackAdvance };
|
|
393
|
+
}
|
|
394
|
+
var RpcStream = class {
|
|
395
|
+
id;
|
|
396
|
+
kind = RpcObjectKind.Remote;
|
|
397
|
+
peer;
|
|
398
|
+
ackPeriod;
|
|
399
|
+
ackAdvance;
|
|
400
|
+
_buffer = [];
|
|
401
|
+
_nextExpectedIndex = 0;
|
|
402
|
+
_consumerWaiting = null;
|
|
403
|
+
_completed = false;
|
|
404
|
+
_completionError = null;
|
|
405
|
+
_disposed = false;
|
|
406
|
+
_started = false;
|
|
407
|
+
_iterating = false;
|
|
408
|
+
_ackSentUpTo = 0;
|
|
409
|
+
constructor(ref, peer) {
|
|
410
|
+
this.id = { hostId: ref.hostId, localId: ref.localId };
|
|
411
|
+
this.peer = peer;
|
|
412
|
+
this.ackPeriod = ref.ackPeriod;
|
|
413
|
+
this.ackAdvance = ref.ackAdvance;
|
|
414
|
+
}
|
|
415
|
+
/** Called by system call handler when a single item arrives ($sys.I). */
|
|
416
|
+
onItem(index, item) {
|
|
417
|
+
if (this._disposed || this._completed) return;
|
|
418
|
+
if (index > this._nextExpectedIndex) {
|
|
419
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (index < this._nextExpectedIndex) {
|
|
423
|
+
this._maybeSendAck(index + 1);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
this._buffer.push(item);
|
|
427
|
+
this._nextExpectedIndex = index + 1;
|
|
428
|
+
this._maybeSendAck(this._nextExpectedIndex);
|
|
429
|
+
this._notifyConsumer();
|
|
430
|
+
}
|
|
431
|
+
/** Called by system call handler when a batch arrives ($sys.B). */
|
|
432
|
+
onBatch(index, items) {
|
|
433
|
+
if (this._disposed || this._completed) return;
|
|
434
|
+
if (index > this._nextExpectedIndex) {
|
|
435
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
if (index < this._nextExpectedIndex) {
|
|
439
|
+
this._maybeSendAck(index + items.length);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
for (const item of items) this._buffer.push(item);
|
|
443
|
+
this._nextExpectedIndex = index + items.length;
|
|
444
|
+
this._maybeSendAck(this._nextExpectedIndex);
|
|
445
|
+
this._notifyConsumer();
|
|
446
|
+
}
|
|
447
|
+
/** Called by system call handler when the stream ends ($sys.End). */
|
|
448
|
+
onEnd(index, error) {
|
|
449
|
+
if (this._disposed || this._completed) return;
|
|
450
|
+
this._completed = true;
|
|
451
|
+
this._completionError = error;
|
|
452
|
+
this._notifyConsumer();
|
|
453
|
+
}
|
|
454
|
+
// -- IRpcObject --
|
|
455
|
+
reconnect() {
|
|
456
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
457
|
+
}
|
|
458
|
+
disconnect() {
|
|
459
|
+
if (this._completed) return;
|
|
460
|
+
this._completed = true;
|
|
461
|
+
this._completionError = new Error("Peer disconnected.");
|
|
462
|
+
this._notifyConsumer();
|
|
463
|
+
}
|
|
464
|
+
// -- AsyncIterable --
|
|
465
|
+
[Symbol.asyncIterator]() {
|
|
466
|
+
if (this._iterating) throw new Error("RpcStream can only be iterated once.");
|
|
467
|
+
this._iterating = true;
|
|
468
|
+
let bufferIndex = 0;
|
|
469
|
+
let consumedIndex = 0;
|
|
470
|
+
const self = this;
|
|
471
|
+
return {
|
|
472
|
+
async next() {
|
|
473
|
+
if (!self._started) {
|
|
474
|
+
self._started = true;
|
|
475
|
+
self._sendAck(0, true);
|
|
476
|
+
}
|
|
477
|
+
while (true) {
|
|
478
|
+
if (bufferIndex < self._buffer.length) {
|
|
479
|
+
const value = self._buffer[bufferIndex];
|
|
480
|
+
bufferIndex++;
|
|
481
|
+
consumedIndex++;
|
|
482
|
+
return { value, done: false };
|
|
483
|
+
}
|
|
484
|
+
if (self._completed) {
|
|
485
|
+
if (self._completionError) throw self._completionError;
|
|
486
|
+
return { value: void 0, done: true };
|
|
487
|
+
}
|
|
488
|
+
self._consumerWaiting = new import_core4.PromiseSource();
|
|
489
|
+
await self._consumerWaiting.promise;
|
|
374
490
|
}
|
|
375
|
-
|
|
491
|
+
},
|
|
492
|
+
async return() {
|
|
493
|
+
self._sendAckEnd();
|
|
494
|
+
self.dispose();
|
|
495
|
+
return { value: void 0, done: true };
|
|
376
496
|
}
|
|
377
|
-
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
dispose() {
|
|
500
|
+
if (this._disposed) return;
|
|
501
|
+
this._disposed = true;
|
|
502
|
+
this._completed = true;
|
|
503
|
+
this.peer.remoteObjects.unregister(this);
|
|
504
|
+
this._notifyConsumer();
|
|
505
|
+
}
|
|
506
|
+
// -- Private --
|
|
507
|
+
_notifyConsumer() {
|
|
508
|
+
if (this._consumerWaiting !== null) {
|
|
509
|
+
this._consumerWaiting.resolve();
|
|
510
|
+
this._consumerWaiting = null;
|
|
378
511
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
386
|
-
break;
|
|
512
|
+
}
|
|
513
|
+
_maybeSendAck(nextIndex) {
|
|
514
|
+
if (this.ackPeriod <= 0) return;
|
|
515
|
+
const threshold = this._ackSentUpTo + this.ackPeriod;
|
|
516
|
+
if (nextIndex >= threshold) {
|
|
517
|
+
this._sendAck(nextIndex, false);
|
|
387
518
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
519
|
+
}
|
|
520
|
+
_sendAck(nextIndex, _mustReset) {
|
|
521
|
+
this._ackSentUpTo = nextIndex;
|
|
522
|
+
const conn = this.peer.connection;
|
|
523
|
+
if (conn) {
|
|
524
|
+
this.peer.hub.systemCallSender.ack(conn, this.id.localId, nextIndex, this.id.hostId);
|
|
391
525
|
}
|
|
392
|
-
|
|
393
|
-
|
|
526
|
+
}
|
|
527
|
+
_sendAckEnd() {
|
|
528
|
+
const conn = this.peer.connection;
|
|
529
|
+
if (conn) {
|
|
530
|
+
this.peer.hub.systemCallSender.ackEnd(conn, this.id.localId, this.id.hostId);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
function resolveStreamRefs(value, peer) {
|
|
535
|
+
if (value === null || value === void 0) return value;
|
|
536
|
+
if (typeof value === "string") {
|
|
537
|
+
const ref = parseStreamRef(value);
|
|
538
|
+
if (ref !== null) {
|
|
539
|
+
const stream = new RpcStream(ref, peer);
|
|
540
|
+
peer.remoteObjects.register(stream);
|
|
541
|
+
return stream;
|
|
394
542
|
}
|
|
543
|
+
return value;
|
|
395
544
|
}
|
|
545
|
+
if (Array.isArray(value)) {
|
|
546
|
+
for (let i = 0; i < value.length; i++) {
|
|
547
|
+
value[i] = resolveStreamRefs(value[i], peer);
|
|
548
|
+
}
|
|
549
|
+
return value;
|
|
550
|
+
}
|
|
551
|
+
if (typeof value === "object") {
|
|
552
|
+
for (const key of Object.keys(value)) {
|
|
553
|
+
value[key] = resolveStreamRefs(
|
|
554
|
+
value[key],
|
|
555
|
+
peer
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
return value;
|
|
559
|
+
}
|
|
560
|
+
return value;
|
|
396
561
|
}
|
|
397
562
|
|
|
563
|
+
// src/rpc-system-call-handler.ts
|
|
564
|
+
var RpcSystemCallHandler = class {
|
|
565
|
+
handle(message, args, peer) {
|
|
566
|
+
const method = message.Method;
|
|
567
|
+
const relatedId = message.RelatedId ?? 0;
|
|
568
|
+
switch (method) {
|
|
569
|
+
case RpcSystemCalls.ok: {
|
|
570
|
+
const call = peer.outbound.get(relatedId);
|
|
571
|
+
if (call !== void 0) {
|
|
572
|
+
if (call.removeOnOk) {
|
|
573
|
+
peer.outbound.remove(relatedId);
|
|
574
|
+
}
|
|
575
|
+
call.result.resolve(args[0]);
|
|
576
|
+
}
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
case RpcSystemCalls.error: {
|
|
580
|
+
const call = peer.outbound.remove(relatedId);
|
|
581
|
+
if (call !== void 0) {
|
|
582
|
+
const errorInfo = args[0];
|
|
583
|
+
const msg = errorInfo?.Message ?? errorInfo?.message ?? "RPC error";
|
|
584
|
+
call.result.reject(new Error(msg));
|
|
585
|
+
}
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
case RpcSystemCalls.cancel: {
|
|
589
|
+
peer.inbound.remove(relatedId);
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
case RpcSystemCalls.keepAlive: {
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
case RpcSystemCalls.item: {
|
|
596
|
+
const stream = peer.remoteObjects.get(relatedId);
|
|
597
|
+
if (stream) stream.onItem(args[0], resolveStreamRefs(args[1], peer));
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
case RpcSystemCalls.batch: {
|
|
601
|
+
const stream = peer.remoteObjects.get(relatedId);
|
|
602
|
+
if (stream) {
|
|
603
|
+
const items = args[1];
|
|
604
|
+
for (let i = 0; i < items.length; i++) items[i] = resolveStreamRefs(items[i], peer);
|
|
605
|
+
stream.onBatch(args[0], items);
|
|
606
|
+
}
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
609
|
+
case RpcSystemCalls.end: {
|
|
610
|
+
const stream = peer.remoteObjects.get(relatedId);
|
|
611
|
+
if (stream) {
|
|
612
|
+
const errorInfo = args[1];
|
|
613
|
+
const msg = errorInfo?.Message ?? errorInfo?.message;
|
|
614
|
+
const error = msg ? new Error(msg) : null;
|
|
615
|
+
stream.onEnd(args[0], error);
|
|
616
|
+
}
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
case RpcSystemCalls.ack: {
|
|
620
|
+
const sender = peer.sharedObjects.get(relatedId);
|
|
621
|
+
sender?.onAck(args[0], args[1]);
|
|
622
|
+
break;
|
|
623
|
+
}
|
|
624
|
+
case RpcSystemCalls.ackEnd: {
|
|
625
|
+
const sender = peer.sharedObjects.get(relatedId);
|
|
626
|
+
sender?.onAckEnd(args[0]);
|
|
627
|
+
break;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
|
|
398
633
|
// src/rpc-system-call-sender.ts
|
|
399
634
|
var RpcSystemCallSender = class {
|
|
400
635
|
handshake(conn, peerId, hubId, index) {
|
|
@@ -427,17 +662,193 @@ var RpcSystemCallSender = class {
|
|
|
427
662
|
const msg = serializeMessage({ Method: RpcSystemCalls.keepAlive }, [activeCallIds]);
|
|
428
663
|
conn.send(msg);
|
|
429
664
|
}
|
|
665
|
+
item(conn, localId, index, item) {
|
|
666
|
+
const msg = serializeMessage(
|
|
667
|
+
{ Method: RpcSystemCalls.item, RelatedId: localId },
|
|
668
|
+
[index, item]
|
|
669
|
+
);
|
|
670
|
+
conn.send(msg);
|
|
671
|
+
}
|
|
672
|
+
batch(conn, localId, index, items) {
|
|
673
|
+
const msg = serializeMessage(
|
|
674
|
+
{ Method: RpcSystemCalls.batch, RelatedId: localId },
|
|
675
|
+
[index, items]
|
|
676
|
+
);
|
|
677
|
+
conn.send(msg);
|
|
678
|
+
}
|
|
679
|
+
end(conn, localId, index, error = null) {
|
|
680
|
+
const msg = serializeMessage(
|
|
681
|
+
{ Method: RpcSystemCalls.end, RelatedId: localId },
|
|
682
|
+
[index, error]
|
|
683
|
+
);
|
|
684
|
+
conn.send(msg);
|
|
685
|
+
}
|
|
686
|
+
ack(conn, localId, nextIndex, hostId) {
|
|
687
|
+
const msg = serializeMessage(
|
|
688
|
+
{ Method: RpcSystemCalls.ack, RelatedId: localId },
|
|
689
|
+
[nextIndex, hostId]
|
|
690
|
+
);
|
|
691
|
+
conn.send(msg);
|
|
692
|
+
}
|
|
693
|
+
ackEnd(conn, localId, hostId) {
|
|
694
|
+
const msg = serializeMessage(
|
|
695
|
+
{ Method: RpcSystemCalls.ackEnd, RelatedId: localId },
|
|
696
|
+
[hostId]
|
|
697
|
+
);
|
|
698
|
+
conn.send(msg);
|
|
699
|
+
}
|
|
430
700
|
};
|
|
431
701
|
|
|
432
|
-
// src/rpc-
|
|
702
|
+
// src/rpc-stream-sender.ts
|
|
433
703
|
var import_core5 = require("@actuallab/core");
|
|
704
|
+
var DEFAULT_ACK_PERIOD = 256;
|
|
705
|
+
var DEFAULT_ACK_ADVANCE = 128;
|
|
706
|
+
var RpcStreamSender = class {
|
|
707
|
+
id;
|
|
708
|
+
kind = RpcObjectKind.Local;
|
|
709
|
+
peer;
|
|
710
|
+
ackPeriod;
|
|
711
|
+
ackAdvance;
|
|
712
|
+
_nextIndex = 0;
|
|
713
|
+
_ended = false;
|
|
714
|
+
_started = new import_core5.PromiseSource();
|
|
715
|
+
constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, ackAdvance = DEFAULT_ACK_ADVANCE) {
|
|
716
|
+
const localId = peer.sharedObjects.nextId();
|
|
717
|
+
this.id = { hostId: peer.hub.hubId, localId };
|
|
718
|
+
this.peer = peer;
|
|
719
|
+
this.ackPeriod = ackPeriod;
|
|
720
|
+
this.ackAdvance = ackAdvance;
|
|
721
|
+
}
|
|
722
|
+
/** Returns the stream reference string for the $sys.Ok response. */
|
|
723
|
+
toRef() {
|
|
724
|
+
return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance}`;
|
|
725
|
+
}
|
|
726
|
+
/** Called by system call handler when $sys.Ack is received from the client. */
|
|
727
|
+
onAck(_nextIndex, _hostId) {
|
|
728
|
+
if (!this._started.isCompleted) {
|
|
729
|
+
this._started.resolve();
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
/** Called by system call handler when $sys.AckEnd is received from the client. */
|
|
733
|
+
onAckEnd(_hostId) {
|
|
734
|
+
this._ended = true;
|
|
735
|
+
this.peer.sharedObjects.unregister(this);
|
|
736
|
+
}
|
|
737
|
+
/** Send a single item to the client. */
|
|
738
|
+
sendItem(item) {
|
|
739
|
+
if (this._ended) return;
|
|
740
|
+
const conn = this.peer.connection;
|
|
741
|
+
if (!conn) return;
|
|
742
|
+
this.peer.hub.systemCallSender.item(conn, this.id.localId, this._nextIndex, item);
|
|
743
|
+
this._nextIndex++;
|
|
744
|
+
}
|
|
745
|
+
/** Send a batch of items to the client. */
|
|
746
|
+
sendBatch(items) {
|
|
747
|
+
if (this._ended || items.length === 0) return;
|
|
748
|
+
const conn = this.peer.connection;
|
|
749
|
+
if (!conn) return;
|
|
750
|
+
this.peer.hub.systemCallSender.batch(conn, this.id.localId, this._nextIndex, items);
|
|
751
|
+
this._nextIndex += items.length;
|
|
752
|
+
}
|
|
753
|
+
/** Signal stream completion to the client. */
|
|
754
|
+
sendEnd(error) {
|
|
755
|
+
if (this._ended) return;
|
|
756
|
+
this._ended = true;
|
|
757
|
+
const conn = this.peer.connection;
|
|
758
|
+
if (!conn) return;
|
|
759
|
+
const errorInfo = error ? { Message: error.message } : null;
|
|
760
|
+
this.peer.hub.systemCallSender.end(conn, this.id.localId, this._nextIndex, errorInfo);
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Consume an AsyncIterable and send all items to the client.
|
|
764
|
+
* Waits for the client's initial Ack before starting to pump items.
|
|
765
|
+
*/
|
|
766
|
+
async writeFrom(source) {
|
|
767
|
+
await this._started.promise;
|
|
768
|
+
try {
|
|
769
|
+
for await (const item of source) {
|
|
770
|
+
if (this._ended) return;
|
|
771
|
+
this.sendItem(item);
|
|
772
|
+
}
|
|
773
|
+
if (!this._ended) {
|
|
774
|
+
this.sendEnd();
|
|
775
|
+
}
|
|
776
|
+
} catch (e) {
|
|
777
|
+
if (!this._ended) {
|
|
778
|
+
this.sendEnd(e instanceof Error ? e : new Error(String(e)));
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
// -- IRpcObject --
|
|
783
|
+
reconnect() {
|
|
784
|
+
}
|
|
785
|
+
disconnect() {
|
|
786
|
+
this._ended = true;
|
|
787
|
+
if (!this._started.isCompleted) {
|
|
788
|
+
this._started.resolve();
|
|
789
|
+
}
|
|
790
|
+
this.peer.sharedObjects.unregister(this);
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
// src/rpc-remote-object-tracker.ts
|
|
795
|
+
var RpcRemoteObjectTracker = class {
|
|
796
|
+
_objects = /* @__PURE__ */ new Map();
|
|
797
|
+
register(obj) {
|
|
798
|
+
this._objects.set(obj.id.localId, obj);
|
|
799
|
+
}
|
|
800
|
+
get(localId) {
|
|
801
|
+
return this._objects.get(localId);
|
|
802
|
+
}
|
|
803
|
+
unregister(obj) {
|
|
804
|
+
this._objects.delete(obj.id.localId);
|
|
805
|
+
}
|
|
806
|
+
disconnectAll() {
|
|
807
|
+
for (const obj of this._objects.values()) {
|
|
808
|
+
obj.disconnect();
|
|
809
|
+
}
|
|
810
|
+
this._objects.clear();
|
|
811
|
+
}
|
|
812
|
+
reconnectAll() {
|
|
813
|
+
for (const obj of this._objects.values()) {
|
|
814
|
+
obj.reconnect();
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
// src/rpc-shared-object-tracker.ts
|
|
820
|
+
var RpcSharedObjectTracker = class {
|
|
821
|
+
_nextId = 1;
|
|
822
|
+
_objects = /* @__PURE__ */ new Map();
|
|
823
|
+
nextId() {
|
|
824
|
+
return this._nextId++;
|
|
825
|
+
}
|
|
826
|
+
register(obj) {
|
|
827
|
+
this._objects.set(obj.id.localId, obj);
|
|
828
|
+
}
|
|
829
|
+
get(localId) {
|
|
830
|
+
return this._objects.get(localId);
|
|
831
|
+
}
|
|
832
|
+
unregister(obj) {
|
|
833
|
+
this._objects.delete(obj.id.localId);
|
|
834
|
+
}
|
|
835
|
+
disconnectAll() {
|
|
836
|
+
for (const obj of this._objects.values()) {
|
|
837
|
+
obj.disconnect();
|
|
838
|
+
}
|
|
839
|
+
this._objects.clear();
|
|
840
|
+
}
|
|
841
|
+
};
|
|
842
|
+
|
|
843
|
+
// src/rpc-peer.ts
|
|
844
|
+
var import_core7 = require("@actuallab/core");
|
|
434
845
|
|
|
435
846
|
// src/rpc-client-peer-reconnect-delayer.ts
|
|
436
|
-
var
|
|
437
|
-
var RpcClientPeerReconnectDelayer = class extends
|
|
847
|
+
var import_core6 = require("@actuallab/core");
|
|
848
|
+
var RpcClientPeerReconnectDelayer = class extends import_core6.RetryDelayer {
|
|
438
849
|
constructor() {
|
|
439
850
|
super();
|
|
440
|
-
this.delays =
|
|
851
|
+
this.delays = import_core6.RetryDelaySeq.exp(1e3, 6e4);
|
|
441
852
|
}
|
|
442
853
|
};
|
|
443
854
|
|
|
@@ -456,14 +867,19 @@ var RpcPeer = class {
|
|
|
456
867
|
_connection;
|
|
457
868
|
outbound = new RpcOutboundCallTracker();
|
|
458
869
|
inbound = new RpcInboundCallTracker();
|
|
459
|
-
|
|
460
|
-
|
|
870
|
+
remoteObjects = new RpcRemoteObjectTracker();
|
|
871
|
+
sharedObjects = new RpcSharedObjectTracker();
|
|
872
|
+
connected = new import_core7.EventHandlerSet();
|
|
873
|
+
disconnected = new import_core7.EventHandlerSet();
|
|
461
874
|
_pendingSends = [];
|
|
462
875
|
_keepAliveTimer;
|
|
463
876
|
constructor(ref, hub) {
|
|
464
877
|
this.ref = ref;
|
|
465
878
|
this._hub = hub;
|
|
466
879
|
}
|
|
880
|
+
get hub() {
|
|
881
|
+
return this._hub;
|
|
882
|
+
}
|
|
467
883
|
get connection() {
|
|
468
884
|
return this._connection;
|
|
469
885
|
}
|
|
@@ -529,6 +945,8 @@ var RpcPeer = class {
|
|
|
529
945
|
}
|
|
530
946
|
close() {
|
|
531
947
|
this._stopKeepAlive();
|
|
948
|
+
this.remoteObjects.disconnectAll();
|
|
949
|
+
this.sharedObjects.disconnectAll();
|
|
532
950
|
for (const call of this._pendingSends) {
|
|
533
951
|
if (!call.result.isCompleted)
|
|
534
952
|
call.result.reject(new Error("Peer closed."));
|
|
@@ -562,7 +980,7 @@ var RpcPeer = class {
|
|
|
562
980
|
return;
|
|
563
981
|
}
|
|
564
982
|
if (method.startsWith("$sys")) {
|
|
565
|
-
this._hub.
|
|
983
|
+
this._hub.systemCallHandler.handle(message, args, this);
|
|
566
984
|
return;
|
|
567
985
|
}
|
|
568
986
|
this._handleInbound(message, args);
|
|
@@ -582,7 +1000,12 @@ var RpcPeer = class {
|
|
|
582
1000
|
try {
|
|
583
1001
|
const context = this._connection !== void 0 ? { __rpcDispatch: true, callId: relatedId, connection: this._connection } : void 0;
|
|
584
1002
|
const result = await serviceHost.dispatch(method, args, context);
|
|
585
|
-
if (
|
|
1003
|
+
if (methodDef?.stream === true && this._connection !== void 0) {
|
|
1004
|
+
const sender = new RpcStreamSender(this);
|
|
1005
|
+
this.sharedObjects.register(sender);
|
|
1006
|
+
this._hub.systemCallSender.ok(this._connection, relatedId, sender.toRef());
|
|
1007
|
+
void sender.writeFrom(result);
|
|
1008
|
+
} else if (!isNoWait && this._connection !== void 0) {
|
|
586
1009
|
this._hub.systemCallSender.ok(this._connection, relatedId, result);
|
|
587
1010
|
}
|
|
588
1011
|
} catch (e) {
|
|
@@ -635,9 +1058,9 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
635
1058
|
serializationFormat;
|
|
636
1059
|
/** Builds the WebSocket URL for this peer. Replace to customize URL construction. */
|
|
637
1060
|
connectionUrlResolver = defaultConnectionUrlResolver;
|
|
638
|
-
peerChanged = new
|
|
1061
|
+
peerChanged = new import_core7.EventHandlerSet();
|
|
639
1062
|
reconnectDelayer = new RpcClientPeerReconnectDelayer();
|
|
640
|
-
reconnectsAtChanged = new
|
|
1063
|
+
reconnectsAtChanged = new import_core7.EventHandlerSet();
|
|
641
1064
|
get reconnectsAt() {
|
|
642
1065
|
return this._reconnectsAt;
|
|
643
1066
|
}
|
|
@@ -667,7 +1090,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
667
1090
|
const ws = wsFactory?.(connUrl) ?? new WebSocket(connUrl);
|
|
668
1091
|
const conn = new RpcWebSocketConnection(ws);
|
|
669
1092
|
this._connectionKind = 1 /* Connecting */;
|
|
670
|
-
this._pendingHandshake = new
|
|
1093
|
+
this._pendingHandshake = new import_core7.PromiseSource();
|
|
671
1094
|
this.setupConnection(conn);
|
|
672
1095
|
this._connection = void 0;
|
|
673
1096
|
const closedRejection = new Promise((_, reject) => conn.closed.add(() => reject(new Error("Connection failed"))));
|
|
@@ -723,6 +1146,11 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
723
1146
|
_reconnect(_isPeerChanged) {
|
|
724
1147
|
if (this._connection === void 0) return;
|
|
725
1148
|
const conn = this._connection;
|
|
1149
|
+
if (_isPeerChanged) {
|
|
1150
|
+
this.remoteObjects.disconnectAll();
|
|
1151
|
+
} else {
|
|
1152
|
+
this.remoteObjects.reconnectAll();
|
|
1153
|
+
}
|
|
726
1154
|
const trackerCalls = [...this.outbound.values()];
|
|
727
1155
|
for (const call of trackerCalls) {
|
|
728
1156
|
if (!call.removeOnOk && call.result.isCompleted) {
|
|
@@ -805,12 +1233,10 @@ function getMethodsMeta(cls) {
|
|
|
805
1233
|
function getServiceMeta(cls) {
|
|
806
1234
|
return cls[Symbol.metadata]?.[SERVICE_META];
|
|
807
1235
|
}
|
|
808
|
-
function rpcService(serviceName
|
|
1236
|
+
function rpcService(serviceName) {
|
|
809
1237
|
return function(_target, context) {
|
|
810
1238
|
const meta = context.metadata[SERVICE_META] ??= {};
|
|
811
1239
|
meta.name = serviceName;
|
|
812
|
-
if (options?.ctOffset !== void 0)
|
|
813
|
-
meta.ctOffset = options.ctOffset;
|
|
814
1240
|
};
|
|
815
1241
|
}
|
|
816
1242
|
function rpcMethod(options) {
|
|
@@ -820,8 +1246,7 @@ function rpcMethod(options) {
|
|
|
820
1246
|
methods[methodName] = {
|
|
821
1247
|
...methods[methodName],
|
|
822
1248
|
argCount: target.length,
|
|
823
|
-
|
|
824
|
-
noWait: options?.noWait ?? false
|
|
1249
|
+
returns: options?.returns
|
|
825
1250
|
};
|
|
826
1251
|
return target;
|
|
827
1252
|
};
|
|
@@ -833,6 +1258,7 @@ var RpcHub = class {
|
|
|
833
1258
|
peers = /* @__PURE__ */ new Map();
|
|
834
1259
|
serviceHost;
|
|
835
1260
|
systemCallSender = new RpcSystemCallSender();
|
|
1261
|
+
systemCallHandler = new RpcSystemCallHandler();
|
|
836
1262
|
constructor(hubId) {
|
|
837
1263
|
this.hubId = hubId ?? crypto.randomUUID();
|
|
838
1264
|
this.serviceHost = new RpcServiceHost();
|
|
@@ -906,19 +1332,14 @@ var RpcHub = class {
|
|
|
906
1332
|
for (const peer of this.peers.values()) peer.close();
|
|
907
1333
|
this.peers.clear();
|
|
908
1334
|
}
|
|
909
|
-
/** Dispatch an inbound system call. Override in FusionHub to handle compute-specific system calls. */
|
|
910
|
-
handleSystemCall(message, args, outbound, inbound) {
|
|
911
|
-
handleSystemCall(message, args, outbound, inbound);
|
|
912
|
-
}
|
|
913
1335
|
/** Build an RpcServiceDef from decorator metadata on a contract class. Override in FusionHub for compute support. */
|
|
914
1336
|
_buildServiceDef(cls) {
|
|
915
1337
|
const svcMeta = getServiceMeta(cls);
|
|
916
1338
|
if (svcMeta === void 0) throw new Error("Contract class missing @rpcService metadata");
|
|
917
1339
|
const methodsMeta = getMethodsMeta(cls) ?? {};
|
|
918
|
-
const ctOffset = svcMeta.ctOffset ?? 0;
|
|
919
1340
|
const methods = /* @__PURE__ */ new Map();
|
|
920
1341
|
for (const [name, meta] of Object.entries(methodsMeta)) {
|
|
921
|
-
const wireArgCount = meta.argCount +
|
|
1342
|
+
const wireArgCount = meta.argCount + 1;
|
|
922
1343
|
const mapKey = `${name}:${wireArgCount}`;
|
|
923
1344
|
methods.set(mapKey, {
|
|
924
1345
|
name,
|
|
@@ -926,8 +1347,8 @@ var RpcHub = class {
|
|
|
926
1347
|
argCount: meta.argCount,
|
|
927
1348
|
wireArgCount,
|
|
928
1349
|
callTypeId: 0,
|
|
929
|
-
stream: meta.
|
|
930
|
-
noWait: meta.
|
|
1350
|
+
stream: meta.returns === RpcType.stream,
|
|
1351
|
+
noWait: meta.returns === RpcType.noWait
|
|
931
1352
|
});
|
|
932
1353
|
}
|
|
933
1354
|
return { name: svcMeta.name, methods };
|
|
@@ -951,11 +1372,24 @@ var RpcHub = class {
|
|
|
951
1372
|
peer.callNoWait(wireName, callArgs);
|
|
952
1373
|
};
|
|
953
1374
|
}
|
|
1375
|
+
if (methodDef.stream) {
|
|
1376
|
+
return async (...args) => {
|
|
1377
|
+
const callArgs = args.slice(0, methodDef.argCount);
|
|
1378
|
+
const outboundCall = peer.call(wireName, callArgs);
|
|
1379
|
+
const result = await outboundCall.result.promise;
|
|
1380
|
+
const ref = parseStreamRef(result);
|
|
1381
|
+
if (!ref) throw new Error(`Expected stream reference, got: ${JSON.stringify(result)}`);
|
|
1382
|
+
const stream = new RpcStream(ref, peer);
|
|
1383
|
+
peer.remoteObjects.register(stream);
|
|
1384
|
+
return stream;
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
954
1387
|
const callOptions = methodDef.callTypeId !== 0 ? { callTypeId: methodDef.callTypeId } : void 0;
|
|
955
1388
|
return async (...args) => {
|
|
956
1389
|
const callArgs = args.slice(0, methodDef.argCount);
|
|
957
1390
|
const outboundCall = peer.call(wireName, callArgs, callOptions);
|
|
958
|
-
|
|
1391
|
+
const result = await outboundCall.result.promise;
|
|
1392
|
+
return resolveStreamRefs(result, peer);
|
|
959
1393
|
};
|
|
960
1394
|
}
|
|
961
1395
|
};
|
|
@@ -1041,13 +1475,13 @@ function getStateDescription(state, useLastError = false) {
|
|
|
1041
1475
|
}
|
|
1042
1476
|
|
|
1043
1477
|
// src/rpc-peer-state-monitor.ts
|
|
1044
|
-
var
|
|
1478
|
+
var import_core8 = require("@actuallab/core");
|
|
1045
1479
|
var JustConnectedPeriodMs = 1500;
|
|
1046
1480
|
var JustDisconnectedPeriodMs = 3e3;
|
|
1047
1481
|
var MinReconnectsInMs = 1e3;
|
|
1048
1482
|
var RpcPeerStateMonitor = class {
|
|
1049
1483
|
peer;
|
|
1050
|
-
stateChanged = new
|
|
1484
|
+
stateChanged = new import_core8.EventHandlerSet();
|
|
1051
1485
|
_rawConnected;
|
|
1052
1486
|
_connectedAt = 0;
|
|
1053
1487
|
_disconnectedAt = 0;
|
|
@@ -1166,14 +1600,20 @@ var RpcPeerStateMonitor = class {
|
|
|
1166
1600
|
RpcInboundCall,
|
|
1167
1601
|
RpcInboundCallTracker,
|
|
1168
1602
|
RpcMessageChannelConnection,
|
|
1603
|
+
RpcObjectKind,
|
|
1169
1604
|
RpcOutboundCall,
|
|
1170
1605
|
RpcOutboundCallTracker,
|
|
1171
1606
|
RpcPeer,
|
|
1172
1607
|
RpcPeerConnectionKind,
|
|
1173
1608
|
RpcPeerStateKind,
|
|
1174
1609
|
RpcPeerStateMonitor,
|
|
1610
|
+
RpcRemoteObjectTracker,
|
|
1175
1611
|
RpcServerPeer,
|
|
1176
1612
|
RpcServiceHost,
|
|
1613
|
+
RpcSharedObjectTracker,
|
|
1614
|
+
RpcStream,
|
|
1615
|
+
RpcStreamSender,
|
|
1616
|
+
RpcSystemCallHandler,
|
|
1177
1617
|
RpcSystemCallSender,
|
|
1178
1618
|
RpcSystemCalls,
|
|
1179
1619
|
RpcType,
|
|
@@ -1187,9 +1627,10 @@ var RpcPeerStateMonitor = class {
|
|
|
1187
1627
|
getMethodsMeta,
|
|
1188
1628
|
getServiceMeta,
|
|
1189
1629
|
getStateDescription,
|
|
1190
|
-
handleSystemCall,
|
|
1191
1630
|
isConnected,
|
|
1192
1631
|
likelyConnected,
|
|
1632
|
+
parseStreamRef,
|
|
1633
|
+
resolveStreamRefs,
|
|
1193
1634
|
rpcMethod,
|
|
1194
1635
|
rpcService,
|
|
1195
1636
|
serializeFrame,
|