@actuallab/rpc 12.1.62 → 12.1.100
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 +513 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +184 -31
- package/dist/index.d.ts +184 -31
- package/dist/index.js +496 -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,270 @@ 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 || parts.length > 5) 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
|
+
const allowReconnect = parts.length < 5 || parts[4] !== "0";
|
|
393
|
+
return { hostId, localId, ackPeriod, ackAdvance, allowReconnect };
|
|
394
|
+
}
|
|
395
|
+
var RpcStream = class {
|
|
396
|
+
id;
|
|
397
|
+
kind = RpcObjectKind.Remote;
|
|
398
|
+
allowReconnect;
|
|
399
|
+
peer;
|
|
400
|
+
ackPeriod;
|
|
401
|
+
ackAdvance;
|
|
402
|
+
_buffer = [];
|
|
403
|
+
_nextExpectedIndex = 0;
|
|
404
|
+
_consumerWaiting = null;
|
|
405
|
+
_completed = false;
|
|
406
|
+
_completionError = null;
|
|
407
|
+
_disposed = false;
|
|
408
|
+
_started = false;
|
|
409
|
+
_iterating = false;
|
|
410
|
+
_ackSentUpTo = 0;
|
|
411
|
+
constructor(ref, peer) {
|
|
412
|
+
this.id = { hostId: ref.hostId, localId: ref.localId };
|
|
413
|
+
this.allowReconnect = ref.allowReconnect;
|
|
414
|
+
this.peer = peer;
|
|
415
|
+
this.ackPeriod = ref.ackPeriod;
|
|
416
|
+
this.ackAdvance = ref.ackAdvance;
|
|
417
|
+
}
|
|
418
|
+
/** Called by system call handler when a single item arrives ($sys.I). */
|
|
419
|
+
onItem(index, item) {
|
|
420
|
+
if (this._disposed || this._completed) return;
|
|
421
|
+
if (index > this._nextExpectedIndex) {
|
|
422
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
if (index < this._nextExpectedIndex) {
|
|
426
|
+
this._maybeSendAck(index + 1);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
this._buffer.push(item);
|
|
430
|
+
this._nextExpectedIndex = index + 1;
|
|
431
|
+
this._maybeSendAck(this._nextExpectedIndex);
|
|
432
|
+
this._notifyConsumer();
|
|
433
|
+
}
|
|
434
|
+
/** Called by system call handler when a batch arrives ($sys.B). */
|
|
435
|
+
onBatch(index, items) {
|
|
436
|
+
if (this._disposed || this._completed) return;
|
|
437
|
+
if (index > this._nextExpectedIndex) {
|
|
438
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if (index < this._nextExpectedIndex) {
|
|
442
|
+
this._maybeSendAck(index + items.length);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
for (const item of items) this._buffer.push(item);
|
|
446
|
+
this._nextExpectedIndex = index + items.length;
|
|
447
|
+
this._maybeSendAck(this._nextExpectedIndex);
|
|
448
|
+
this._notifyConsumer();
|
|
449
|
+
}
|
|
450
|
+
/** Called by system call handler when the stream ends ($sys.End). */
|
|
451
|
+
onEnd(index, error) {
|
|
452
|
+
if (this._disposed || this._completed) return;
|
|
453
|
+
this._completed = true;
|
|
454
|
+
this._completionError = error;
|
|
455
|
+
this._notifyConsumer();
|
|
456
|
+
}
|
|
457
|
+
// -- IRpcObject --
|
|
458
|
+
reconnect() {
|
|
459
|
+
if (this.allowReconnect) {
|
|
460
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
461
|
+
} else {
|
|
462
|
+
this.disconnect();
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
disconnect() {
|
|
466
|
+
if (this._completed) return;
|
|
467
|
+
this._completed = true;
|
|
468
|
+
this._completionError = new Error("Peer disconnected.");
|
|
469
|
+
this._notifyConsumer();
|
|
470
|
+
}
|
|
471
|
+
// -- AsyncIterable --
|
|
472
|
+
[Symbol.asyncIterator]() {
|
|
473
|
+
if (this._iterating) throw new Error("RpcStream can only be iterated once.");
|
|
474
|
+
this._iterating = true;
|
|
475
|
+
let bufferIndex = 0;
|
|
476
|
+
let consumedIndex = 0;
|
|
477
|
+
const self = this;
|
|
478
|
+
return {
|
|
479
|
+
async next() {
|
|
480
|
+
if (!self._started) {
|
|
481
|
+
self._started = true;
|
|
482
|
+
self._sendAck(0, true);
|
|
374
483
|
}
|
|
375
|
-
|
|
484
|
+
while (true) {
|
|
485
|
+
if (bufferIndex < self._buffer.length) {
|
|
486
|
+
const value = self._buffer[bufferIndex];
|
|
487
|
+
bufferIndex++;
|
|
488
|
+
consumedIndex++;
|
|
489
|
+
return { value, done: false };
|
|
490
|
+
}
|
|
491
|
+
if (self._completed) {
|
|
492
|
+
if (self._completionError) throw self._completionError;
|
|
493
|
+
return { value: void 0, done: true };
|
|
494
|
+
}
|
|
495
|
+
self._consumerWaiting = new import_core4.PromiseSource();
|
|
496
|
+
await self._consumerWaiting.promise;
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
async return() {
|
|
500
|
+
self._sendAckEnd();
|
|
501
|
+
self.dispose();
|
|
502
|
+
return { value: void 0, done: true };
|
|
376
503
|
}
|
|
377
|
-
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
dispose() {
|
|
507
|
+
if (this._disposed) return;
|
|
508
|
+
this._disposed = true;
|
|
509
|
+
this._completed = true;
|
|
510
|
+
this.peer.remoteObjects.unregister(this);
|
|
511
|
+
this._notifyConsumer();
|
|
512
|
+
}
|
|
513
|
+
// -- Private --
|
|
514
|
+
_notifyConsumer() {
|
|
515
|
+
if (this._consumerWaiting !== null) {
|
|
516
|
+
this._consumerWaiting.resolve();
|
|
517
|
+
this._consumerWaiting = null;
|
|
378
518
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
519
|
+
}
|
|
520
|
+
_maybeSendAck(nextIndex) {
|
|
521
|
+
if (this.ackPeriod <= 0) return;
|
|
522
|
+
const threshold = this._ackSentUpTo + this.ackPeriod;
|
|
523
|
+
if (nextIndex >= threshold) {
|
|
524
|
+
this._sendAck(nextIndex, false);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
_sendAck(nextIndex, _mustReset) {
|
|
528
|
+
this._ackSentUpTo = nextIndex;
|
|
529
|
+
const conn = this.peer.connection;
|
|
530
|
+
if (conn) {
|
|
531
|
+
this.peer.hub.systemCallSender.ack(conn, this.id.localId, nextIndex, this.id.hostId);
|
|
387
532
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
533
|
+
}
|
|
534
|
+
_sendAckEnd() {
|
|
535
|
+
const conn = this.peer.connection;
|
|
536
|
+
if (conn) {
|
|
537
|
+
this.peer.hub.systemCallSender.ackEnd(conn, this.id.localId, this.id.hostId);
|
|
391
538
|
}
|
|
392
|
-
|
|
393
|
-
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
function resolveStreamRefs(value, peer) {
|
|
542
|
+
if (value === null || value === void 0) return value;
|
|
543
|
+
if (typeof value === "string") {
|
|
544
|
+
const ref = parseStreamRef(value);
|
|
545
|
+
if (ref !== null) {
|
|
546
|
+
const stream = new RpcStream(ref, peer);
|
|
547
|
+
peer.remoteObjects.register(stream);
|
|
548
|
+
return stream;
|
|
394
549
|
}
|
|
550
|
+
return value;
|
|
395
551
|
}
|
|
552
|
+
if (Array.isArray(value)) {
|
|
553
|
+
for (let i = 0; i < value.length; i++) {
|
|
554
|
+
value[i] = resolveStreamRefs(value[i], peer);
|
|
555
|
+
}
|
|
556
|
+
return value;
|
|
557
|
+
}
|
|
558
|
+
if (typeof value === "object") {
|
|
559
|
+
for (const key of Object.keys(value)) {
|
|
560
|
+
value[key] = resolveStreamRefs(
|
|
561
|
+
value[key],
|
|
562
|
+
peer
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
return value;
|
|
566
|
+
}
|
|
567
|
+
return value;
|
|
396
568
|
}
|
|
397
569
|
|
|
570
|
+
// src/rpc-system-call-handler.ts
|
|
571
|
+
var RpcSystemCallHandler = class {
|
|
572
|
+
handle(message, args, peer) {
|
|
573
|
+
const method = message.Method;
|
|
574
|
+
const relatedId = message.RelatedId ?? 0;
|
|
575
|
+
switch (method) {
|
|
576
|
+
case RpcSystemCalls.ok: {
|
|
577
|
+
const call = peer.outbound.get(relatedId);
|
|
578
|
+
if (call !== void 0) {
|
|
579
|
+
if (call.removeOnOk) {
|
|
580
|
+
peer.outbound.remove(relatedId);
|
|
581
|
+
}
|
|
582
|
+
call.result.resolve(args[0]);
|
|
583
|
+
}
|
|
584
|
+
break;
|
|
585
|
+
}
|
|
586
|
+
case RpcSystemCalls.error: {
|
|
587
|
+
const call = peer.outbound.remove(relatedId);
|
|
588
|
+
if (call !== void 0) {
|
|
589
|
+
const errorInfo = args[0];
|
|
590
|
+
const msg = errorInfo?.Message ?? errorInfo?.message ?? "RPC error";
|
|
591
|
+
call.result.reject(new Error(msg));
|
|
592
|
+
}
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
case RpcSystemCalls.cancel: {
|
|
596
|
+
peer.inbound.remove(relatedId);
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
case RpcSystemCalls.keepAlive: {
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
case RpcSystemCalls.item: {
|
|
603
|
+
const stream = peer.remoteObjects.get(relatedId);
|
|
604
|
+
if (stream) stream.onItem(args[0], resolveStreamRefs(args[1], peer));
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
607
|
+
case RpcSystemCalls.batch: {
|
|
608
|
+
const stream = peer.remoteObjects.get(relatedId);
|
|
609
|
+
if (stream) {
|
|
610
|
+
const items = args[1];
|
|
611
|
+
for (let i = 0; i < items.length; i++) items[i] = resolveStreamRefs(items[i], peer);
|
|
612
|
+
stream.onBatch(args[0], items);
|
|
613
|
+
}
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
case RpcSystemCalls.end: {
|
|
617
|
+
const stream = peer.remoteObjects.get(relatedId);
|
|
618
|
+
if (stream) {
|
|
619
|
+
const errorInfo = args[1];
|
|
620
|
+
const msg = errorInfo?.Message ?? errorInfo?.message;
|
|
621
|
+
const error = msg ? new Error(msg) : null;
|
|
622
|
+
stream.onEnd(args[0], error);
|
|
623
|
+
}
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
case RpcSystemCalls.ack: {
|
|
627
|
+
const sender = peer.sharedObjects.get(relatedId);
|
|
628
|
+
sender?.onAck(args[0], args[1]);
|
|
629
|
+
break;
|
|
630
|
+
}
|
|
631
|
+
case RpcSystemCalls.ackEnd: {
|
|
632
|
+
const sender = peer.sharedObjects.get(relatedId);
|
|
633
|
+
sender?.onAckEnd(args[0]);
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
|
|
398
640
|
// src/rpc-system-call-sender.ts
|
|
399
641
|
var RpcSystemCallSender = class {
|
|
400
642
|
handshake(conn, peerId, hubId, index) {
|
|
@@ -427,17 +669,198 @@ var RpcSystemCallSender = class {
|
|
|
427
669
|
const msg = serializeMessage({ Method: RpcSystemCalls.keepAlive }, [activeCallIds]);
|
|
428
670
|
conn.send(msg);
|
|
429
671
|
}
|
|
672
|
+
item(conn, localId, index, item) {
|
|
673
|
+
const msg = serializeMessage(
|
|
674
|
+
{ Method: RpcSystemCalls.item, RelatedId: localId },
|
|
675
|
+
[index, item]
|
|
676
|
+
);
|
|
677
|
+
conn.send(msg);
|
|
678
|
+
}
|
|
679
|
+
batch(conn, localId, index, items) {
|
|
680
|
+
const msg = serializeMessage(
|
|
681
|
+
{ Method: RpcSystemCalls.batch, RelatedId: localId },
|
|
682
|
+
[index, items]
|
|
683
|
+
);
|
|
684
|
+
conn.send(msg);
|
|
685
|
+
}
|
|
686
|
+
end(conn, localId, index, error = null) {
|
|
687
|
+
const msg = serializeMessage(
|
|
688
|
+
{ Method: RpcSystemCalls.end, RelatedId: localId },
|
|
689
|
+
[index, error]
|
|
690
|
+
);
|
|
691
|
+
conn.send(msg);
|
|
692
|
+
}
|
|
693
|
+
ack(conn, localId, nextIndex, hostId) {
|
|
694
|
+
const msg = serializeMessage(
|
|
695
|
+
{ Method: RpcSystemCalls.ack, RelatedId: localId },
|
|
696
|
+
[nextIndex, hostId]
|
|
697
|
+
);
|
|
698
|
+
conn.send(msg);
|
|
699
|
+
}
|
|
700
|
+
ackEnd(conn, localId, hostId) {
|
|
701
|
+
const msg = serializeMessage(
|
|
702
|
+
{ Method: RpcSystemCalls.ackEnd, RelatedId: localId },
|
|
703
|
+
[hostId]
|
|
704
|
+
);
|
|
705
|
+
conn.send(msg);
|
|
706
|
+
}
|
|
430
707
|
};
|
|
431
708
|
|
|
432
|
-
// src/rpc-
|
|
709
|
+
// src/rpc-stream-sender.ts
|
|
433
710
|
var import_core5 = require("@actuallab/core");
|
|
711
|
+
var DEFAULT_ACK_PERIOD = 256;
|
|
712
|
+
var DEFAULT_ACK_ADVANCE = 128;
|
|
713
|
+
var RpcStreamSender = class {
|
|
714
|
+
id;
|
|
715
|
+
kind = RpcObjectKind.Local;
|
|
716
|
+
allowReconnect;
|
|
717
|
+
peer;
|
|
718
|
+
ackPeriod;
|
|
719
|
+
ackAdvance;
|
|
720
|
+
_nextIndex = 0;
|
|
721
|
+
_ended = false;
|
|
722
|
+
_started = new import_core5.PromiseSource();
|
|
723
|
+
constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, ackAdvance = DEFAULT_ACK_ADVANCE, allowReconnect = true) {
|
|
724
|
+
const localId = peer.sharedObjects.nextId();
|
|
725
|
+
this.id = { hostId: peer.hub.hubId, localId };
|
|
726
|
+
this.allowReconnect = allowReconnect;
|
|
727
|
+
this.peer = peer;
|
|
728
|
+
this.ackPeriod = ackPeriod;
|
|
729
|
+
this.ackAdvance = ackAdvance;
|
|
730
|
+
}
|
|
731
|
+
/** Returns the stream reference string for the $sys.Ok response. */
|
|
732
|
+
toRef() {
|
|
733
|
+
return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance},${this.allowReconnect ? "1" : "0"}`;
|
|
734
|
+
}
|
|
735
|
+
/** Called by system call handler when $sys.Ack is received from the client. */
|
|
736
|
+
onAck(_nextIndex, _hostId) {
|
|
737
|
+
if (!this._started.isCompleted) {
|
|
738
|
+
this._started.resolve();
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
/** Called by system call handler when $sys.AckEnd is received from the client. */
|
|
742
|
+
onAckEnd(_hostId) {
|
|
743
|
+
this._ended = true;
|
|
744
|
+
this.peer.sharedObjects.unregister(this);
|
|
745
|
+
}
|
|
746
|
+
/** Send a single item to the client. */
|
|
747
|
+
sendItem(item) {
|
|
748
|
+
if (this._ended) return;
|
|
749
|
+
const conn = this.peer.connection;
|
|
750
|
+
if (!conn) return;
|
|
751
|
+
this.peer.hub.systemCallSender.item(conn, this.id.localId, this._nextIndex, item);
|
|
752
|
+
this._nextIndex++;
|
|
753
|
+
}
|
|
754
|
+
/** Send a batch of items to the client. */
|
|
755
|
+
sendBatch(items) {
|
|
756
|
+
if (this._ended || items.length === 0) return;
|
|
757
|
+
const conn = this.peer.connection;
|
|
758
|
+
if (!conn) return;
|
|
759
|
+
this.peer.hub.systemCallSender.batch(conn, this.id.localId, this._nextIndex, items);
|
|
760
|
+
this._nextIndex += items.length;
|
|
761
|
+
}
|
|
762
|
+
/** Signal stream completion to the client. */
|
|
763
|
+
sendEnd(error) {
|
|
764
|
+
if (this._ended) return;
|
|
765
|
+
this._ended = true;
|
|
766
|
+
const conn = this.peer.connection;
|
|
767
|
+
if (!conn) return;
|
|
768
|
+
const errorInfo = error ? { Message: error.message } : null;
|
|
769
|
+
this.peer.hub.systemCallSender.end(conn, this.id.localId, this._nextIndex, errorInfo);
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Consume an AsyncIterable and send all items to the client.
|
|
773
|
+
* Waits for the client's initial Ack before starting to pump items.
|
|
774
|
+
*/
|
|
775
|
+
async writeFrom(source) {
|
|
776
|
+
await this._started.promise;
|
|
777
|
+
try {
|
|
778
|
+
for await (const item of source) {
|
|
779
|
+
if (this._ended) return;
|
|
780
|
+
this.sendItem(item);
|
|
781
|
+
}
|
|
782
|
+
if (!this._ended) {
|
|
783
|
+
this.sendEnd();
|
|
784
|
+
}
|
|
785
|
+
} catch (e) {
|
|
786
|
+
if (!this._ended) {
|
|
787
|
+
this.sendEnd(e instanceof Error ? e : new Error(String(e)));
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
// -- IRpcObject --
|
|
792
|
+
reconnect() {
|
|
793
|
+
}
|
|
794
|
+
disconnect() {
|
|
795
|
+
this._ended = true;
|
|
796
|
+
if (!this._started.isCompleted) {
|
|
797
|
+
this._started.resolve();
|
|
798
|
+
}
|
|
799
|
+
this.peer.sharedObjects.unregister(this);
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
// src/rpc-remote-object-tracker.ts
|
|
804
|
+
var RpcRemoteObjectTracker = class {
|
|
805
|
+
_objects = /* @__PURE__ */ new Map();
|
|
806
|
+
register(obj) {
|
|
807
|
+
this._objects.set(obj.id.localId, obj);
|
|
808
|
+
}
|
|
809
|
+
get(localId) {
|
|
810
|
+
return this._objects.get(localId);
|
|
811
|
+
}
|
|
812
|
+
unregister(obj) {
|
|
813
|
+
this._objects.delete(obj.id.localId);
|
|
814
|
+
}
|
|
815
|
+
disconnectAll() {
|
|
816
|
+
for (const obj of this._objects.values()) {
|
|
817
|
+
obj.disconnect();
|
|
818
|
+
}
|
|
819
|
+
this._objects.clear();
|
|
820
|
+
}
|
|
821
|
+
reconnectAll() {
|
|
822
|
+
for (const obj of this._objects.values()) {
|
|
823
|
+
if (obj.allowReconnect)
|
|
824
|
+
obj.reconnect();
|
|
825
|
+
else
|
|
826
|
+
obj.disconnect();
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
// src/rpc-shared-object-tracker.ts
|
|
832
|
+
var RpcSharedObjectTracker = class {
|
|
833
|
+
_nextId = 1;
|
|
834
|
+
_objects = /* @__PURE__ */ new Map();
|
|
835
|
+
nextId() {
|
|
836
|
+
return this._nextId++;
|
|
837
|
+
}
|
|
838
|
+
register(obj) {
|
|
839
|
+
this._objects.set(obj.id.localId, obj);
|
|
840
|
+
}
|
|
841
|
+
get(localId) {
|
|
842
|
+
return this._objects.get(localId);
|
|
843
|
+
}
|
|
844
|
+
unregister(obj) {
|
|
845
|
+
this._objects.delete(obj.id.localId);
|
|
846
|
+
}
|
|
847
|
+
disconnectAll() {
|
|
848
|
+
for (const obj of this._objects.values()) {
|
|
849
|
+
obj.disconnect();
|
|
850
|
+
}
|
|
851
|
+
this._objects.clear();
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
// src/rpc-peer.ts
|
|
856
|
+
var import_core7 = require("@actuallab/core");
|
|
434
857
|
|
|
435
858
|
// src/rpc-client-peer-reconnect-delayer.ts
|
|
436
|
-
var
|
|
437
|
-
var RpcClientPeerReconnectDelayer = class extends
|
|
859
|
+
var import_core6 = require("@actuallab/core");
|
|
860
|
+
var RpcClientPeerReconnectDelayer = class extends import_core6.RetryDelayer {
|
|
438
861
|
constructor() {
|
|
439
862
|
super();
|
|
440
|
-
this.delays =
|
|
863
|
+
this.delays = import_core6.RetryDelaySeq.exp(1e3, 6e4);
|
|
441
864
|
}
|
|
442
865
|
};
|
|
443
866
|
|
|
@@ -456,14 +879,19 @@ var RpcPeer = class {
|
|
|
456
879
|
_connection;
|
|
457
880
|
outbound = new RpcOutboundCallTracker();
|
|
458
881
|
inbound = new RpcInboundCallTracker();
|
|
459
|
-
|
|
460
|
-
|
|
882
|
+
remoteObjects = new RpcRemoteObjectTracker();
|
|
883
|
+
sharedObjects = new RpcSharedObjectTracker();
|
|
884
|
+
connected = new import_core7.EventHandlerSet();
|
|
885
|
+
disconnected = new import_core7.EventHandlerSet();
|
|
461
886
|
_pendingSends = [];
|
|
462
887
|
_keepAliveTimer;
|
|
463
888
|
constructor(ref, hub) {
|
|
464
889
|
this.ref = ref;
|
|
465
890
|
this._hub = hub;
|
|
466
891
|
}
|
|
892
|
+
get hub() {
|
|
893
|
+
return this._hub;
|
|
894
|
+
}
|
|
467
895
|
get connection() {
|
|
468
896
|
return this._connection;
|
|
469
897
|
}
|
|
@@ -529,6 +957,8 @@ var RpcPeer = class {
|
|
|
529
957
|
}
|
|
530
958
|
close() {
|
|
531
959
|
this._stopKeepAlive();
|
|
960
|
+
this.remoteObjects.disconnectAll();
|
|
961
|
+
this.sharedObjects.disconnectAll();
|
|
532
962
|
for (const call of this._pendingSends) {
|
|
533
963
|
if (!call.result.isCompleted)
|
|
534
964
|
call.result.reject(new Error("Peer closed."));
|
|
@@ -562,7 +992,7 @@ var RpcPeer = class {
|
|
|
562
992
|
return;
|
|
563
993
|
}
|
|
564
994
|
if (method.startsWith("$sys")) {
|
|
565
|
-
this._hub.
|
|
995
|
+
this._hub.systemCallHandler.handle(message, args, this);
|
|
566
996
|
return;
|
|
567
997
|
}
|
|
568
998
|
this._handleInbound(message, args);
|
|
@@ -582,7 +1012,12 @@ var RpcPeer = class {
|
|
|
582
1012
|
try {
|
|
583
1013
|
const context = this._connection !== void 0 ? { __rpcDispatch: true, callId: relatedId, connection: this._connection } : void 0;
|
|
584
1014
|
const result = await serviceHost.dispatch(method, args, context);
|
|
585
|
-
if (
|
|
1015
|
+
if (methodDef?.stream === true && this._connection !== void 0) {
|
|
1016
|
+
const sender = new RpcStreamSender(this);
|
|
1017
|
+
this.sharedObjects.register(sender);
|
|
1018
|
+
this._hub.systemCallSender.ok(this._connection, relatedId, sender.toRef());
|
|
1019
|
+
void sender.writeFrom(result);
|
|
1020
|
+
} else if (!isNoWait && this._connection !== void 0) {
|
|
586
1021
|
this._hub.systemCallSender.ok(this._connection, relatedId, result);
|
|
587
1022
|
}
|
|
588
1023
|
} catch (e) {
|
|
@@ -635,9 +1070,9 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
635
1070
|
serializationFormat;
|
|
636
1071
|
/** Builds the WebSocket URL for this peer. Replace to customize URL construction. */
|
|
637
1072
|
connectionUrlResolver = defaultConnectionUrlResolver;
|
|
638
|
-
peerChanged = new
|
|
1073
|
+
peerChanged = new import_core7.EventHandlerSet();
|
|
639
1074
|
reconnectDelayer = new RpcClientPeerReconnectDelayer();
|
|
640
|
-
reconnectsAtChanged = new
|
|
1075
|
+
reconnectsAtChanged = new import_core7.EventHandlerSet();
|
|
641
1076
|
get reconnectsAt() {
|
|
642
1077
|
return this._reconnectsAt;
|
|
643
1078
|
}
|
|
@@ -667,7 +1102,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
667
1102
|
const ws = wsFactory?.(connUrl) ?? new WebSocket(connUrl);
|
|
668
1103
|
const conn = new RpcWebSocketConnection(ws);
|
|
669
1104
|
this._connectionKind = 1 /* Connecting */;
|
|
670
|
-
this._pendingHandshake = new
|
|
1105
|
+
this._pendingHandshake = new import_core7.PromiseSource();
|
|
671
1106
|
this.setupConnection(conn);
|
|
672
1107
|
this._connection = void 0;
|
|
673
1108
|
const closedRejection = new Promise((_, reject) => conn.closed.add(() => reject(new Error("Connection failed"))));
|
|
@@ -723,6 +1158,11 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
723
1158
|
_reconnect(_isPeerChanged) {
|
|
724
1159
|
if (this._connection === void 0) return;
|
|
725
1160
|
const conn = this._connection;
|
|
1161
|
+
if (_isPeerChanged) {
|
|
1162
|
+
this.remoteObjects.disconnectAll();
|
|
1163
|
+
} else {
|
|
1164
|
+
this.remoteObjects.reconnectAll();
|
|
1165
|
+
}
|
|
726
1166
|
const trackerCalls = [...this.outbound.values()];
|
|
727
1167
|
for (const call of trackerCalls) {
|
|
728
1168
|
if (!call.removeOnOk && call.result.isCompleted) {
|
|
@@ -805,12 +1245,10 @@ function getMethodsMeta(cls) {
|
|
|
805
1245
|
function getServiceMeta(cls) {
|
|
806
1246
|
return cls[Symbol.metadata]?.[SERVICE_META];
|
|
807
1247
|
}
|
|
808
|
-
function rpcService(serviceName
|
|
1248
|
+
function rpcService(serviceName) {
|
|
809
1249
|
return function(_target, context) {
|
|
810
1250
|
const meta = context.metadata[SERVICE_META] ??= {};
|
|
811
1251
|
meta.name = serviceName;
|
|
812
|
-
if (options?.ctOffset !== void 0)
|
|
813
|
-
meta.ctOffset = options.ctOffset;
|
|
814
1252
|
};
|
|
815
1253
|
}
|
|
816
1254
|
function rpcMethod(options) {
|
|
@@ -820,8 +1258,7 @@ function rpcMethod(options) {
|
|
|
820
1258
|
methods[methodName] = {
|
|
821
1259
|
...methods[methodName],
|
|
822
1260
|
argCount: target.length,
|
|
823
|
-
|
|
824
|
-
noWait: options?.noWait ?? false
|
|
1261
|
+
returns: options?.returns
|
|
825
1262
|
};
|
|
826
1263
|
return target;
|
|
827
1264
|
};
|
|
@@ -833,6 +1270,7 @@ var RpcHub = class {
|
|
|
833
1270
|
peers = /* @__PURE__ */ new Map();
|
|
834
1271
|
serviceHost;
|
|
835
1272
|
systemCallSender = new RpcSystemCallSender();
|
|
1273
|
+
systemCallHandler = new RpcSystemCallHandler();
|
|
836
1274
|
constructor(hubId) {
|
|
837
1275
|
this.hubId = hubId ?? crypto.randomUUID();
|
|
838
1276
|
this.serviceHost = new RpcServiceHost();
|
|
@@ -906,19 +1344,14 @@ var RpcHub = class {
|
|
|
906
1344
|
for (const peer of this.peers.values()) peer.close();
|
|
907
1345
|
this.peers.clear();
|
|
908
1346
|
}
|
|
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
1347
|
/** Build an RpcServiceDef from decorator metadata on a contract class. Override in FusionHub for compute support. */
|
|
914
1348
|
_buildServiceDef(cls) {
|
|
915
1349
|
const svcMeta = getServiceMeta(cls);
|
|
916
1350
|
if (svcMeta === void 0) throw new Error("Contract class missing @rpcService metadata");
|
|
917
1351
|
const methodsMeta = getMethodsMeta(cls) ?? {};
|
|
918
|
-
const ctOffset = svcMeta.ctOffset ?? 0;
|
|
919
1352
|
const methods = /* @__PURE__ */ new Map();
|
|
920
1353
|
for (const [name, meta] of Object.entries(methodsMeta)) {
|
|
921
|
-
const wireArgCount = meta.argCount +
|
|
1354
|
+
const wireArgCount = meta.argCount + 1;
|
|
922
1355
|
const mapKey = `${name}:${wireArgCount}`;
|
|
923
1356
|
methods.set(mapKey, {
|
|
924
1357
|
name,
|
|
@@ -926,8 +1359,8 @@ var RpcHub = class {
|
|
|
926
1359
|
argCount: meta.argCount,
|
|
927
1360
|
wireArgCount,
|
|
928
1361
|
callTypeId: 0,
|
|
929
|
-
stream: meta.
|
|
930
|
-
noWait: meta.
|
|
1362
|
+
stream: meta.returns === RpcType.stream,
|
|
1363
|
+
noWait: meta.returns === RpcType.noWait
|
|
931
1364
|
});
|
|
932
1365
|
}
|
|
933
1366
|
return { name: svcMeta.name, methods };
|
|
@@ -951,11 +1384,24 @@ var RpcHub = class {
|
|
|
951
1384
|
peer.callNoWait(wireName, callArgs);
|
|
952
1385
|
};
|
|
953
1386
|
}
|
|
1387
|
+
if (methodDef.stream) {
|
|
1388
|
+
return async (...args) => {
|
|
1389
|
+
const callArgs = args.slice(0, methodDef.argCount);
|
|
1390
|
+
const outboundCall = peer.call(wireName, callArgs);
|
|
1391
|
+
const result = await outboundCall.result.promise;
|
|
1392
|
+
const ref = parseStreamRef(result);
|
|
1393
|
+
if (!ref) throw new Error(`Expected stream reference, got: ${JSON.stringify(result)}`);
|
|
1394
|
+
const stream = new RpcStream(ref, peer);
|
|
1395
|
+
peer.remoteObjects.register(stream);
|
|
1396
|
+
return stream;
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
954
1399
|
const callOptions = methodDef.callTypeId !== 0 ? { callTypeId: methodDef.callTypeId } : void 0;
|
|
955
1400
|
return async (...args) => {
|
|
956
1401
|
const callArgs = args.slice(0, methodDef.argCount);
|
|
957
1402
|
const outboundCall = peer.call(wireName, callArgs, callOptions);
|
|
958
|
-
|
|
1403
|
+
const result = await outboundCall.result.promise;
|
|
1404
|
+
return resolveStreamRefs(result, peer);
|
|
959
1405
|
};
|
|
960
1406
|
}
|
|
961
1407
|
};
|
|
@@ -1041,13 +1487,13 @@ function getStateDescription(state, useLastError = false) {
|
|
|
1041
1487
|
}
|
|
1042
1488
|
|
|
1043
1489
|
// src/rpc-peer-state-monitor.ts
|
|
1044
|
-
var
|
|
1490
|
+
var import_core8 = require("@actuallab/core");
|
|
1045
1491
|
var JustConnectedPeriodMs = 1500;
|
|
1046
1492
|
var JustDisconnectedPeriodMs = 3e3;
|
|
1047
1493
|
var MinReconnectsInMs = 1e3;
|
|
1048
1494
|
var RpcPeerStateMonitor = class {
|
|
1049
1495
|
peer;
|
|
1050
|
-
stateChanged = new
|
|
1496
|
+
stateChanged = new import_core8.EventHandlerSet();
|
|
1051
1497
|
_rawConnected;
|
|
1052
1498
|
_connectedAt = 0;
|
|
1053
1499
|
_disconnectedAt = 0;
|
|
@@ -1166,14 +1612,20 @@ var RpcPeerStateMonitor = class {
|
|
|
1166
1612
|
RpcInboundCall,
|
|
1167
1613
|
RpcInboundCallTracker,
|
|
1168
1614
|
RpcMessageChannelConnection,
|
|
1615
|
+
RpcObjectKind,
|
|
1169
1616
|
RpcOutboundCall,
|
|
1170
1617
|
RpcOutboundCallTracker,
|
|
1171
1618
|
RpcPeer,
|
|
1172
1619
|
RpcPeerConnectionKind,
|
|
1173
1620
|
RpcPeerStateKind,
|
|
1174
1621
|
RpcPeerStateMonitor,
|
|
1622
|
+
RpcRemoteObjectTracker,
|
|
1175
1623
|
RpcServerPeer,
|
|
1176
1624
|
RpcServiceHost,
|
|
1625
|
+
RpcSharedObjectTracker,
|
|
1626
|
+
RpcStream,
|
|
1627
|
+
RpcStreamSender,
|
|
1628
|
+
RpcSystemCallHandler,
|
|
1177
1629
|
RpcSystemCallSender,
|
|
1178
1630
|
RpcSystemCalls,
|
|
1179
1631
|
RpcType,
|
|
@@ -1187,9 +1639,10 @@ var RpcPeerStateMonitor = class {
|
|
|
1187
1639
|
getMethodsMeta,
|
|
1188
1640
|
getServiceMeta,
|
|
1189
1641
|
getStateDescription,
|
|
1190
|
-
handleSystemCall,
|
|
1191
1642
|
isConnected,
|
|
1192
1643
|
likelyConnected,
|
|
1644
|
+
parseStreamRef,
|
|
1645
|
+
resolveStreamRefs,
|
|
1193
1646
|
rpcMethod,
|
|
1194
1647
|
rpcService,
|
|
1195
1648
|
serializeFrame,
|