@actuallab/rpc 12.2.23 → 12.3.4
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 +344 -256
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +78 -40
- package/dist/index.d.ts +78 -40
- package/dist/index.js +344 -256
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -645,12 +645,200 @@ var RpcInboundCallTracker = class {
|
|
|
645
645
|
|
|
646
646
|
// src/rpc-stream.ts
|
|
647
647
|
import Denque from "denque";
|
|
648
|
-
import { PromiseSource as
|
|
648
|
+
import { PromiseSource as PromiseSource4 } from "@actuallab/core";
|
|
649
649
|
|
|
650
650
|
// src/rpc-object.ts
|
|
651
651
|
var RpcObjectKind = { Local: 0, Remote: 1 };
|
|
652
652
|
|
|
653
|
+
// src/rpc-stream-sender.ts
|
|
654
|
+
import { PromiseSource as PromiseSource3 } from "@actuallab/core";
|
|
655
|
+
var DEFAULT_ACK_PERIOD = 256;
|
|
656
|
+
var DEFAULT_ACK_ADVANCE = 128;
|
|
657
|
+
var RpcStreamSender = class _RpcStreamSender {
|
|
658
|
+
static _emptyGuid = "00000000-0000-0000-0000-000000000000";
|
|
659
|
+
id;
|
|
660
|
+
kind = RpcObjectKind.Local;
|
|
661
|
+
allowReconnect;
|
|
662
|
+
isRealTime;
|
|
663
|
+
canSkipTo;
|
|
664
|
+
peer;
|
|
665
|
+
ackPeriod;
|
|
666
|
+
ackAdvance;
|
|
667
|
+
_nextIndex = 0;
|
|
668
|
+
_ended = false;
|
|
669
|
+
_started = new PromiseSource3();
|
|
670
|
+
_lastAckedIndex = 0;
|
|
671
|
+
_ackWaiting = null;
|
|
672
|
+
_resetRequested = false;
|
|
673
|
+
constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, ackAdvance = DEFAULT_ACK_ADVANCE, allowReconnect = true, isRealTime = false, canSkipTo = () => true) {
|
|
674
|
+
const localId = peer.sharedObjects.nextId();
|
|
675
|
+
this.id = { hostId: peer.hub.hubId, localId };
|
|
676
|
+
this.allowReconnect = allowReconnect;
|
|
677
|
+
this.isRealTime = isRealTime;
|
|
678
|
+
this.canSkipTo = canSkipTo;
|
|
679
|
+
this.peer = peer;
|
|
680
|
+
this.ackPeriod = ackPeriod;
|
|
681
|
+
this.ackAdvance = ackAdvance;
|
|
682
|
+
}
|
|
683
|
+
/** Returns the stream reference string for the $sys.Ok response. */
|
|
684
|
+
toRef() {
|
|
685
|
+
return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance},${this.allowReconnect ? "1" : "0"},${this.isRealTime ? "1" : "0"}`;
|
|
686
|
+
}
|
|
687
|
+
/** Called by system call handler when $sys.Ack is received from the client. */
|
|
688
|
+
onAck(nextIndex, hostId) {
|
|
689
|
+
const mustReset = hostId !== "" && hostId !== _RpcStreamSender._emptyGuid;
|
|
690
|
+
if (!this._started.isCompleted) {
|
|
691
|
+
this._started.resolve();
|
|
692
|
+
}
|
|
693
|
+
if (mustReset && this.isRealTime) {
|
|
694
|
+
this._nextIndex = nextIndex;
|
|
695
|
+
this._lastAckedIndex = nextIndex;
|
|
696
|
+
this._resetRequested = true;
|
|
697
|
+
} else {
|
|
698
|
+
if (mustReset || nextIndex > this._lastAckedIndex) {
|
|
699
|
+
this._lastAckedIndex = nextIndex;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
if (this._ackWaiting) {
|
|
703
|
+
this._ackWaiting.resolve();
|
|
704
|
+
this._ackWaiting = null;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
/** Called by system call handler when $sys.AckEnd is received from the client. */
|
|
708
|
+
onAckEnd(_hostId) {
|
|
709
|
+
this._ended = true;
|
|
710
|
+
this.peer.sharedObjects.unregister(this);
|
|
711
|
+
}
|
|
712
|
+
/** Send a single item to the client. */
|
|
713
|
+
sendItem(item) {
|
|
714
|
+
if (this._ended) return;
|
|
715
|
+
const conn = this.peer.connection;
|
|
716
|
+
if (!conn) return;
|
|
717
|
+
this.peer.hub.systemCallSender.item(
|
|
718
|
+
conn,
|
|
719
|
+
this.peer.format,
|
|
720
|
+
this.id.localId,
|
|
721
|
+
this._nextIndex,
|
|
722
|
+
item
|
|
723
|
+
);
|
|
724
|
+
this._nextIndex++;
|
|
725
|
+
}
|
|
726
|
+
/** Send a batch of items to the client. */
|
|
727
|
+
sendBatch(items) {
|
|
728
|
+
if (this._ended || items.length === 0) return;
|
|
729
|
+
const conn = this.peer.connection;
|
|
730
|
+
if (!conn) return;
|
|
731
|
+
this.peer.hub.systemCallSender.batch(
|
|
732
|
+
conn,
|
|
733
|
+
this.peer.format,
|
|
734
|
+
this.id.localId,
|
|
735
|
+
this._nextIndex,
|
|
736
|
+
items
|
|
737
|
+
);
|
|
738
|
+
this._nextIndex += items.length;
|
|
739
|
+
}
|
|
740
|
+
/** Signal stream completion to the client. */
|
|
741
|
+
sendEnd(error) {
|
|
742
|
+
if (this._ended) return;
|
|
743
|
+
this._ended = true;
|
|
744
|
+
const conn = this.peer.connection;
|
|
745
|
+
if (!conn) return;
|
|
746
|
+
const errorInfo = error ? { TypeRef: "System.Exception", Message: error.message } : { TypeRef: "", Message: "" };
|
|
747
|
+
this.peer.hub.systemCallSender.end(
|
|
748
|
+
conn,
|
|
749
|
+
this.peer.format,
|
|
750
|
+
this.id.localId,
|
|
751
|
+
this._nextIndex,
|
|
752
|
+
errorInfo
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Consume an AsyncIterable and send all items to the client.
|
|
757
|
+
* Waits for the client's initial Ack before starting to pump items.
|
|
758
|
+
*
|
|
759
|
+
* When `isRealTime` is true, applies flow control based on `ackAdvance`:
|
|
760
|
+
* if the sender is `ackAdvance` items ahead of the last ACK, it enters
|
|
761
|
+
* skip mode — draining the source and discarding items until it finds
|
|
762
|
+
* one where `canSkipTo` returns true.
|
|
763
|
+
*/
|
|
764
|
+
async writeFrom(source) {
|
|
765
|
+
await this._started.promise;
|
|
766
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
767
|
+
try {
|
|
768
|
+
for (; ; ) {
|
|
769
|
+
const next = await iterator.next();
|
|
770
|
+
if (next.done || this._ended) break;
|
|
771
|
+
const item = next.value;
|
|
772
|
+
if (this._resetRequested) {
|
|
773
|
+
if (!this.canSkipTo(item)) continue;
|
|
774
|
+
this._resetRequested = false;
|
|
775
|
+
this.sendItem(item);
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
if (this._nextIndex < this._lastAckedIndex + this.ackAdvance) {
|
|
779
|
+
this.sendItem(item);
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
if (!this.isRealTime) {
|
|
783
|
+
await this._waitForAckBudget();
|
|
784
|
+
if (this._ended) return;
|
|
785
|
+
this.sendItem(item);
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
let latestSkipTarget = this.canSkipTo(item) ? item : void 0;
|
|
789
|
+
let sourceExhausted = false;
|
|
790
|
+
while (this._nextIndex >= this._lastAckedIndex + this.ackAdvance) {
|
|
791
|
+
if (this._ended) return;
|
|
792
|
+
const n = await iterator.next();
|
|
793
|
+
if (n.done) {
|
|
794
|
+
sourceExhausted = true;
|
|
795
|
+
break;
|
|
796
|
+
}
|
|
797
|
+
if (this.canSkipTo(n.value)) {
|
|
798
|
+
latestSkipTarget = n.value;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
if (this._ended) return;
|
|
802
|
+
if (latestSkipTarget !== void 0) {
|
|
803
|
+
this.sendItem(latestSkipTarget);
|
|
804
|
+
}
|
|
805
|
+
if (sourceExhausted) {
|
|
806
|
+
if (!this._ended) this.sendEnd();
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
if (!this._ended) {
|
|
811
|
+
this.sendEnd();
|
|
812
|
+
}
|
|
813
|
+
} catch (e) {
|
|
814
|
+
if (!this._ended) {
|
|
815
|
+
this.sendEnd(e instanceof Error ? e : new Error(String(e)));
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
/** Wait until _nextIndex < _lastAckedIndex + ackAdvance. */
|
|
820
|
+
async _waitForAckBudget() {
|
|
821
|
+
while (this._nextIndex >= this._lastAckedIndex + this.ackAdvance) {
|
|
822
|
+
if (this._ended) return;
|
|
823
|
+
this._ackWaiting = new PromiseSource3();
|
|
824
|
+
await this._ackWaiting.promise;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
// -- IRpcObject --
|
|
828
|
+
reconnect() {
|
|
829
|
+
}
|
|
830
|
+
disconnect() {
|
|
831
|
+
this._ended = true;
|
|
832
|
+
if (!this._started.isCompleted) {
|
|
833
|
+
this._started.resolve();
|
|
834
|
+
}
|
|
835
|
+
this.peer.sharedObjects.unregister(this);
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
|
|
653
839
|
// src/rpc-stream.ts
|
|
840
|
+
var DEFAULT_ACK_PERIOD2 = 30;
|
|
841
|
+
var DEFAULT_ACK_ADVANCE2 = 61;
|
|
654
842
|
function parseStreamRef(value) {
|
|
655
843
|
if (typeof value === "string") {
|
|
656
844
|
const parts = value.split(",");
|
|
@@ -683,17 +871,21 @@ function parseStreamRef(value) {
|
|
|
683
871
|
}
|
|
684
872
|
var RpcStream = class _RpcStream {
|
|
685
873
|
id;
|
|
686
|
-
kind
|
|
874
|
+
kind;
|
|
687
875
|
allowReconnect;
|
|
688
|
-
|
|
876
|
+
isRealTime;
|
|
877
|
+
canSkipTo;
|
|
689
878
|
ackPeriod;
|
|
690
879
|
ackAdvance;
|
|
691
|
-
//
|
|
692
|
-
|
|
693
|
-
//
|
|
694
|
-
|
|
695
|
-
//
|
|
696
|
-
|
|
880
|
+
// Local (origin) mode
|
|
881
|
+
localSource;
|
|
882
|
+
// Remote (target) mode
|
|
883
|
+
peer;
|
|
884
|
+
// Local-only state
|
|
885
|
+
_sender;
|
|
886
|
+
_toRefValue;
|
|
887
|
+
_whenSent;
|
|
888
|
+
// Remote-only state
|
|
697
889
|
_buffer = new Denque();
|
|
698
890
|
_nextExpectedIndex = 0;
|
|
699
891
|
_consumerWaiting = null;
|
|
@@ -703,12 +895,76 @@ var RpcStream = class _RpcStream {
|
|
|
703
895
|
_started = false;
|
|
704
896
|
_iterating = false;
|
|
705
897
|
_ackSentUpTo = 0;
|
|
706
|
-
constructor(
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
898
|
+
constructor(sourceOrRef, optionsOrPeer) {
|
|
899
|
+
if (_isAsyncIterable(sourceOrRef)) {
|
|
900
|
+
const options = optionsOrPeer ?? {};
|
|
901
|
+
this.kind = RpcObjectKind.Local;
|
|
902
|
+
this.localSource = sourceOrRef;
|
|
903
|
+
this.id = { hostId: "", localId: 0 };
|
|
904
|
+
this.allowReconnect = options.allowReconnect ?? true;
|
|
905
|
+
this.isRealTime = options.isRealTime ?? false;
|
|
906
|
+
this.canSkipTo = options.canSkipTo ?? (() => true);
|
|
907
|
+
this.ackPeriod = options.ackPeriod ?? DEFAULT_ACK_PERIOD2;
|
|
908
|
+
this.ackAdvance = options.ackAdvance ?? DEFAULT_ACK_ADVANCE2;
|
|
909
|
+
} else {
|
|
910
|
+
const ref = sourceOrRef;
|
|
911
|
+
const peer = optionsOrPeer;
|
|
912
|
+
this.kind = RpcObjectKind.Remote;
|
|
913
|
+
this.id = { hostId: ref.hostId, localId: ref.localId };
|
|
914
|
+
this.peer = peer;
|
|
915
|
+
this.allowReconnect = ref.allowReconnect;
|
|
916
|
+
this.isRealTime = ref.isRealTime;
|
|
917
|
+
this.canSkipTo = () => true;
|
|
918
|
+
this.ackPeriod = ref.ackPeriod;
|
|
919
|
+
this.ackAdvance = ref.ackAdvance;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* For local streams: resolves when the sender finishes pumping (after `toRef()` was called).
|
|
924
|
+
* Rejects if the source throws. Throws if `toRef()` hasn't been called yet.
|
|
925
|
+
*/
|
|
926
|
+
get whenSent() {
|
|
927
|
+
if (!this._whenSent)
|
|
928
|
+
throw new Error("toRef() must be called before awaiting whenSent.");
|
|
929
|
+
return this._whenSent;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* For local streams: bind to a peer, create and register an `RpcStreamSender`,
|
|
933
|
+
* start pumping items from the local source in the background, and return the
|
|
934
|
+
* stream reference.
|
|
935
|
+
*
|
|
936
|
+
* For binary peers the ref is a MessagePack-compatible object
|
|
937
|
+
* (`{ SerializedId, AckPeriod, AckAdvance, AllowReconnect }`);
|
|
938
|
+
* for text peers it is a comma-separated string.
|
|
939
|
+
*
|
|
940
|
+
* Can only be called once per stream.
|
|
941
|
+
*/
|
|
942
|
+
toRef(peer) {
|
|
943
|
+
if (this.kind !== RpcObjectKind.Local)
|
|
944
|
+
throw new Error("toRef() is only valid for local streams.");
|
|
945
|
+
if (this._sender) {
|
|
946
|
+
if (this._sender.peer !== peer)
|
|
947
|
+
throw new Error("toRef() was already called with a different peer.");
|
|
948
|
+
return this._toRefValue;
|
|
949
|
+
}
|
|
950
|
+
const sender = new RpcStreamSender(
|
|
951
|
+
peer,
|
|
952
|
+
this.ackPeriod,
|
|
953
|
+
this.ackAdvance,
|
|
954
|
+
this.allowReconnect,
|
|
955
|
+
this.isRealTime,
|
|
956
|
+
this.canSkipTo
|
|
957
|
+
);
|
|
958
|
+
peer.sharedObjects.register(sender);
|
|
959
|
+
this._sender = sender;
|
|
960
|
+
this._whenSent = sender.writeFrom(this.localSource);
|
|
961
|
+
this._toRefValue = peer.format.isBinary ? {
|
|
962
|
+
SerializedId: [sender.id.hostId, sender.id.localId],
|
|
963
|
+
AckPeriod: sender.ackPeriod,
|
|
964
|
+
AckAdvance: sender.ackAdvance,
|
|
965
|
+
AllowReconnect: sender.allowReconnect
|
|
966
|
+
} : sender.toRef();
|
|
967
|
+
return this._toRefValue;
|
|
712
968
|
}
|
|
713
969
|
/** Called by system call handler when a single item arrives ($sys.I). */
|
|
714
970
|
onItem(index, item) {
|
|
@@ -773,6 +1029,7 @@ var RpcStream = class _RpcStream {
|
|
|
773
1029
|
}
|
|
774
1030
|
// -- IRpcObject --
|
|
775
1031
|
reconnect() {
|
|
1032
|
+
if (this.kind === RpcObjectKind.Local) return;
|
|
776
1033
|
if (this.allowReconnect) {
|
|
777
1034
|
this._sendAck(this._nextExpectedIndex, true);
|
|
778
1035
|
} else {
|
|
@@ -780,6 +1037,7 @@ var RpcStream = class _RpcStream {
|
|
|
780
1037
|
}
|
|
781
1038
|
}
|
|
782
1039
|
disconnect() {
|
|
1040
|
+
if (this.kind === RpcObjectKind.Local) return;
|
|
783
1041
|
if (this._completed) return;
|
|
784
1042
|
this._completed = true;
|
|
785
1043
|
this._completionError = new Error("Peer disconnected.");
|
|
@@ -787,33 +1045,35 @@ var RpcStream = class _RpcStream {
|
|
|
787
1045
|
}
|
|
788
1046
|
// -- AsyncIterable --
|
|
789
1047
|
[Symbol.asyncIterator]() {
|
|
1048
|
+
if (this.kind === RpcObjectKind.Local) {
|
|
1049
|
+
return this.localSource[Symbol.asyncIterator]();
|
|
1050
|
+
}
|
|
790
1051
|
if (this._iterating)
|
|
791
1052
|
throw new Error("RpcStream can only be iterated once.");
|
|
792
1053
|
this._iterating = true;
|
|
793
|
-
const self = this;
|
|
794
1054
|
return {
|
|
795
|
-
async
|
|
796
|
-
if (!
|
|
797
|
-
|
|
798
|
-
|
|
1055
|
+
next: async () => {
|
|
1056
|
+
if (!this._started) {
|
|
1057
|
+
this._started = true;
|
|
1058
|
+
this._sendAck(0, true);
|
|
799
1059
|
}
|
|
800
|
-
|
|
801
|
-
if (!
|
|
802
|
-
const value =
|
|
1060
|
+
for (; ; ) {
|
|
1061
|
+
if (!this._buffer.isEmpty()) {
|
|
1062
|
+
const value = this._buffer.shift();
|
|
803
1063
|
return { value, done: false };
|
|
804
1064
|
}
|
|
805
|
-
if (
|
|
806
|
-
if (
|
|
1065
|
+
if (this._completed) {
|
|
1066
|
+
if (this._completionError) throw this._completionError;
|
|
807
1067
|
return { value: void 0, done: true };
|
|
808
1068
|
}
|
|
809
|
-
|
|
810
|
-
await
|
|
1069
|
+
this._consumerWaiting = new PromiseSource4();
|
|
1070
|
+
await this._consumerWaiting.promise;
|
|
811
1071
|
}
|
|
812
1072
|
},
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
return { value: void 0, done: true };
|
|
1073
|
+
return: () => {
|
|
1074
|
+
this._sendAckEnd();
|
|
1075
|
+
this.dispose();
|
|
1076
|
+
return Promise.resolve({ value: void 0, done: true });
|
|
817
1077
|
}
|
|
818
1078
|
};
|
|
819
1079
|
}
|
|
@@ -821,7 +1081,9 @@ var RpcStream = class _RpcStream {
|
|
|
821
1081
|
if (this._disposed) return;
|
|
822
1082
|
this._disposed = true;
|
|
823
1083
|
this._completed = true;
|
|
824
|
-
this.
|
|
1084
|
+
if (this.kind === RpcObjectKind.Remote) {
|
|
1085
|
+
this.peer.remoteObjects.unregister(this);
|
|
1086
|
+
}
|
|
825
1087
|
this._notifyConsumer();
|
|
826
1088
|
}
|
|
827
1089
|
// -- Private --
|
|
@@ -865,6 +1127,9 @@ var RpcStream = class _RpcStream {
|
|
|
865
1127
|
}
|
|
866
1128
|
}
|
|
867
1129
|
};
|
|
1130
|
+
function _isAsyncIterable(value) {
|
|
1131
|
+
return value !== null && typeof value === "object" && Symbol.asyncIterator in value;
|
|
1132
|
+
}
|
|
868
1133
|
function resolveStreamRefs(value, peer) {
|
|
869
1134
|
if (value === null || value === void 0) return value;
|
|
870
1135
|
if (typeof value === "string") {
|
|
@@ -1119,177 +1384,6 @@ var RpcSystemCallSender = class _RpcSystemCallSender {
|
|
|
1119
1384
|
}
|
|
1120
1385
|
};
|
|
1121
1386
|
|
|
1122
|
-
// src/rpc-stream-sender.ts
|
|
1123
|
-
import { PromiseSource as PromiseSource4 } from "@actuallab/core";
|
|
1124
|
-
var DEFAULT_ACK_PERIOD = 256;
|
|
1125
|
-
var DEFAULT_ACK_ADVANCE = 128;
|
|
1126
|
-
var RpcStreamSender = class {
|
|
1127
|
-
id;
|
|
1128
|
-
kind = RpcObjectKind.Local;
|
|
1129
|
-
allowReconnect;
|
|
1130
|
-
isRealTime;
|
|
1131
|
-
canSkipTo;
|
|
1132
|
-
peer;
|
|
1133
|
-
ackPeriod;
|
|
1134
|
-
ackAdvance;
|
|
1135
|
-
_nextIndex = 0;
|
|
1136
|
-
_ended = false;
|
|
1137
|
-
_started = new PromiseSource4();
|
|
1138
|
-
_lastAckedIndex = 0;
|
|
1139
|
-
_ackWaiting = null;
|
|
1140
|
-
constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, ackAdvance = DEFAULT_ACK_ADVANCE, allowReconnect = true, isRealTime = false, canSkipTo = () => true) {
|
|
1141
|
-
const localId = peer.sharedObjects.nextId();
|
|
1142
|
-
this.id = { hostId: peer.hub.hubId, localId };
|
|
1143
|
-
this.allowReconnect = allowReconnect;
|
|
1144
|
-
this.isRealTime = isRealTime;
|
|
1145
|
-
this.canSkipTo = canSkipTo;
|
|
1146
|
-
this.peer = peer;
|
|
1147
|
-
this.ackPeriod = ackPeriod;
|
|
1148
|
-
this.ackAdvance = ackAdvance;
|
|
1149
|
-
}
|
|
1150
|
-
/** Returns the stream reference string for the $sys.Ok response. */
|
|
1151
|
-
toRef() {
|
|
1152
|
-
return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance},${this.allowReconnect ? "1" : "0"},${this.isRealTime ? "1" : "0"}`;
|
|
1153
|
-
}
|
|
1154
|
-
/** Called by system call handler when $sys.Ack is received from the client. */
|
|
1155
|
-
onAck(nextIndex, _hostId) {
|
|
1156
|
-
if (!this._started.isCompleted) {
|
|
1157
|
-
this._started.resolve();
|
|
1158
|
-
}
|
|
1159
|
-
if (nextIndex > this._lastAckedIndex) {
|
|
1160
|
-
this._lastAckedIndex = nextIndex;
|
|
1161
|
-
}
|
|
1162
|
-
if (this._ackWaiting) {
|
|
1163
|
-
this._ackWaiting.resolve();
|
|
1164
|
-
this._ackWaiting = null;
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
/** Called by system call handler when $sys.AckEnd is received from the client. */
|
|
1168
|
-
onAckEnd(_hostId) {
|
|
1169
|
-
this._ended = true;
|
|
1170
|
-
this.peer.sharedObjects.unregister(this);
|
|
1171
|
-
}
|
|
1172
|
-
/** Send a single item to the client. */
|
|
1173
|
-
sendItem(item) {
|
|
1174
|
-
if (this._ended) return;
|
|
1175
|
-
const conn = this.peer.connection;
|
|
1176
|
-
if (!conn) return;
|
|
1177
|
-
this.peer.hub.systemCallSender.item(
|
|
1178
|
-
conn,
|
|
1179
|
-
this.peer.format,
|
|
1180
|
-
this.id.localId,
|
|
1181
|
-
this._nextIndex,
|
|
1182
|
-
item
|
|
1183
|
-
);
|
|
1184
|
-
this._nextIndex++;
|
|
1185
|
-
}
|
|
1186
|
-
/** Send a batch of items to the client. */
|
|
1187
|
-
sendBatch(items) {
|
|
1188
|
-
if (this._ended || items.length === 0) return;
|
|
1189
|
-
const conn = this.peer.connection;
|
|
1190
|
-
if (!conn) return;
|
|
1191
|
-
this.peer.hub.systemCallSender.batch(
|
|
1192
|
-
conn,
|
|
1193
|
-
this.peer.format,
|
|
1194
|
-
this.id.localId,
|
|
1195
|
-
this._nextIndex,
|
|
1196
|
-
items
|
|
1197
|
-
);
|
|
1198
|
-
this._nextIndex += items.length;
|
|
1199
|
-
}
|
|
1200
|
-
/** Signal stream completion to the client. */
|
|
1201
|
-
sendEnd(error) {
|
|
1202
|
-
if (this._ended) return;
|
|
1203
|
-
this._ended = true;
|
|
1204
|
-
const conn = this.peer.connection;
|
|
1205
|
-
if (!conn) return;
|
|
1206
|
-
const errorInfo = error ? { TypeRef: "System.Exception", Message: error.message } : { TypeRef: "", Message: "" };
|
|
1207
|
-
this.peer.hub.systemCallSender.end(
|
|
1208
|
-
conn,
|
|
1209
|
-
this.peer.format,
|
|
1210
|
-
this.id.localId,
|
|
1211
|
-
this._nextIndex,
|
|
1212
|
-
errorInfo
|
|
1213
|
-
);
|
|
1214
|
-
}
|
|
1215
|
-
/**
|
|
1216
|
-
* Consume an AsyncIterable and send all items to the client.
|
|
1217
|
-
* Waits for the client's initial Ack before starting to pump items.
|
|
1218
|
-
*
|
|
1219
|
-
* When `isRealTime` is true, applies flow control based on `ackAdvance`:
|
|
1220
|
-
* if the sender is `ackAdvance` items ahead of the last ACK, it enters
|
|
1221
|
-
* skip mode — draining the source and discarding items until it finds
|
|
1222
|
-
* one where `canSkipTo` returns true.
|
|
1223
|
-
*/
|
|
1224
|
-
async writeFrom(source) {
|
|
1225
|
-
await this._started.promise;
|
|
1226
|
-
const iterator = source[Symbol.asyncIterator]();
|
|
1227
|
-
try {
|
|
1228
|
-
while (true) {
|
|
1229
|
-
const next = await iterator.next();
|
|
1230
|
-
if (next.done || this._ended) break;
|
|
1231
|
-
const item = next.value;
|
|
1232
|
-
if (this._nextIndex < this._lastAckedIndex + this.ackAdvance) {
|
|
1233
|
-
this.sendItem(item);
|
|
1234
|
-
continue;
|
|
1235
|
-
}
|
|
1236
|
-
if (!this.isRealTime) {
|
|
1237
|
-
await this._waitForAckBudget();
|
|
1238
|
-
if (this._ended) return;
|
|
1239
|
-
this.sendItem(item);
|
|
1240
|
-
continue;
|
|
1241
|
-
}
|
|
1242
|
-
let latestSkipTarget = this.canSkipTo(item) ? item : void 0;
|
|
1243
|
-
let sourceExhausted = false;
|
|
1244
|
-
while (this._nextIndex >= this._lastAckedIndex + this.ackAdvance) {
|
|
1245
|
-
if (this._ended) return;
|
|
1246
|
-
const n = await iterator.next();
|
|
1247
|
-
if (n.done) {
|
|
1248
|
-
sourceExhausted = true;
|
|
1249
|
-
break;
|
|
1250
|
-
}
|
|
1251
|
-
if (this.canSkipTo(n.value)) {
|
|
1252
|
-
latestSkipTarget = n.value;
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
if (this._ended) return;
|
|
1256
|
-
if (latestSkipTarget !== void 0) {
|
|
1257
|
-
this.sendItem(latestSkipTarget);
|
|
1258
|
-
}
|
|
1259
|
-
if (sourceExhausted) {
|
|
1260
|
-
if (!this._ended) this.sendEnd();
|
|
1261
|
-
return;
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
if (!this._ended) {
|
|
1265
|
-
this.sendEnd();
|
|
1266
|
-
}
|
|
1267
|
-
} catch (e) {
|
|
1268
|
-
if (!this._ended) {
|
|
1269
|
-
this.sendEnd(e instanceof Error ? e : new Error(String(e)));
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
/** Wait until _nextIndex < _lastAckedIndex + ackAdvance. */
|
|
1274
|
-
async _waitForAckBudget() {
|
|
1275
|
-
while (this._nextIndex >= this._lastAckedIndex + this.ackAdvance) {
|
|
1276
|
-
if (this._ended) return;
|
|
1277
|
-
this._ackWaiting = new PromiseSource4();
|
|
1278
|
-
await this._ackWaiting.promise;
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
// -- IRpcObject --
|
|
1282
|
-
reconnect() {
|
|
1283
|
-
}
|
|
1284
|
-
disconnect() {
|
|
1285
|
-
this._ended = true;
|
|
1286
|
-
if (!this._started.isCompleted) {
|
|
1287
|
-
this._started.resolve();
|
|
1288
|
-
}
|
|
1289
|
-
this.peer.sharedObjects.unregister(this);
|
|
1290
|
-
}
|
|
1291
|
-
};
|
|
1292
|
-
|
|
1293
1387
|
// src/rpc-remote-object-tracker.ts
|
|
1294
1388
|
var RpcRemoteObjectTracker = class {
|
|
1295
1389
|
_objects = /* @__PURE__ */ new Map();
|
|
@@ -1588,21 +1682,18 @@ var RpcPeer = class {
|
|
|
1588
1682
|
}
|
|
1589
1683
|
const signal = options?.signal;
|
|
1590
1684
|
if (signal !== void 0) {
|
|
1591
|
-
const peer = this;
|
|
1592
|
-
const hub = this._hub;
|
|
1593
|
-
const tracker = this.outbound;
|
|
1594
1685
|
const onAbort = () => {
|
|
1595
|
-
if (
|
|
1686
|
+
if (this.outbound.remove(callId) !== void 0) {
|
|
1596
1687
|
outboundCall.result.reject(new Error("Call cancelled."));
|
|
1597
1688
|
outboundCall.onDisconnect();
|
|
1598
|
-
if (
|
|
1599
|
-
|
|
1689
|
+
if (this._connection !== void 0)
|
|
1690
|
+
this._hub.systemCallSender.cancel(this._connection, this.format, callId);
|
|
1600
1691
|
} else {
|
|
1601
|
-
const idx =
|
|
1692
|
+
const idx = this._pendingSends.findIndex(
|
|
1602
1693
|
(c) => c.callId === callId
|
|
1603
1694
|
);
|
|
1604
1695
|
if (idx !== -1) {
|
|
1605
|
-
|
|
1696
|
+
this._pendingSends.splice(idx, 1);
|
|
1606
1697
|
outboundCall.result.reject(
|
|
1607
1698
|
new Error("Call cancelled.")
|
|
1608
1699
|
);
|
|
@@ -1696,53 +1787,49 @@ var RpcPeer = class {
|
|
|
1696
1787
|
this.inbound.register(call);
|
|
1697
1788
|
}
|
|
1698
1789
|
const serviceHost = this._hub.serviceHost;
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1790
|
+
void (async () => {
|
|
1791
|
+
try {
|
|
1792
|
+
const context = this._connection !== void 0 ? {
|
|
1793
|
+
__rpcDispatch: true,
|
|
1794
|
+
callId: relatedId,
|
|
1795
|
+
connection: this._connection
|
|
1796
|
+
} : void 0;
|
|
1797
|
+
const result = await serviceHost.dispatch(
|
|
1798
|
+
method,
|
|
1799
|
+
args,
|
|
1800
|
+
context
|
|
1801
|
+
);
|
|
1802
|
+
if (methodDef?.stream === true && this._connection !== void 0) {
|
|
1803
|
+
const stream = result instanceof RpcStream ? result : new RpcStream(result);
|
|
1804
|
+
this._hub.systemCallSender.ok(
|
|
1805
|
+
this._connection,
|
|
1806
|
+
this.format,
|
|
1807
|
+
relatedId,
|
|
1808
|
+
stream.toRef(this)
|
|
1809
|
+
);
|
|
1810
|
+
} else if (!isNoWait && this._connection !== void 0) {
|
|
1811
|
+
this._hub.systemCallSender.ok(
|
|
1812
|
+
this._connection,
|
|
1813
|
+
this.format,
|
|
1814
|
+
relatedId,
|
|
1815
|
+
result
|
|
1711
1816
|
);
|
|
1712
|
-
if (methodDef?.stream === true && this._connection !== void 0) {
|
|
1713
|
-
const sender = new RpcStreamSender(this);
|
|
1714
|
-
this.sharedObjects.register(sender);
|
|
1715
|
-
this._hub.systemCallSender.ok(
|
|
1716
|
-
this._connection,
|
|
1717
|
-
this.format,
|
|
1718
|
-
relatedId,
|
|
1719
|
-
sender.toRef()
|
|
1720
|
-
);
|
|
1721
|
-
void sender.writeFrom(result);
|
|
1722
|
-
} else if (!isNoWait && this._connection !== void 0) {
|
|
1723
|
-
this._hub.systemCallSender.ok(
|
|
1724
|
-
this._connection,
|
|
1725
|
-
this.format,
|
|
1726
|
-
relatedId,
|
|
1727
|
-
result
|
|
1728
|
-
);
|
|
1729
|
-
}
|
|
1730
|
-
} catch (e) {
|
|
1731
|
-
if (!isNoWait && this._connection !== void 0) {
|
|
1732
|
-
this._hub.systemCallSender.error(
|
|
1733
|
-
this._connection,
|
|
1734
|
-
this.format,
|
|
1735
|
-
relatedId,
|
|
1736
|
-
e
|
|
1737
|
-
);
|
|
1738
|
-
}
|
|
1739
|
-
} finally {
|
|
1740
|
-
if (!isNoWait) {
|
|
1741
|
-
this.inbound.remove(relatedId);
|
|
1742
|
-
}
|
|
1743
1817
|
}
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1818
|
+
} catch (e) {
|
|
1819
|
+
if (!isNoWait && this._connection !== void 0) {
|
|
1820
|
+
this._hub.systemCallSender.error(
|
|
1821
|
+
this._connection,
|
|
1822
|
+
this.format,
|
|
1823
|
+
relatedId,
|
|
1824
|
+
e
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1827
|
+
} finally {
|
|
1828
|
+
if (!isNoWait) {
|
|
1829
|
+
this.inbound.remove(relatedId);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
})();
|
|
1746
1833
|
}
|
|
1747
1834
|
_startKeepAlive() {
|
|
1748
1835
|
this._keepAliveTimer = setInterval(() => {
|
|
@@ -2611,6 +2698,7 @@ var RpcHub = class {
|
|
|
2611
2698
|
this.registry.registerService(def.name, def.methods);
|
|
2612
2699
|
}
|
|
2613
2700
|
/** Create a typed client proxy for a service on a remote peer. */
|
|
2701
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- T is used for caller-specified proxy type
|
|
2614
2702
|
addClient(peer, defOrContract) {
|
|
2615
2703
|
const def = this._resolveServiceDef(defOrContract);
|
|
2616
2704
|
this.registry.registerService(def.name, def.methods);
|