@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.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,263 @@ 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) 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
|
+
return { hostId, localId, ackPeriod, ackAdvance };
|
|
320
|
+
}
|
|
321
|
+
var RpcStream = class {
|
|
322
|
+
id;
|
|
323
|
+
kind = RpcObjectKind.Remote;
|
|
324
|
+
peer;
|
|
325
|
+
ackPeriod;
|
|
326
|
+
ackAdvance;
|
|
327
|
+
_buffer = [];
|
|
328
|
+
_nextExpectedIndex = 0;
|
|
329
|
+
_consumerWaiting = null;
|
|
330
|
+
_completed = false;
|
|
331
|
+
_completionError = null;
|
|
332
|
+
_disposed = false;
|
|
333
|
+
_started = false;
|
|
334
|
+
_iterating = false;
|
|
335
|
+
_ackSentUpTo = 0;
|
|
336
|
+
constructor(ref, peer) {
|
|
337
|
+
this.id = { hostId: ref.hostId, localId: ref.localId };
|
|
338
|
+
this.peer = peer;
|
|
339
|
+
this.ackPeriod = ref.ackPeriod;
|
|
340
|
+
this.ackAdvance = ref.ackAdvance;
|
|
341
|
+
}
|
|
342
|
+
/** Called by system call handler when a single item arrives ($sys.I). */
|
|
343
|
+
onItem(index, item) {
|
|
344
|
+
if (this._disposed || this._completed) return;
|
|
345
|
+
if (index > this._nextExpectedIndex) {
|
|
346
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (index < this._nextExpectedIndex) {
|
|
350
|
+
this._maybeSendAck(index + 1);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
this._buffer.push(item);
|
|
354
|
+
this._nextExpectedIndex = index + 1;
|
|
355
|
+
this._maybeSendAck(this._nextExpectedIndex);
|
|
356
|
+
this._notifyConsumer();
|
|
357
|
+
}
|
|
358
|
+
/** Called by system call handler when a batch arrives ($sys.B). */
|
|
359
|
+
onBatch(index, items) {
|
|
360
|
+
if (this._disposed || this._completed) return;
|
|
361
|
+
if (index > this._nextExpectedIndex) {
|
|
362
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (index < this._nextExpectedIndex) {
|
|
366
|
+
this._maybeSendAck(index + items.length);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
for (const item of items) this._buffer.push(item);
|
|
370
|
+
this._nextExpectedIndex = index + items.length;
|
|
371
|
+
this._maybeSendAck(this._nextExpectedIndex);
|
|
372
|
+
this._notifyConsumer();
|
|
373
|
+
}
|
|
374
|
+
/** Called by system call handler when the stream ends ($sys.End). */
|
|
375
|
+
onEnd(index, error) {
|
|
376
|
+
if (this._disposed || this._completed) return;
|
|
377
|
+
this._completed = true;
|
|
378
|
+
this._completionError = error;
|
|
379
|
+
this._notifyConsumer();
|
|
380
|
+
}
|
|
381
|
+
// -- IRpcObject --
|
|
382
|
+
reconnect() {
|
|
383
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
384
|
+
}
|
|
385
|
+
disconnect() {
|
|
386
|
+
if (this._completed) return;
|
|
387
|
+
this._completed = true;
|
|
388
|
+
this._completionError = new Error("Peer disconnected.");
|
|
389
|
+
this._notifyConsumer();
|
|
390
|
+
}
|
|
391
|
+
// -- AsyncIterable --
|
|
392
|
+
[Symbol.asyncIterator]() {
|
|
393
|
+
if (this._iterating) throw new Error("RpcStream can only be iterated once.");
|
|
394
|
+
this._iterating = true;
|
|
395
|
+
let bufferIndex = 0;
|
|
396
|
+
let consumedIndex = 0;
|
|
397
|
+
const self = this;
|
|
398
|
+
return {
|
|
399
|
+
async next() {
|
|
400
|
+
if (!self._started) {
|
|
401
|
+
self._started = true;
|
|
402
|
+
self._sendAck(0, true);
|
|
308
403
|
}
|
|
309
|
-
|
|
404
|
+
while (true) {
|
|
405
|
+
if (bufferIndex < self._buffer.length) {
|
|
406
|
+
const value = self._buffer[bufferIndex];
|
|
407
|
+
bufferIndex++;
|
|
408
|
+
consumedIndex++;
|
|
409
|
+
return { value, done: false };
|
|
410
|
+
}
|
|
411
|
+
if (self._completed) {
|
|
412
|
+
if (self._completionError) throw self._completionError;
|
|
413
|
+
return { value: void 0, done: true };
|
|
414
|
+
}
|
|
415
|
+
self._consumerWaiting = new PromiseSource3();
|
|
416
|
+
await self._consumerWaiting.promise;
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
async return() {
|
|
420
|
+
self._sendAckEnd();
|
|
421
|
+
self.dispose();
|
|
422
|
+
return { value: void 0, done: true };
|
|
310
423
|
}
|
|
311
|
-
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
dispose() {
|
|
427
|
+
if (this._disposed) return;
|
|
428
|
+
this._disposed = true;
|
|
429
|
+
this._completed = true;
|
|
430
|
+
this.peer.remoteObjects.unregister(this);
|
|
431
|
+
this._notifyConsumer();
|
|
432
|
+
}
|
|
433
|
+
// -- Private --
|
|
434
|
+
_notifyConsumer() {
|
|
435
|
+
if (this._consumerWaiting !== null) {
|
|
436
|
+
this._consumerWaiting.resolve();
|
|
437
|
+
this._consumerWaiting = null;
|
|
312
438
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
439
|
+
}
|
|
440
|
+
_maybeSendAck(nextIndex) {
|
|
441
|
+
if (this.ackPeriod <= 0) return;
|
|
442
|
+
const threshold = this._ackSentUpTo + this.ackPeriod;
|
|
443
|
+
if (nextIndex >= threshold) {
|
|
444
|
+
this._sendAck(nextIndex, false);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
_sendAck(nextIndex, _mustReset) {
|
|
448
|
+
this._ackSentUpTo = nextIndex;
|
|
449
|
+
const conn = this.peer.connection;
|
|
450
|
+
if (conn) {
|
|
451
|
+
this.peer.hub.systemCallSender.ack(conn, this.id.localId, nextIndex, this.id.hostId);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
_sendAckEnd() {
|
|
455
|
+
const conn = this.peer.connection;
|
|
456
|
+
if (conn) {
|
|
457
|
+
this.peer.hub.systemCallSender.ackEnd(conn, this.id.localId, this.id.hostId);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
function resolveStreamRefs(value, peer) {
|
|
462
|
+
if (value === null || value === void 0) return value;
|
|
463
|
+
if (typeof value === "string") {
|
|
464
|
+
const ref = parseStreamRef(value);
|
|
465
|
+
if (ref !== null) {
|
|
466
|
+
const stream = new RpcStream(ref, peer);
|
|
467
|
+
peer.remoteObjects.register(stream);
|
|
468
|
+
return stream;
|
|
321
469
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
470
|
+
return value;
|
|
471
|
+
}
|
|
472
|
+
if (Array.isArray(value)) {
|
|
473
|
+
for (let i = 0; i < value.length; i++) {
|
|
474
|
+
value[i] = resolveStreamRefs(value[i], peer);
|
|
325
475
|
}
|
|
326
|
-
|
|
327
|
-
|
|
476
|
+
return value;
|
|
477
|
+
}
|
|
478
|
+
if (typeof value === "object") {
|
|
479
|
+
for (const key of Object.keys(value)) {
|
|
480
|
+
value[key] = resolveStreamRefs(
|
|
481
|
+
value[key],
|
|
482
|
+
peer
|
|
483
|
+
);
|
|
328
484
|
}
|
|
485
|
+
return value;
|
|
329
486
|
}
|
|
487
|
+
return value;
|
|
330
488
|
}
|
|
331
489
|
|
|
490
|
+
// src/rpc-system-call-handler.ts
|
|
491
|
+
var RpcSystemCallHandler = class {
|
|
492
|
+
handle(message, args, peer) {
|
|
493
|
+
const method = message.Method;
|
|
494
|
+
const relatedId = message.RelatedId ?? 0;
|
|
495
|
+
switch (method) {
|
|
496
|
+
case RpcSystemCalls.ok: {
|
|
497
|
+
const call = peer.outbound.get(relatedId);
|
|
498
|
+
if (call !== void 0) {
|
|
499
|
+
if (call.removeOnOk) {
|
|
500
|
+
peer.outbound.remove(relatedId);
|
|
501
|
+
}
|
|
502
|
+
call.result.resolve(args[0]);
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
case RpcSystemCalls.error: {
|
|
507
|
+
const call = peer.outbound.remove(relatedId);
|
|
508
|
+
if (call !== void 0) {
|
|
509
|
+
const errorInfo = args[0];
|
|
510
|
+
const msg = errorInfo?.Message ?? errorInfo?.message ?? "RPC error";
|
|
511
|
+
call.result.reject(new Error(msg));
|
|
512
|
+
}
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
case RpcSystemCalls.cancel: {
|
|
516
|
+
peer.inbound.remove(relatedId);
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
case RpcSystemCalls.keepAlive: {
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
case RpcSystemCalls.item: {
|
|
523
|
+
const stream = peer.remoteObjects.get(relatedId);
|
|
524
|
+
if (stream) stream.onItem(args[0], resolveStreamRefs(args[1], peer));
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
case RpcSystemCalls.batch: {
|
|
528
|
+
const stream = peer.remoteObjects.get(relatedId);
|
|
529
|
+
if (stream) {
|
|
530
|
+
const items = args[1];
|
|
531
|
+
for (let i = 0; i < items.length; i++) items[i] = resolveStreamRefs(items[i], peer);
|
|
532
|
+
stream.onBatch(args[0], items);
|
|
533
|
+
}
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
case RpcSystemCalls.end: {
|
|
537
|
+
const stream = peer.remoteObjects.get(relatedId);
|
|
538
|
+
if (stream) {
|
|
539
|
+
const errorInfo = args[1];
|
|
540
|
+
const msg = errorInfo?.Message ?? errorInfo?.message;
|
|
541
|
+
const error = msg ? new Error(msg) : null;
|
|
542
|
+
stream.onEnd(args[0], error);
|
|
543
|
+
}
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
case RpcSystemCalls.ack: {
|
|
547
|
+
const sender = peer.sharedObjects.get(relatedId);
|
|
548
|
+
sender?.onAck(args[0], args[1]);
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
case RpcSystemCalls.ackEnd: {
|
|
552
|
+
const sender = peer.sharedObjects.get(relatedId);
|
|
553
|
+
sender?.onAckEnd(args[0]);
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
|
|
332
560
|
// src/rpc-system-call-sender.ts
|
|
333
561
|
var RpcSystemCallSender = class {
|
|
334
562
|
handshake(conn, peerId, hubId, index) {
|
|
@@ -361,10 +589,186 @@ var RpcSystemCallSender = class {
|
|
|
361
589
|
const msg = serializeMessage({ Method: RpcSystemCalls.keepAlive }, [activeCallIds]);
|
|
362
590
|
conn.send(msg);
|
|
363
591
|
}
|
|
592
|
+
item(conn, localId, index, item) {
|
|
593
|
+
const msg = serializeMessage(
|
|
594
|
+
{ Method: RpcSystemCalls.item, RelatedId: localId },
|
|
595
|
+
[index, item]
|
|
596
|
+
);
|
|
597
|
+
conn.send(msg);
|
|
598
|
+
}
|
|
599
|
+
batch(conn, localId, index, items) {
|
|
600
|
+
const msg = serializeMessage(
|
|
601
|
+
{ Method: RpcSystemCalls.batch, RelatedId: localId },
|
|
602
|
+
[index, items]
|
|
603
|
+
);
|
|
604
|
+
conn.send(msg);
|
|
605
|
+
}
|
|
606
|
+
end(conn, localId, index, error = null) {
|
|
607
|
+
const msg = serializeMessage(
|
|
608
|
+
{ Method: RpcSystemCalls.end, RelatedId: localId },
|
|
609
|
+
[index, error]
|
|
610
|
+
);
|
|
611
|
+
conn.send(msg);
|
|
612
|
+
}
|
|
613
|
+
ack(conn, localId, nextIndex, hostId) {
|
|
614
|
+
const msg = serializeMessage(
|
|
615
|
+
{ Method: RpcSystemCalls.ack, RelatedId: localId },
|
|
616
|
+
[nextIndex, hostId]
|
|
617
|
+
);
|
|
618
|
+
conn.send(msg);
|
|
619
|
+
}
|
|
620
|
+
ackEnd(conn, localId, hostId) {
|
|
621
|
+
const msg = serializeMessage(
|
|
622
|
+
{ Method: RpcSystemCalls.ackEnd, RelatedId: localId },
|
|
623
|
+
[hostId]
|
|
624
|
+
);
|
|
625
|
+
conn.send(msg);
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
// src/rpc-stream-sender.ts
|
|
630
|
+
import { PromiseSource as PromiseSource4 } from "@actuallab/core";
|
|
631
|
+
var DEFAULT_ACK_PERIOD = 256;
|
|
632
|
+
var DEFAULT_ACK_ADVANCE = 128;
|
|
633
|
+
var RpcStreamSender = class {
|
|
634
|
+
id;
|
|
635
|
+
kind = RpcObjectKind.Local;
|
|
636
|
+
peer;
|
|
637
|
+
ackPeriod;
|
|
638
|
+
ackAdvance;
|
|
639
|
+
_nextIndex = 0;
|
|
640
|
+
_ended = false;
|
|
641
|
+
_started = new PromiseSource4();
|
|
642
|
+
constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, ackAdvance = DEFAULT_ACK_ADVANCE) {
|
|
643
|
+
const localId = peer.sharedObjects.nextId();
|
|
644
|
+
this.id = { hostId: peer.hub.hubId, localId };
|
|
645
|
+
this.peer = peer;
|
|
646
|
+
this.ackPeriod = ackPeriod;
|
|
647
|
+
this.ackAdvance = ackAdvance;
|
|
648
|
+
}
|
|
649
|
+
/** Returns the stream reference string for the $sys.Ok response. */
|
|
650
|
+
toRef() {
|
|
651
|
+
return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance}`;
|
|
652
|
+
}
|
|
653
|
+
/** Called by system call handler when $sys.Ack is received from the client. */
|
|
654
|
+
onAck(_nextIndex, _hostId) {
|
|
655
|
+
if (!this._started.isCompleted) {
|
|
656
|
+
this._started.resolve();
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
/** Called by system call handler when $sys.AckEnd is received from the client. */
|
|
660
|
+
onAckEnd(_hostId) {
|
|
661
|
+
this._ended = true;
|
|
662
|
+
this.peer.sharedObjects.unregister(this);
|
|
663
|
+
}
|
|
664
|
+
/** Send a single item to the client. */
|
|
665
|
+
sendItem(item) {
|
|
666
|
+
if (this._ended) return;
|
|
667
|
+
const conn = this.peer.connection;
|
|
668
|
+
if (!conn) return;
|
|
669
|
+
this.peer.hub.systemCallSender.item(conn, this.id.localId, this._nextIndex, item);
|
|
670
|
+
this._nextIndex++;
|
|
671
|
+
}
|
|
672
|
+
/** Send a batch of items to the client. */
|
|
673
|
+
sendBatch(items) {
|
|
674
|
+
if (this._ended || items.length === 0) return;
|
|
675
|
+
const conn = this.peer.connection;
|
|
676
|
+
if (!conn) return;
|
|
677
|
+
this.peer.hub.systemCallSender.batch(conn, this.id.localId, this._nextIndex, items);
|
|
678
|
+
this._nextIndex += items.length;
|
|
679
|
+
}
|
|
680
|
+
/** Signal stream completion to the client. */
|
|
681
|
+
sendEnd(error) {
|
|
682
|
+
if (this._ended) return;
|
|
683
|
+
this._ended = true;
|
|
684
|
+
const conn = this.peer.connection;
|
|
685
|
+
if (!conn) return;
|
|
686
|
+
const errorInfo = error ? { Message: error.message } : null;
|
|
687
|
+
this.peer.hub.systemCallSender.end(conn, this.id.localId, this._nextIndex, errorInfo);
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Consume an AsyncIterable and send all items to the client.
|
|
691
|
+
* Waits for the client's initial Ack before starting to pump items.
|
|
692
|
+
*/
|
|
693
|
+
async writeFrom(source) {
|
|
694
|
+
await this._started.promise;
|
|
695
|
+
try {
|
|
696
|
+
for await (const item of source) {
|
|
697
|
+
if (this._ended) return;
|
|
698
|
+
this.sendItem(item);
|
|
699
|
+
}
|
|
700
|
+
if (!this._ended) {
|
|
701
|
+
this.sendEnd();
|
|
702
|
+
}
|
|
703
|
+
} catch (e) {
|
|
704
|
+
if (!this._ended) {
|
|
705
|
+
this.sendEnd(e instanceof Error ? e : new Error(String(e)));
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
// -- IRpcObject --
|
|
710
|
+
reconnect() {
|
|
711
|
+
}
|
|
712
|
+
disconnect() {
|
|
713
|
+
this._ended = true;
|
|
714
|
+
if (!this._started.isCompleted) {
|
|
715
|
+
this._started.resolve();
|
|
716
|
+
}
|
|
717
|
+
this.peer.sharedObjects.unregister(this);
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
// src/rpc-remote-object-tracker.ts
|
|
722
|
+
var RpcRemoteObjectTracker = class {
|
|
723
|
+
_objects = /* @__PURE__ */ new Map();
|
|
724
|
+
register(obj) {
|
|
725
|
+
this._objects.set(obj.id.localId, obj);
|
|
726
|
+
}
|
|
727
|
+
get(localId) {
|
|
728
|
+
return this._objects.get(localId);
|
|
729
|
+
}
|
|
730
|
+
unregister(obj) {
|
|
731
|
+
this._objects.delete(obj.id.localId);
|
|
732
|
+
}
|
|
733
|
+
disconnectAll() {
|
|
734
|
+
for (const obj of this._objects.values()) {
|
|
735
|
+
obj.disconnect();
|
|
736
|
+
}
|
|
737
|
+
this._objects.clear();
|
|
738
|
+
}
|
|
739
|
+
reconnectAll() {
|
|
740
|
+
for (const obj of this._objects.values()) {
|
|
741
|
+
obj.reconnect();
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
// src/rpc-shared-object-tracker.ts
|
|
747
|
+
var RpcSharedObjectTracker = class {
|
|
748
|
+
_nextId = 1;
|
|
749
|
+
_objects = /* @__PURE__ */ new Map();
|
|
750
|
+
nextId() {
|
|
751
|
+
return this._nextId++;
|
|
752
|
+
}
|
|
753
|
+
register(obj) {
|
|
754
|
+
this._objects.set(obj.id.localId, obj);
|
|
755
|
+
}
|
|
756
|
+
get(localId) {
|
|
757
|
+
return this._objects.get(localId);
|
|
758
|
+
}
|
|
759
|
+
unregister(obj) {
|
|
760
|
+
this._objects.delete(obj.id.localId);
|
|
761
|
+
}
|
|
762
|
+
disconnectAll() {
|
|
763
|
+
for (const obj of this._objects.values()) {
|
|
764
|
+
obj.disconnect();
|
|
765
|
+
}
|
|
766
|
+
this._objects.clear();
|
|
767
|
+
}
|
|
364
768
|
};
|
|
365
769
|
|
|
366
770
|
// src/rpc-peer.ts
|
|
367
|
-
import { EventHandlerSet as EventHandlerSet3, PromiseSource as
|
|
771
|
+
import { EventHandlerSet as EventHandlerSet3, PromiseSource as PromiseSource5 } from "@actuallab/core";
|
|
368
772
|
|
|
369
773
|
// src/rpc-client-peer-reconnect-delayer.ts
|
|
370
774
|
import { RetryDelaySeq, RetryDelayer } from "@actuallab/core";
|
|
@@ -390,6 +794,8 @@ var RpcPeer = class {
|
|
|
390
794
|
_connection;
|
|
391
795
|
outbound = new RpcOutboundCallTracker();
|
|
392
796
|
inbound = new RpcInboundCallTracker();
|
|
797
|
+
remoteObjects = new RpcRemoteObjectTracker();
|
|
798
|
+
sharedObjects = new RpcSharedObjectTracker();
|
|
393
799
|
connected = new EventHandlerSet3();
|
|
394
800
|
disconnected = new EventHandlerSet3();
|
|
395
801
|
_pendingSends = [];
|
|
@@ -398,6 +804,9 @@ var RpcPeer = class {
|
|
|
398
804
|
this.ref = ref;
|
|
399
805
|
this._hub = hub;
|
|
400
806
|
}
|
|
807
|
+
get hub() {
|
|
808
|
+
return this._hub;
|
|
809
|
+
}
|
|
401
810
|
get connection() {
|
|
402
811
|
return this._connection;
|
|
403
812
|
}
|
|
@@ -463,6 +872,8 @@ var RpcPeer = class {
|
|
|
463
872
|
}
|
|
464
873
|
close() {
|
|
465
874
|
this._stopKeepAlive();
|
|
875
|
+
this.remoteObjects.disconnectAll();
|
|
876
|
+
this.sharedObjects.disconnectAll();
|
|
466
877
|
for (const call of this._pendingSends) {
|
|
467
878
|
if (!call.result.isCompleted)
|
|
468
879
|
call.result.reject(new Error("Peer closed."));
|
|
@@ -496,7 +907,7 @@ var RpcPeer = class {
|
|
|
496
907
|
return;
|
|
497
908
|
}
|
|
498
909
|
if (method.startsWith("$sys")) {
|
|
499
|
-
this._hub.
|
|
910
|
+
this._hub.systemCallHandler.handle(message, args, this);
|
|
500
911
|
return;
|
|
501
912
|
}
|
|
502
913
|
this._handleInbound(message, args);
|
|
@@ -516,7 +927,12 @@ var RpcPeer = class {
|
|
|
516
927
|
try {
|
|
517
928
|
const context = this._connection !== void 0 ? { __rpcDispatch: true, callId: relatedId, connection: this._connection } : void 0;
|
|
518
929
|
const result = await serviceHost.dispatch(method, args, context);
|
|
519
|
-
if (
|
|
930
|
+
if (methodDef?.stream === true && this._connection !== void 0) {
|
|
931
|
+
const sender = new RpcStreamSender(this);
|
|
932
|
+
this.sharedObjects.register(sender);
|
|
933
|
+
this._hub.systemCallSender.ok(this._connection, relatedId, sender.toRef());
|
|
934
|
+
void sender.writeFrom(result);
|
|
935
|
+
} else if (!isNoWait && this._connection !== void 0) {
|
|
520
936
|
this._hub.systemCallSender.ok(this._connection, relatedId, result);
|
|
521
937
|
}
|
|
522
938
|
} catch (e) {
|
|
@@ -601,7 +1017,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
601
1017
|
const ws = wsFactory?.(connUrl) ?? new WebSocket(connUrl);
|
|
602
1018
|
const conn = new RpcWebSocketConnection(ws);
|
|
603
1019
|
this._connectionKind = 1 /* Connecting */;
|
|
604
|
-
this._pendingHandshake = new
|
|
1020
|
+
this._pendingHandshake = new PromiseSource5();
|
|
605
1021
|
this.setupConnection(conn);
|
|
606
1022
|
this._connection = void 0;
|
|
607
1023
|
const closedRejection = new Promise((_, reject) => conn.closed.add(() => reject(new Error("Connection failed"))));
|
|
@@ -657,6 +1073,11 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
657
1073
|
_reconnect(_isPeerChanged) {
|
|
658
1074
|
if (this._connection === void 0) return;
|
|
659
1075
|
const conn = this._connection;
|
|
1076
|
+
if (_isPeerChanged) {
|
|
1077
|
+
this.remoteObjects.disconnectAll();
|
|
1078
|
+
} else {
|
|
1079
|
+
this.remoteObjects.reconnectAll();
|
|
1080
|
+
}
|
|
660
1081
|
const trackerCalls = [...this.outbound.values()];
|
|
661
1082
|
for (const call of trackerCalls) {
|
|
662
1083
|
if (!call.removeOnOk && call.result.isCompleted) {
|
|
@@ -739,12 +1160,10 @@ function getMethodsMeta(cls) {
|
|
|
739
1160
|
function getServiceMeta(cls) {
|
|
740
1161
|
return cls[Symbol.metadata]?.[SERVICE_META];
|
|
741
1162
|
}
|
|
742
|
-
function rpcService(serviceName
|
|
1163
|
+
function rpcService(serviceName) {
|
|
743
1164
|
return function(_target, context) {
|
|
744
1165
|
const meta = context.metadata[SERVICE_META] ??= {};
|
|
745
1166
|
meta.name = serviceName;
|
|
746
|
-
if (options?.ctOffset !== void 0)
|
|
747
|
-
meta.ctOffset = options.ctOffset;
|
|
748
1167
|
};
|
|
749
1168
|
}
|
|
750
1169
|
function rpcMethod(options) {
|
|
@@ -754,8 +1173,7 @@ function rpcMethod(options) {
|
|
|
754
1173
|
methods[methodName] = {
|
|
755
1174
|
...methods[methodName],
|
|
756
1175
|
argCount: target.length,
|
|
757
|
-
|
|
758
|
-
noWait: options?.noWait ?? false
|
|
1176
|
+
returns: options?.returns
|
|
759
1177
|
};
|
|
760
1178
|
return target;
|
|
761
1179
|
};
|
|
@@ -767,6 +1185,7 @@ var RpcHub = class {
|
|
|
767
1185
|
peers = /* @__PURE__ */ new Map();
|
|
768
1186
|
serviceHost;
|
|
769
1187
|
systemCallSender = new RpcSystemCallSender();
|
|
1188
|
+
systemCallHandler = new RpcSystemCallHandler();
|
|
770
1189
|
constructor(hubId) {
|
|
771
1190
|
this.hubId = hubId ?? crypto.randomUUID();
|
|
772
1191
|
this.serviceHost = new RpcServiceHost();
|
|
@@ -840,19 +1259,14 @@ var RpcHub = class {
|
|
|
840
1259
|
for (const peer of this.peers.values()) peer.close();
|
|
841
1260
|
this.peers.clear();
|
|
842
1261
|
}
|
|
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
1262
|
/** Build an RpcServiceDef from decorator metadata on a contract class. Override in FusionHub for compute support. */
|
|
848
1263
|
_buildServiceDef(cls) {
|
|
849
1264
|
const svcMeta = getServiceMeta(cls);
|
|
850
1265
|
if (svcMeta === void 0) throw new Error("Contract class missing @rpcService metadata");
|
|
851
1266
|
const methodsMeta = getMethodsMeta(cls) ?? {};
|
|
852
|
-
const ctOffset = svcMeta.ctOffset ?? 0;
|
|
853
1267
|
const methods = /* @__PURE__ */ new Map();
|
|
854
1268
|
for (const [name, meta] of Object.entries(methodsMeta)) {
|
|
855
|
-
const wireArgCount = meta.argCount +
|
|
1269
|
+
const wireArgCount = meta.argCount + 1;
|
|
856
1270
|
const mapKey = `${name}:${wireArgCount}`;
|
|
857
1271
|
methods.set(mapKey, {
|
|
858
1272
|
name,
|
|
@@ -860,8 +1274,8 @@ var RpcHub = class {
|
|
|
860
1274
|
argCount: meta.argCount,
|
|
861
1275
|
wireArgCount,
|
|
862
1276
|
callTypeId: 0,
|
|
863
|
-
stream: meta.
|
|
864
|
-
noWait: meta.
|
|
1277
|
+
stream: meta.returns === RpcType.stream,
|
|
1278
|
+
noWait: meta.returns === RpcType.noWait
|
|
865
1279
|
});
|
|
866
1280
|
}
|
|
867
1281
|
return { name: svcMeta.name, methods };
|
|
@@ -885,11 +1299,24 @@ var RpcHub = class {
|
|
|
885
1299
|
peer.callNoWait(wireName, callArgs);
|
|
886
1300
|
};
|
|
887
1301
|
}
|
|
1302
|
+
if (methodDef.stream) {
|
|
1303
|
+
return async (...args) => {
|
|
1304
|
+
const callArgs = args.slice(0, methodDef.argCount);
|
|
1305
|
+
const outboundCall = peer.call(wireName, callArgs);
|
|
1306
|
+
const result = await outboundCall.result.promise;
|
|
1307
|
+
const ref = parseStreamRef(result);
|
|
1308
|
+
if (!ref) throw new Error(`Expected stream reference, got: ${JSON.stringify(result)}`);
|
|
1309
|
+
const stream = new RpcStream(ref, peer);
|
|
1310
|
+
peer.remoteObjects.register(stream);
|
|
1311
|
+
return stream;
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
888
1314
|
const callOptions = methodDef.callTypeId !== 0 ? { callTypeId: methodDef.callTypeId } : void 0;
|
|
889
1315
|
return async (...args) => {
|
|
890
1316
|
const callArgs = args.slice(0, methodDef.argCount);
|
|
891
1317
|
const outboundCall = peer.call(wireName, callArgs, callOptions);
|
|
892
|
-
|
|
1318
|
+
const result = await outboundCall.result.promise;
|
|
1319
|
+
return resolveStreamRefs(result, peer);
|
|
893
1320
|
};
|
|
894
1321
|
}
|
|
895
1322
|
};
|
|
@@ -1099,14 +1526,20 @@ export {
|
|
|
1099
1526
|
RpcInboundCall,
|
|
1100
1527
|
RpcInboundCallTracker,
|
|
1101
1528
|
RpcMessageChannelConnection,
|
|
1529
|
+
RpcObjectKind,
|
|
1102
1530
|
RpcOutboundCall,
|
|
1103
1531
|
RpcOutboundCallTracker,
|
|
1104
1532
|
RpcPeer,
|
|
1105
1533
|
RpcPeerConnectionKind,
|
|
1106
1534
|
RpcPeerStateKind,
|
|
1107
1535
|
RpcPeerStateMonitor,
|
|
1536
|
+
RpcRemoteObjectTracker,
|
|
1108
1537
|
RpcServerPeer,
|
|
1109
1538
|
RpcServiceHost,
|
|
1539
|
+
RpcSharedObjectTracker,
|
|
1540
|
+
RpcStream,
|
|
1541
|
+
RpcStreamSender,
|
|
1542
|
+
RpcSystemCallHandler,
|
|
1110
1543
|
RpcSystemCallSender,
|
|
1111
1544
|
RpcSystemCalls,
|
|
1112
1545
|
RpcType,
|
|
@@ -1120,9 +1553,10 @@ export {
|
|
|
1120
1553
|
getMethodsMeta,
|
|
1121
1554
|
getServiceMeta,
|
|
1122
1555
|
getStateDescription,
|
|
1123
|
-
handleSystemCall,
|
|
1124
1556
|
isConnected,
|
|
1125
1557
|
likelyConnected,
|
|
1558
|
+
parseStreamRef,
|
|
1559
|
+
resolveStreamRefs,
|
|
1126
1560
|
rpcMethod,
|
|
1127
1561
|
rpcService,
|
|
1128
1562
|
serializeFrame,
|