@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.js
CHANGED
|
@@ -10,7 +10,12 @@ var RpcSystemCalls = {
|
|
|
10
10
|
error: "$sys.Error:1",
|
|
11
11
|
cancel: "$sys.Cancel:0",
|
|
12
12
|
keepAlive: "$sys.KeepAlive:1",
|
|
13
|
-
reconnect: "$sys.Reconnect:3"
|
|
13
|
+
reconnect: "$sys.Reconnect:3",
|
|
14
|
+
ack: "$sys.Ack:2",
|
|
15
|
+
ackEnd: "$sys.AckEnd:1",
|
|
16
|
+
item: "$sys.I:2",
|
|
17
|
+
batch: "$sys.B:2",
|
|
18
|
+
end: "$sys.End:2"
|
|
14
19
|
};
|
|
15
20
|
var ENVELOPE_DELIMITER = "\n";
|
|
16
21
|
var ARG_DELIMITER = "";
|
|
@@ -170,18 +175,18 @@ function createMessageChannelPair() {
|
|
|
170
175
|
var RpcType = {
|
|
171
176
|
object: /* @__PURE__ */ Symbol("object"),
|
|
172
177
|
stream: /* @__PURE__ */ Symbol("stream"),
|
|
178
|
+
noWait: /* @__PURE__ */ Symbol("noWait"),
|
|
173
179
|
void: /* @__PURE__ */ Symbol("void")
|
|
174
180
|
};
|
|
175
181
|
function wireMethodName(def) {
|
|
176
182
|
return `${def.serviceName}.${def.name}:${def.wireArgCount}`;
|
|
177
183
|
}
|
|
178
|
-
function defineRpcService(name, methods
|
|
179
|
-
const ctOffset = options?.ctOffset ?? 0;
|
|
184
|
+
function defineRpcService(name, methods) {
|
|
180
185
|
const map = /* @__PURE__ */ new Map();
|
|
181
186
|
for (const [key, input] of Object.entries(methods)) {
|
|
182
187
|
const colonIndex = key.indexOf(":");
|
|
183
188
|
const cleanName = colonIndex >= 0 ? key.substring(0, colonIndex) : key;
|
|
184
|
-
const wireArgCount = input.args.length +
|
|
189
|
+
const wireArgCount = input.wireArgCount ?? input.args.length + 1;
|
|
185
190
|
const mapKey = `${cleanName}:${wireArgCount}`;
|
|
186
191
|
map.set(mapKey, {
|
|
187
192
|
name: cleanName,
|
|
@@ -189,8 +194,8 @@ function defineRpcService(name, methods, options) {
|
|
|
189
194
|
argCount: input.args.length,
|
|
190
195
|
wireArgCount,
|
|
191
196
|
callTypeId: input.callTypeId ?? 0,
|
|
192
|
-
stream: input.
|
|
193
|
-
noWait: input.
|
|
197
|
+
stream: input.returns === RpcType.stream,
|
|
198
|
+
noWait: input.returns === RpcType.noWait
|
|
194
199
|
});
|
|
195
200
|
}
|
|
196
201
|
return { name, methods: map };
|
|
@@ -295,40 +300,270 @@ var RpcInboundCallTracker = class {
|
|
|
295
300
|
}
|
|
296
301
|
};
|
|
297
302
|
|
|
298
|
-
// src/rpc-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
303
|
+
// src/rpc-stream.ts
|
|
304
|
+
import { PromiseSource as PromiseSource3 } from "@actuallab/core";
|
|
305
|
+
|
|
306
|
+
// src/rpc-object.ts
|
|
307
|
+
var RpcObjectKind = { Local: 0, Remote: 1 };
|
|
308
|
+
|
|
309
|
+
// src/rpc-stream.ts
|
|
310
|
+
function parseStreamRef(value) {
|
|
311
|
+
if (typeof value !== "string") return null;
|
|
312
|
+
const parts = value.split(",");
|
|
313
|
+
if (parts.length < 4 || parts.length > 5) return null;
|
|
314
|
+
const hostId = parts[0];
|
|
315
|
+
const localId = parseInt(parts[1], 10);
|
|
316
|
+
const ackPeriod = parseInt(parts[2], 10);
|
|
317
|
+
const ackAdvance = parseInt(parts[3], 10);
|
|
318
|
+
if (isNaN(localId) || isNaN(ackPeriod) || isNaN(ackAdvance)) return null;
|
|
319
|
+
const allowReconnect = parts.length < 5 || parts[4] !== "0";
|
|
320
|
+
return { hostId, localId, ackPeriod, ackAdvance, allowReconnect };
|
|
321
|
+
}
|
|
322
|
+
var RpcStream = class {
|
|
323
|
+
id;
|
|
324
|
+
kind = RpcObjectKind.Remote;
|
|
325
|
+
allowReconnect;
|
|
326
|
+
peer;
|
|
327
|
+
ackPeriod;
|
|
328
|
+
ackAdvance;
|
|
329
|
+
_buffer = [];
|
|
330
|
+
_nextExpectedIndex = 0;
|
|
331
|
+
_consumerWaiting = null;
|
|
332
|
+
_completed = false;
|
|
333
|
+
_completionError = null;
|
|
334
|
+
_disposed = false;
|
|
335
|
+
_started = false;
|
|
336
|
+
_iterating = false;
|
|
337
|
+
_ackSentUpTo = 0;
|
|
338
|
+
constructor(ref, peer) {
|
|
339
|
+
this.id = { hostId: ref.hostId, localId: ref.localId };
|
|
340
|
+
this.allowReconnect = ref.allowReconnect;
|
|
341
|
+
this.peer = peer;
|
|
342
|
+
this.ackPeriod = ref.ackPeriod;
|
|
343
|
+
this.ackAdvance = ref.ackAdvance;
|
|
344
|
+
}
|
|
345
|
+
/** Called by system call handler when a single item arrives ($sys.I). */
|
|
346
|
+
onItem(index, item) {
|
|
347
|
+
if (this._disposed || this._completed) return;
|
|
348
|
+
if (index > this._nextExpectedIndex) {
|
|
349
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (index < this._nextExpectedIndex) {
|
|
353
|
+
this._maybeSendAck(index + 1);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
this._buffer.push(item);
|
|
357
|
+
this._nextExpectedIndex = index + 1;
|
|
358
|
+
this._maybeSendAck(this._nextExpectedIndex);
|
|
359
|
+
this._notifyConsumer();
|
|
360
|
+
}
|
|
361
|
+
/** Called by system call handler when a batch arrives ($sys.B). */
|
|
362
|
+
onBatch(index, items) {
|
|
363
|
+
if (this._disposed || this._completed) return;
|
|
364
|
+
if (index > this._nextExpectedIndex) {
|
|
365
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (index < this._nextExpectedIndex) {
|
|
369
|
+
this._maybeSendAck(index + items.length);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
for (const item of items) this._buffer.push(item);
|
|
373
|
+
this._nextExpectedIndex = index + items.length;
|
|
374
|
+
this._maybeSendAck(this._nextExpectedIndex);
|
|
375
|
+
this._notifyConsumer();
|
|
376
|
+
}
|
|
377
|
+
/** Called by system call handler when the stream ends ($sys.End). */
|
|
378
|
+
onEnd(index, error) {
|
|
379
|
+
if (this._disposed || this._completed) return;
|
|
380
|
+
this._completed = true;
|
|
381
|
+
this._completionError = error;
|
|
382
|
+
this._notifyConsumer();
|
|
383
|
+
}
|
|
384
|
+
// -- IRpcObject --
|
|
385
|
+
reconnect() {
|
|
386
|
+
if (this.allowReconnect) {
|
|
387
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
388
|
+
} else {
|
|
389
|
+
this.disconnect();
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
disconnect() {
|
|
393
|
+
if (this._completed) return;
|
|
394
|
+
this._completed = true;
|
|
395
|
+
this._completionError = new Error("Peer disconnected.");
|
|
396
|
+
this._notifyConsumer();
|
|
397
|
+
}
|
|
398
|
+
// -- AsyncIterable --
|
|
399
|
+
[Symbol.asyncIterator]() {
|
|
400
|
+
if (this._iterating) throw new Error("RpcStream can only be iterated once.");
|
|
401
|
+
this._iterating = true;
|
|
402
|
+
let bufferIndex = 0;
|
|
403
|
+
let consumedIndex = 0;
|
|
404
|
+
const self = this;
|
|
405
|
+
return {
|
|
406
|
+
async next() {
|
|
407
|
+
if (!self._started) {
|
|
408
|
+
self._started = true;
|
|
409
|
+
self._sendAck(0, true);
|
|
410
|
+
}
|
|
411
|
+
while (true) {
|
|
412
|
+
if (bufferIndex < self._buffer.length) {
|
|
413
|
+
const value = self._buffer[bufferIndex];
|
|
414
|
+
bufferIndex++;
|
|
415
|
+
consumedIndex++;
|
|
416
|
+
return { value, done: false };
|
|
417
|
+
}
|
|
418
|
+
if (self._completed) {
|
|
419
|
+
if (self._completionError) throw self._completionError;
|
|
420
|
+
return { value: void 0, done: true };
|
|
421
|
+
}
|
|
422
|
+
self._consumerWaiting = new PromiseSource3();
|
|
423
|
+
await self._consumerWaiting.promise;
|
|
308
424
|
}
|
|
309
|
-
|
|
425
|
+
},
|
|
426
|
+
async return() {
|
|
427
|
+
self._sendAckEnd();
|
|
428
|
+
self.dispose();
|
|
429
|
+
return { value: void 0, done: true };
|
|
310
430
|
}
|
|
311
|
-
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
dispose() {
|
|
434
|
+
if (this._disposed) return;
|
|
435
|
+
this._disposed = true;
|
|
436
|
+
this._completed = true;
|
|
437
|
+
this.peer.remoteObjects.unregister(this);
|
|
438
|
+
this._notifyConsumer();
|
|
439
|
+
}
|
|
440
|
+
// -- Private --
|
|
441
|
+
_notifyConsumer() {
|
|
442
|
+
if (this._consumerWaiting !== null) {
|
|
443
|
+
this._consumerWaiting.resolve();
|
|
444
|
+
this._consumerWaiting = null;
|
|
312
445
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
446
|
+
}
|
|
447
|
+
_maybeSendAck(nextIndex) {
|
|
448
|
+
if (this.ackPeriod <= 0) return;
|
|
449
|
+
const threshold = this._ackSentUpTo + this.ackPeriod;
|
|
450
|
+
if (nextIndex >= threshold) {
|
|
451
|
+
this._sendAck(nextIndex, false);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
_sendAck(nextIndex, _mustReset) {
|
|
455
|
+
this._ackSentUpTo = nextIndex;
|
|
456
|
+
const conn = this.peer.connection;
|
|
457
|
+
if (conn) {
|
|
458
|
+
this.peer.hub.systemCallSender.ack(conn, this.id.localId, nextIndex, this.id.hostId);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
_sendAckEnd() {
|
|
462
|
+
const conn = this.peer.connection;
|
|
463
|
+
if (conn) {
|
|
464
|
+
this.peer.hub.systemCallSender.ackEnd(conn, this.id.localId, this.id.hostId);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
function resolveStreamRefs(value, peer) {
|
|
469
|
+
if (value === null || value === void 0) return value;
|
|
470
|
+
if (typeof value === "string") {
|
|
471
|
+
const ref = parseStreamRef(value);
|
|
472
|
+
if (ref !== null) {
|
|
473
|
+
const stream = new RpcStream(ref, peer);
|
|
474
|
+
peer.remoteObjects.register(stream);
|
|
475
|
+
return stream;
|
|
321
476
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
477
|
+
return value;
|
|
478
|
+
}
|
|
479
|
+
if (Array.isArray(value)) {
|
|
480
|
+
for (let i = 0; i < value.length; i++) {
|
|
481
|
+
value[i] = resolveStreamRefs(value[i], peer);
|
|
325
482
|
}
|
|
326
|
-
|
|
327
|
-
|
|
483
|
+
return value;
|
|
484
|
+
}
|
|
485
|
+
if (typeof value === "object") {
|
|
486
|
+
for (const key of Object.keys(value)) {
|
|
487
|
+
value[key] = resolveStreamRefs(
|
|
488
|
+
value[key],
|
|
489
|
+
peer
|
|
490
|
+
);
|
|
328
491
|
}
|
|
492
|
+
return value;
|
|
329
493
|
}
|
|
494
|
+
return value;
|
|
330
495
|
}
|
|
331
496
|
|
|
497
|
+
// src/rpc-system-call-handler.ts
|
|
498
|
+
var RpcSystemCallHandler = class {
|
|
499
|
+
handle(message, args, peer) {
|
|
500
|
+
const method = message.Method;
|
|
501
|
+
const relatedId = message.RelatedId ?? 0;
|
|
502
|
+
switch (method) {
|
|
503
|
+
case RpcSystemCalls.ok: {
|
|
504
|
+
const call = peer.outbound.get(relatedId);
|
|
505
|
+
if (call !== void 0) {
|
|
506
|
+
if (call.removeOnOk) {
|
|
507
|
+
peer.outbound.remove(relatedId);
|
|
508
|
+
}
|
|
509
|
+
call.result.resolve(args[0]);
|
|
510
|
+
}
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
case RpcSystemCalls.error: {
|
|
514
|
+
const call = peer.outbound.remove(relatedId);
|
|
515
|
+
if (call !== void 0) {
|
|
516
|
+
const errorInfo = args[0];
|
|
517
|
+
const msg = errorInfo?.Message ?? errorInfo?.message ?? "RPC error";
|
|
518
|
+
call.result.reject(new Error(msg));
|
|
519
|
+
}
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
case RpcSystemCalls.cancel: {
|
|
523
|
+
peer.inbound.remove(relatedId);
|
|
524
|
+
break;
|
|
525
|
+
}
|
|
526
|
+
case RpcSystemCalls.keepAlive: {
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
case RpcSystemCalls.item: {
|
|
530
|
+
const stream = peer.remoteObjects.get(relatedId);
|
|
531
|
+
if (stream) stream.onItem(args[0], resolveStreamRefs(args[1], peer));
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
case RpcSystemCalls.batch: {
|
|
535
|
+
const stream = peer.remoteObjects.get(relatedId);
|
|
536
|
+
if (stream) {
|
|
537
|
+
const items = args[1];
|
|
538
|
+
for (let i = 0; i < items.length; i++) items[i] = resolveStreamRefs(items[i], peer);
|
|
539
|
+
stream.onBatch(args[0], items);
|
|
540
|
+
}
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
case RpcSystemCalls.end: {
|
|
544
|
+
const stream = peer.remoteObjects.get(relatedId);
|
|
545
|
+
if (stream) {
|
|
546
|
+
const errorInfo = args[1];
|
|
547
|
+
const msg = errorInfo?.Message ?? errorInfo?.message;
|
|
548
|
+
const error = msg ? new Error(msg) : null;
|
|
549
|
+
stream.onEnd(args[0], error);
|
|
550
|
+
}
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
case RpcSystemCalls.ack: {
|
|
554
|
+
const sender = peer.sharedObjects.get(relatedId);
|
|
555
|
+
sender?.onAck(args[0], args[1]);
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
case RpcSystemCalls.ackEnd: {
|
|
559
|
+
const sender = peer.sharedObjects.get(relatedId);
|
|
560
|
+
sender?.onAckEnd(args[0]);
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
332
567
|
// src/rpc-system-call-sender.ts
|
|
333
568
|
var RpcSystemCallSender = class {
|
|
334
569
|
handshake(conn, peerId, hubId, index) {
|
|
@@ -361,10 +596,191 @@ var RpcSystemCallSender = class {
|
|
|
361
596
|
const msg = serializeMessage({ Method: RpcSystemCalls.keepAlive }, [activeCallIds]);
|
|
362
597
|
conn.send(msg);
|
|
363
598
|
}
|
|
599
|
+
item(conn, localId, index, item) {
|
|
600
|
+
const msg = serializeMessage(
|
|
601
|
+
{ Method: RpcSystemCalls.item, RelatedId: localId },
|
|
602
|
+
[index, item]
|
|
603
|
+
);
|
|
604
|
+
conn.send(msg);
|
|
605
|
+
}
|
|
606
|
+
batch(conn, localId, index, items) {
|
|
607
|
+
const msg = serializeMessage(
|
|
608
|
+
{ Method: RpcSystemCalls.batch, RelatedId: localId },
|
|
609
|
+
[index, items]
|
|
610
|
+
);
|
|
611
|
+
conn.send(msg);
|
|
612
|
+
}
|
|
613
|
+
end(conn, localId, index, error = null) {
|
|
614
|
+
const msg = serializeMessage(
|
|
615
|
+
{ Method: RpcSystemCalls.end, RelatedId: localId },
|
|
616
|
+
[index, error]
|
|
617
|
+
);
|
|
618
|
+
conn.send(msg);
|
|
619
|
+
}
|
|
620
|
+
ack(conn, localId, nextIndex, hostId) {
|
|
621
|
+
const msg = serializeMessage(
|
|
622
|
+
{ Method: RpcSystemCalls.ack, RelatedId: localId },
|
|
623
|
+
[nextIndex, hostId]
|
|
624
|
+
);
|
|
625
|
+
conn.send(msg);
|
|
626
|
+
}
|
|
627
|
+
ackEnd(conn, localId, hostId) {
|
|
628
|
+
const msg = serializeMessage(
|
|
629
|
+
{ Method: RpcSystemCalls.ackEnd, RelatedId: localId },
|
|
630
|
+
[hostId]
|
|
631
|
+
);
|
|
632
|
+
conn.send(msg);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
// src/rpc-stream-sender.ts
|
|
637
|
+
import { PromiseSource as PromiseSource4 } from "@actuallab/core";
|
|
638
|
+
var DEFAULT_ACK_PERIOD = 256;
|
|
639
|
+
var DEFAULT_ACK_ADVANCE = 128;
|
|
640
|
+
var RpcStreamSender = class {
|
|
641
|
+
id;
|
|
642
|
+
kind = RpcObjectKind.Local;
|
|
643
|
+
allowReconnect;
|
|
644
|
+
peer;
|
|
645
|
+
ackPeriod;
|
|
646
|
+
ackAdvance;
|
|
647
|
+
_nextIndex = 0;
|
|
648
|
+
_ended = false;
|
|
649
|
+
_started = new PromiseSource4();
|
|
650
|
+
constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, ackAdvance = DEFAULT_ACK_ADVANCE, allowReconnect = true) {
|
|
651
|
+
const localId = peer.sharedObjects.nextId();
|
|
652
|
+
this.id = { hostId: peer.hub.hubId, localId };
|
|
653
|
+
this.allowReconnect = allowReconnect;
|
|
654
|
+
this.peer = peer;
|
|
655
|
+
this.ackPeriod = ackPeriod;
|
|
656
|
+
this.ackAdvance = ackAdvance;
|
|
657
|
+
}
|
|
658
|
+
/** Returns the stream reference string for the $sys.Ok response. */
|
|
659
|
+
toRef() {
|
|
660
|
+
return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance},${this.allowReconnect ? "1" : "0"}`;
|
|
661
|
+
}
|
|
662
|
+
/** Called by system call handler when $sys.Ack is received from the client. */
|
|
663
|
+
onAck(_nextIndex, _hostId) {
|
|
664
|
+
if (!this._started.isCompleted) {
|
|
665
|
+
this._started.resolve();
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
/** Called by system call handler when $sys.AckEnd is received from the client. */
|
|
669
|
+
onAckEnd(_hostId) {
|
|
670
|
+
this._ended = true;
|
|
671
|
+
this.peer.sharedObjects.unregister(this);
|
|
672
|
+
}
|
|
673
|
+
/** Send a single item to the client. */
|
|
674
|
+
sendItem(item) {
|
|
675
|
+
if (this._ended) return;
|
|
676
|
+
const conn = this.peer.connection;
|
|
677
|
+
if (!conn) return;
|
|
678
|
+
this.peer.hub.systemCallSender.item(conn, this.id.localId, this._nextIndex, item);
|
|
679
|
+
this._nextIndex++;
|
|
680
|
+
}
|
|
681
|
+
/** Send a batch of items to the client. */
|
|
682
|
+
sendBatch(items) {
|
|
683
|
+
if (this._ended || items.length === 0) return;
|
|
684
|
+
const conn = this.peer.connection;
|
|
685
|
+
if (!conn) return;
|
|
686
|
+
this.peer.hub.systemCallSender.batch(conn, this.id.localId, this._nextIndex, items);
|
|
687
|
+
this._nextIndex += items.length;
|
|
688
|
+
}
|
|
689
|
+
/** Signal stream completion to the client. */
|
|
690
|
+
sendEnd(error) {
|
|
691
|
+
if (this._ended) return;
|
|
692
|
+
this._ended = true;
|
|
693
|
+
const conn = this.peer.connection;
|
|
694
|
+
if (!conn) return;
|
|
695
|
+
const errorInfo = error ? { Message: error.message } : null;
|
|
696
|
+
this.peer.hub.systemCallSender.end(conn, this.id.localId, this._nextIndex, errorInfo);
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Consume an AsyncIterable and send all items to the client.
|
|
700
|
+
* Waits for the client's initial Ack before starting to pump items.
|
|
701
|
+
*/
|
|
702
|
+
async writeFrom(source) {
|
|
703
|
+
await this._started.promise;
|
|
704
|
+
try {
|
|
705
|
+
for await (const item of source) {
|
|
706
|
+
if (this._ended) return;
|
|
707
|
+
this.sendItem(item);
|
|
708
|
+
}
|
|
709
|
+
if (!this._ended) {
|
|
710
|
+
this.sendEnd();
|
|
711
|
+
}
|
|
712
|
+
} catch (e) {
|
|
713
|
+
if (!this._ended) {
|
|
714
|
+
this.sendEnd(e instanceof Error ? e : new Error(String(e)));
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
// -- IRpcObject --
|
|
719
|
+
reconnect() {
|
|
720
|
+
}
|
|
721
|
+
disconnect() {
|
|
722
|
+
this._ended = true;
|
|
723
|
+
if (!this._started.isCompleted) {
|
|
724
|
+
this._started.resolve();
|
|
725
|
+
}
|
|
726
|
+
this.peer.sharedObjects.unregister(this);
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
// src/rpc-remote-object-tracker.ts
|
|
731
|
+
var RpcRemoteObjectTracker = class {
|
|
732
|
+
_objects = /* @__PURE__ */ new Map();
|
|
733
|
+
register(obj) {
|
|
734
|
+
this._objects.set(obj.id.localId, obj);
|
|
735
|
+
}
|
|
736
|
+
get(localId) {
|
|
737
|
+
return this._objects.get(localId);
|
|
738
|
+
}
|
|
739
|
+
unregister(obj) {
|
|
740
|
+
this._objects.delete(obj.id.localId);
|
|
741
|
+
}
|
|
742
|
+
disconnectAll() {
|
|
743
|
+
for (const obj of this._objects.values()) {
|
|
744
|
+
obj.disconnect();
|
|
745
|
+
}
|
|
746
|
+
this._objects.clear();
|
|
747
|
+
}
|
|
748
|
+
reconnectAll() {
|
|
749
|
+
for (const obj of this._objects.values()) {
|
|
750
|
+
if (obj.allowReconnect)
|
|
751
|
+
obj.reconnect();
|
|
752
|
+
else
|
|
753
|
+
obj.disconnect();
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
// src/rpc-shared-object-tracker.ts
|
|
759
|
+
var RpcSharedObjectTracker = class {
|
|
760
|
+
_nextId = 1;
|
|
761
|
+
_objects = /* @__PURE__ */ new Map();
|
|
762
|
+
nextId() {
|
|
763
|
+
return this._nextId++;
|
|
764
|
+
}
|
|
765
|
+
register(obj) {
|
|
766
|
+
this._objects.set(obj.id.localId, obj);
|
|
767
|
+
}
|
|
768
|
+
get(localId) {
|
|
769
|
+
return this._objects.get(localId);
|
|
770
|
+
}
|
|
771
|
+
unregister(obj) {
|
|
772
|
+
this._objects.delete(obj.id.localId);
|
|
773
|
+
}
|
|
774
|
+
disconnectAll() {
|
|
775
|
+
for (const obj of this._objects.values()) {
|
|
776
|
+
obj.disconnect();
|
|
777
|
+
}
|
|
778
|
+
this._objects.clear();
|
|
779
|
+
}
|
|
364
780
|
};
|
|
365
781
|
|
|
366
782
|
// src/rpc-peer.ts
|
|
367
|
-
import { EventHandlerSet as EventHandlerSet3, PromiseSource as
|
|
783
|
+
import { EventHandlerSet as EventHandlerSet3, PromiseSource as PromiseSource5 } from "@actuallab/core";
|
|
368
784
|
|
|
369
785
|
// src/rpc-client-peer-reconnect-delayer.ts
|
|
370
786
|
import { RetryDelaySeq, RetryDelayer } from "@actuallab/core";
|
|
@@ -390,6 +806,8 @@ var RpcPeer = class {
|
|
|
390
806
|
_connection;
|
|
391
807
|
outbound = new RpcOutboundCallTracker();
|
|
392
808
|
inbound = new RpcInboundCallTracker();
|
|
809
|
+
remoteObjects = new RpcRemoteObjectTracker();
|
|
810
|
+
sharedObjects = new RpcSharedObjectTracker();
|
|
393
811
|
connected = new EventHandlerSet3();
|
|
394
812
|
disconnected = new EventHandlerSet3();
|
|
395
813
|
_pendingSends = [];
|
|
@@ -398,6 +816,9 @@ var RpcPeer = class {
|
|
|
398
816
|
this.ref = ref;
|
|
399
817
|
this._hub = hub;
|
|
400
818
|
}
|
|
819
|
+
get hub() {
|
|
820
|
+
return this._hub;
|
|
821
|
+
}
|
|
401
822
|
get connection() {
|
|
402
823
|
return this._connection;
|
|
403
824
|
}
|
|
@@ -463,6 +884,8 @@ var RpcPeer = class {
|
|
|
463
884
|
}
|
|
464
885
|
close() {
|
|
465
886
|
this._stopKeepAlive();
|
|
887
|
+
this.remoteObjects.disconnectAll();
|
|
888
|
+
this.sharedObjects.disconnectAll();
|
|
466
889
|
for (const call of this._pendingSends) {
|
|
467
890
|
if (!call.result.isCompleted)
|
|
468
891
|
call.result.reject(new Error("Peer closed."));
|
|
@@ -496,7 +919,7 @@ var RpcPeer = class {
|
|
|
496
919
|
return;
|
|
497
920
|
}
|
|
498
921
|
if (method.startsWith("$sys")) {
|
|
499
|
-
this._hub.
|
|
922
|
+
this._hub.systemCallHandler.handle(message, args, this);
|
|
500
923
|
return;
|
|
501
924
|
}
|
|
502
925
|
this._handleInbound(message, args);
|
|
@@ -516,7 +939,12 @@ var RpcPeer = class {
|
|
|
516
939
|
try {
|
|
517
940
|
const context = this._connection !== void 0 ? { __rpcDispatch: true, callId: relatedId, connection: this._connection } : void 0;
|
|
518
941
|
const result = await serviceHost.dispatch(method, args, context);
|
|
519
|
-
if (
|
|
942
|
+
if (methodDef?.stream === true && this._connection !== void 0) {
|
|
943
|
+
const sender = new RpcStreamSender(this);
|
|
944
|
+
this.sharedObjects.register(sender);
|
|
945
|
+
this._hub.systemCallSender.ok(this._connection, relatedId, sender.toRef());
|
|
946
|
+
void sender.writeFrom(result);
|
|
947
|
+
} else if (!isNoWait && this._connection !== void 0) {
|
|
520
948
|
this._hub.systemCallSender.ok(this._connection, relatedId, result);
|
|
521
949
|
}
|
|
522
950
|
} catch (e) {
|
|
@@ -601,7 +1029,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
601
1029
|
const ws = wsFactory?.(connUrl) ?? new WebSocket(connUrl);
|
|
602
1030
|
const conn = new RpcWebSocketConnection(ws);
|
|
603
1031
|
this._connectionKind = 1 /* Connecting */;
|
|
604
|
-
this._pendingHandshake = new
|
|
1032
|
+
this._pendingHandshake = new PromiseSource5();
|
|
605
1033
|
this.setupConnection(conn);
|
|
606
1034
|
this._connection = void 0;
|
|
607
1035
|
const closedRejection = new Promise((_, reject) => conn.closed.add(() => reject(new Error("Connection failed"))));
|
|
@@ -657,6 +1085,11 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
657
1085
|
_reconnect(_isPeerChanged) {
|
|
658
1086
|
if (this._connection === void 0) return;
|
|
659
1087
|
const conn = this._connection;
|
|
1088
|
+
if (_isPeerChanged) {
|
|
1089
|
+
this.remoteObjects.disconnectAll();
|
|
1090
|
+
} else {
|
|
1091
|
+
this.remoteObjects.reconnectAll();
|
|
1092
|
+
}
|
|
660
1093
|
const trackerCalls = [...this.outbound.values()];
|
|
661
1094
|
for (const call of trackerCalls) {
|
|
662
1095
|
if (!call.removeOnOk && call.result.isCompleted) {
|
|
@@ -739,12 +1172,10 @@ function getMethodsMeta(cls) {
|
|
|
739
1172
|
function getServiceMeta(cls) {
|
|
740
1173
|
return cls[Symbol.metadata]?.[SERVICE_META];
|
|
741
1174
|
}
|
|
742
|
-
function rpcService(serviceName
|
|
1175
|
+
function rpcService(serviceName) {
|
|
743
1176
|
return function(_target, context) {
|
|
744
1177
|
const meta = context.metadata[SERVICE_META] ??= {};
|
|
745
1178
|
meta.name = serviceName;
|
|
746
|
-
if (options?.ctOffset !== void 0)
|
|
747
|
-
meta.ctOffset = options.ctOffset;
|
|
748
1179
|
};
|
|
749
1180
|
}
|
|
750
1181
|
function rpcMethod(options) {
|
|
@@ -754,8 +1185,7 @@ function rpcMethod(options) {
|
|
|
754
1185
|
methods[methodName] = {
|
|
755
1186
|
...methods[methodName],
|
|
756
1187
|
argCount: target.length,
|
|
757
|
-
|
|
758
|
-
noWait: options?.noWait ?? false
|
|
1188
|
+
returns: options?.returns
|
|
759
1189
|
};
|
|
760
1190
|
return target;
|
|
761
1191
|
};
|
|
@@ -767,6 +1197,7 @@ var RpcHub = class {
|
|
|
767
1197
|
peers = /* @__PURE__ */ new Map();
|
|
768
1198
|
serviceHost;
|
|
769
1199
|
systemCallSender = new RpcSystemCallSender();
|
|
1200
|
+
systemCallHandler = new RpcSystemCallHandler();
|
|
770
1201
|
constructor(hubId) {
|
|
771
1202
|
this.hubId = hubId ?? crypto.randomUUID();
|
|
772
1203
|
this.serviceHost = new RpcServiceHost();
|
|
@@ -840,19 +1271,14 @@ var RpcHub = class {
|
|
|
840
1271
|
for (const peer of this.peers.values()) peer.close();
|
|
841
1272
|
this.peers.clear();
|
|
842
1273
|
}
|
|
843
|
-
/** Dispatch an inbound system call. Override in FusionHub to handle compute-specific system calls. */
|
|
844
|
-
handleSystemCall(message, args, outbound, inbound) {
|
|
845
|
-
handleSystemCall(message, args, outbound, inbound);
|
|
846
|
-
}
|
|
847
1274
|
/** Build an RpcServiceDef from decorator metadata on a contract class. Override in FusionHub for compute support. */
|
|
848
1275
|
_buildServiceDef(cls) {
|
|
849
1276
|
const svcMeta = getServiceMeta(cls);
|
|
850
1277
|
if (svcMeta === void 0) throw new Error("Contract class missing @rpcService metadata");
|
|
851
1278
|
const methodsMeta = getMethodsMeta(cls) ?? {};
|
|
852
|
-
const ctOffset = svcMeta.ctOffset ?? 0;
|
|
853
1279
|
const methods = /* @__PURE__ */ new Map();
|
|
854
1280
|
for (const [name, meta] of Object.entries(methodsMeta)) {
|
|
855
|
-
const wireArgCount = meta.argCount +
|
|
1281
|
+
const wireArgCount = meta.argCount + 1;
|
|
856
1282
|
const mapKey = `${name}:${wireArgCount}`;
|
|
857
1283
|
methods.set(mapKey, {
|
|
858
1284
|
name,
|
|
@@ -860,8 +1286,8 @@ var RpcHub = class {
|
|
|
860
1286
|
argCount: meta.argCount,
|
|
861
1287
|
wireArgCount,
|
|
862
1288
|
callTypeId: 0,
|
|
863
|
-
stream: meta.
|
|
864
|
-
noWait: meta.
|
|
1289
|
+
stream: meta.returns === RpcType.stream,
|
|
1290
|
+
noWait: meta.returns === RpcType.noWait
|
|
865
1291
|
});
|
|
866
1292
|
}
|
|
867
1293
|
return { name: svcMeta.name, methods };
|
|
@@ -885,11 +1311,24 @@ var RpcHub = class {
|
|
|
885
1311
|
peer.callNoWait(wireName, callArgs);
|
|
886
1312
|
};
|
|
887
1313
|
}
|
|
1314
|
+
if (methodDef.stream) {
|
|
1315
|
+
return async (...args) => {
|
|
1316
|
+
const callArgs = args.slice(0, methodDef.argCount);
|
|
1317
|
+
const outboundCall = peer.call(wireName, callArgs);
|
|
1318
|
+
const result = await outboundCall.result.promise;
|
|
1319
|
+
const ref = parseStreamRef(result);
|
|
1320
|
+
if (!ref) throw new Error(`Expected stream reference, got: ${JSON.stringify(result)}`);
|
|
1321
|
+
const stream = new RpcStream(ref, peer);
|
|
1322
|
+
peer.remoteObjects.register(stream);
|
|
1323
|
+
return stream;
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
888
1326
|
const callOptions = methodDef.callTypeId !== 0 ? { callTypeId: methodDef.callTypeId } : void 0;
|
|
889
1327
|
return async (...args) => {
|
|
890
1328
|
const callArgs = args.slice(0, methodDef.argCount);
|
|
891
1329
|
const outboundCall = peer.call(wireName, callArgs, callOptions);
|
|
892
|
-
|
|
1330
|
+
const result = await outboundCall.result.promise;
|
|
1331
|
+
return resolveStreamRefs(result, peer);
|
|
893
1332
|
};
|
|
894
1333
|
}
|
|
895
1334
|
};
|
|
@@ -1099,14 +1538,20 @@ export {
|
|
|
1099
1538
|
RpcInboundCall,
|
|
1100
1539
|
RpcInboundCallTracker,
|
|
1101
1540
|
RpcMessageChannelConnection,
|
|
1541
|
+
RpcObjectKind,
|
|
1102
1542
|
RpcOutboundCall,
|
|
1103
1543
|
RpcOutboundCallTracker,
|
|
1104
1544
|
RpcPeer,
|
|
1105
1545
|
RpcPeerConnectionKind,
|
|
1106
1546
|
RpcPeerStateKind,
|
|
1107
1547
|
RpcPeerStateMonitor,
|
|
1548
|
+
RpcRemoteObjectTracker,
|
|
1108
1549
|
RpcServerPeer,
|
|
1109
1550
|
RpcServiceHost,
|
|
1551
|
+
RpcSharedObjectTracker,
|
|
1552
|
+
RpcStream,
|
|
1553
|
+
RpcStreamSender,
|
|
1554
|
+
RpcSystemCallHandler,
|
|
1110
1555
|
RpcSystemCallSender,
|
|
1111
1556
|
RpcSystemCalls,
|
|
1112
1557
|
RpcType,
|
|
@@ -1120,9 +1565,10 @@ export {
|
|
|
1120
1565
|
getMethodsMeta,
|
|
1121
1566
|
getServiceMeta,
|
|
1122
1567
|
getStateDescription,
|
|
1123
|
-
handleSystemCall,
|
|
1124
1568
|
isConnected,
|
|
1125
1569
|
likelyConnected,
|
|
1570
|
+
parseStreamRef,
|
|
1571
|
+
resolveStreamRefs,
|
|
1126
1572
|
rpcMethod,
|
|
1127
1573
|
rpcService,
|
|
1128
1574
|
serializeFrame,
|