@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.cjs
CHANGED
|
@@ -744,12 +744,200 @@ var RpcInboundCallTracker = class {
|
|
|
744
744
|
|
|
745
745
|
// src/rpc-stream.ts
|
|
746
746
|
var import_denque = __toESM(require("denque"), 1);
|
|
747
|
-
var
|
|
747
|
+
var import_core5 = require("@actuallab/core");
|
|
748
748
|
|
|
749
749
|
// src/rpc-object.ts
|
|
750
750
|
var RpcObjectKind = { Local: 0, Remote: 1 };
|
|
751
751
|
|
|
752
|
+
// src/rpc-stream-sender.ts
|
|
753
|
+
var import_core4 = require("@actuallab/core");
|
|
754
|
+
var DEFAULT_ACK_PERIOD = 256;
|
|
755
|
+
var DEFAULT_ACK_ADVANCE = 128;
|
|
756
|
+
var RpcStreamSender = class _RpcStreamSender {
|
|
757
|
+
static _emptyGuid = "00000000-0000-0000-0000-000000000000";
|
|
758
|
+
id;
|
|
759
|
+
kind = RpcObjectKind.Local;
|
|
760
|
+
allowReconnect;
|
|
761
|
+
isRealTime;
|
|
762
|
+
canSkipTo;
|
|
763
|
+
peer;
|
|
764
|
+
ackPeriod;
|
|
765
|
+
ackAdvance;
|
|
766
|
+
_nextIndex = 0;
|
|
767
|
+
_ended = false;
|
|
768
|
+
_started = new import_core4.PromiseSource();
|
|
769
|
+
_lastAckedIndex = 0;
|
|
770
|
+
_ackWaiting = null;
|
|
771
|
+
_resetRequested = false;
|
|
772
|
+
constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, ackAdvance = DEFAULT_ACK_ADVANCE, allowReconnect = true, isRealTime = false, canSkipTo = () => true) {
|
|
773
|
+
const localId = peer.sharedObjects.nextId();
|
|
774
|
+
this.id = { hostId: peer.hub.hubId, localId };
|
|
775
|
+
this.allowReconnect = allowReconnect;
|
|
776
|
+
this.isRealTime = isRealTime;
|
|
777
|
+
this.canSkipTo = canSkipTo;
|
|
778
|
+
this.peer = peer;
|
|
779
|
+
this.ackPeriod = ackPeriod;
|
|
780
|
+
this.ackAdvance = ackAdvance;
|
|
781
|
+
}
|
|
782
|
+
/** Returns the stream reference string for the $sys.Ok response. */
|
|
783
|
+
toRef() {
|
|
784
|
+
return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance},${this.allowReconnect ? "1" : "0"},${this.isRealTime ? "1" : "0"}`;
|
|
785
|
+
}
|
|
786
|
+
/** Called by system call handler when $sys.Ack is received from the client. */
|
|
787
|
+
onAck(nextIndex, hostId) {
|
|
788
|
+
const mustReset = hostId !== "" && hostId !== _RpcStreamSender._emptyGuid;
|
|
789
|
+
if (!this._started.isCompleted) {
|
|
790
|
+
this._started.resolve();
|
|
791
|
+
}
|
|
792
|
+
if (mustReset && this.isRealTime) {
|
|
793
|
+
this._nextIndex = nextIndex;
|
|
794
|
+
this._lastAckedIndex = nextIndex;
|
|
795
|
+
this._resetRequested = true;
|
|
796
|
+
} else {
|
|
797
|
+
if (mustReset || nextIndex > this._lastAckedIndex) {
|
|
798
|
+
this._lastAckedIndex = nextIndex;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
if (this._ackWaiting) {
|
|
802
|
+
this._ackWaiting.resolve();
|
|
803
|
+
this._ackWaiting = null;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
/** Called by system call handler when $sys.AckEnd is received from the client. */
|
|
807
|
+
onAckEnd(_hostId) {
|
|
808
|
+
this._ended = true;
|
|
809
|
+
this.peer.sharedObjects.unregister(this);
|
|
810
|
+
}
|
|
811
|
+
/** Send a single item to the client. */
|
|
812
|
+
sendItem(item) {
|
|
813
|
+
if (this._ended) return;
|
|
814
|
+
const conn = this.peer.connection;
|
|
815
|
+
if (!conn) return;
|
|
816
|
+
this.peer.hub.systemCallSender.item(
|
|
817
|
+
conn,
|
|
818
|
+
this.peer.format,
|
|
819
|
+
this.id.localId,
|
|
820
|
+
this._nextIndex,
|
|
821
|
+
item
|
|
822
|
+
);
|
|
823
|
+
this._nextIndex++;
|
|
824
|
+
}
|
|
825
|
+
/** Send a batch of items to the client. */
|
|
826
|
+
sendBatch(items) {
|
|
827
|
+
if (this._ended || items.length === 0) return;
|
|
828
|
+
const conn = this.peer.connection;
|
|
829
|
+
if (!conn) return;
|
|
830
|
+
this.peer.hub.systemCallSender.batch(
|
|
831
|
+
conn,
|
|
832
|
+
this.peer.format,
|
|
833
|
+
this.id.localId,
|
|
834
|
+
this._nextIndex,
|
|
835
|
+
items
|
|
836
|
+
);
|
|
837
|
+
this._nextIndex += items.length;
|
|
838
|
+
}
|
|
839
|
+
/** Signal stream completion to the client. */
|
|
840
|
+
sendEnd(error) {
|
|
841
|
+
if (this._ended) return;
|
|
842
|
+
this._ended = true;
|
|
843
|
+
const conn = this.peer.connection;
|
|
844
|
+
if (!conn) return;
|
|
845
|
+
const errorInfo = error ? { TypeRef: "System.Exception", Message: error.message } : { TypeRef: "", Message: "" };
|
|
846
|
+
this.peer.hub.systemCallSender.end(
|
|
847
|
+
conn,
|
|
848
|
+
this.peer.format,
|
|
849
|
+
this.id.localId,
|
|
850
|
+
this._nextIndex,
|
|
851
|
+
errorInfo
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Consume an AsyncIterable and send all items to the client.
|
|
856
|
+
* Waits for the client's initial Ack before starting to pump items.
|
|
857
|
+
*
|
|
858
|
+
* When `isRealTime` is true, applies flow control based on `ackAdvance`:
|
|
859
|
+
* if the sender is `ackAdvance` items ahead of the last ACK, it enters
|
|
860
|
+
* skip mode — draining the source and discarding items until it finds
|
|
861
|
+
* one where `canSkipTo` returns true.
|
|
862
|
+
*/
|
|
863
|
+
async writeFrom(source) {
|
|
864
|
+
await this._started.promise;
|
|
865
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
866
|
+
try {
|
|
867
|
+
for (; ; ) {
|
|
868
|
+
const next = await iterator.next();
|
|
869
|
+
if (next.done || this._ended) break;
|
|
870
|
+
const item = next.value;
|
|
871
|
+
if (this._resetRequested) {
|
|
872
|
+
if (!this.canSkipTo(item)) continue;
|
|
873
|
+
this._resetRequested = false;
|
|
874
|
+
this.sendItem(item);
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
if (this._nextIndex < this._lastAckedIndex + this.ackAdvance) {
|
|
878
|
+
this.sendItem(item);
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
if (!this.isRealTime) {
|
|
882
|
+
await this._waitForAckBudget();
|
|
883
|
+
if (this._ended) return;
|
|
884
|
+
this.sendItem(item);
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
let latestSkipTarget = this.canSkipTo(item) ? item : void 0;
|
|
888
|
+
let sourceExhausted = false;
|
|
889
|
+
while (this._nextIndex >= this._lastAckedIndex + this.ackAdvance) {
|
|
890
|
+
if (this._ended) return;
|
|
891
|
+
const n = await iterator.next();
|
|
892
|
+
if (n.done) {
|
|
893
|
+
sourceExhausted = true;
|
|
894
|
+
break;
|
|
895
|
+
}
|
|
896
|
+
if (this.canSkipTo(n.value)) {
|
|
897
|
+
latestSkipTarget = n.value;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
if (this._ended) return;
|
|
901
|
+
if (latestSkipTarget !== void 0) {
|
|
902
|
+
this.sendItem(latestSkipTarget);
|
|
903
|
+
}
|
|
904
|
+
if (sourceExhausted) {
|
|
905
|
+
if (!this._ended) this.sendEnd();
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
if (!this._ended) {
|
|
910
|
+
this.sendEnd();
|
|
911
|
+
}
|
|
912
|
+
} catch (e) {
|
|
913
|
+
if (!this._ended) {
|
|
914
|
+
this.sendEnd(e instanceof Error ? e : new Error(String(e)));
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
/** Wait until _nextIndex < _lastAckedIndex + ackAdvance. */
|
|
919
|
+
async _waitForAckBudget() {
|
|
920
|
+
while (this._nextIndex >= this._lastAckedIndex + this.ackAdvance) {
|
|
921
|
+
if (this._ended) return;
|
|
922
|
+
this._ackWaiting = new import_core4.PromiseSource();
|
|
923
|
+
await this._ackWaiting.promise;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
// -- IRpcObject --
|
|
927
|
+
reconnect() {
|
|
928
|
+
}
|
|
929
|
+
disconnect() {
|
|
930
|
+
this._ended = true;
|
|
931
|
+
if (!this._started.isCompleted) {
|
|
932
|
+
this._started.resolve();
|
|
933
|
+
}
|
|
934
|
+
this.peer.sharedObjects.unregister(this);
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
|
|
752
938
|
// src/rpc-stream.ts
|
|
939
|
+
var DEFAULT_ACK_PERIOD2 = 30;
|
|
940
|
+
var DEFAULT_ACK_ADVANCE2 = 61;
|
|
753
941
|
function parseStreamRef(value) {
|
|
754
942
|
if (typeof value === "string") {
|
|
755
943
|
const parts = value.split(",");
|
|
@@ -782,17 +970,21 @@ function parseStreamRef(value) {
|
|
|
782
970
|
}
|
|
783
971
|
var RpcStream = class _RpcStream {
|
|
784
972
|
id;
|
|
785
|
-
kind
|
|
973
|
+
kind;
|
|
786
974
|
allowReconnect;
|
|
787
|
-
|
|
975
|
+
isRealTime;
|
|
976
|
+
canSkipTo;
|
|
788
977
|
ackPeriod;
|
|
789
978
|
ackAdvance;
|
|
790
|
-
//
|
|
791
|
-
|
|
792
|
-
//
|
|
793
|
-
|
|
794
|
-
//
|
|
795
|
-
|
|
979
|
+
// Local (origin) mode
|
|
980
|
+
localSource;
|
|
981
|
+
// Remote (target) mode
|
|
982
|
+
peer;
|
|
983
|
+
// Local-only state
|
|
984
|
+
_sender;
|
|
985
|
+
_toRefValue;
|
|
986
|
+
_whenSent;
|
|
987
|
+
// Remote-only state
|
|
796
988
|
_buffer = new import_denque.default();
|
|
797
989
|
_nextExpectedIndex = 0;
|
|
798
990
|
_consumerWaiting = null;
|
|
@@ -802,12 +994,76 @@ var RpcStream = class _RpcStream {
|
|
|
802
994
|
_started = false;
|
|
803
995
|
_iterating = false;
|
|
804
996
|
_ackSentUpTo = 0;
|
|
805
|
-
constructor(
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
997
|
+
constructor(sourceOrRef, optionsOrPeer) {
|
|
998
|
+
if (_isAsyncIterable(sourceOrRef)) {
|
|
999
|
+
const options = optionsOrPeer ?? {};
|
|
1000
|
+
this.kind = RpcObjectKind.Local;
|
|
1001
|
+
this.localSource = sourceOrRef;
|
|
1002
|
+
this.id = { hostId: "", localId: 0 };
|
|
1003
|
+
this.allowReconnect = options.allowReconnect ?? true;
|
|
1004
|
+
this.isRealTime = options.isRealTime ?? false;
|
|
1005
|
+
this.canSkipTo = options.canSkipTo ?? (() => true);
|
|
1006
|
+
this.ackPeriod = options.ackPeriod ?? DEFAULT_ACK_PERIOD2;
|
|
1007
|
+
this.ackAdvance = options.ackAdvance ?? DEFAULT_ACK_ADVANCE2;
|
|
1008
|
+
} else {
|
|
1009
|
+
const ref = sourceOrRef;
|
|
1010
|
+
const peer = optionsOrPeer;
|
|
1011
|
+
this.kind = RpcObjectKind.Remote;
|
|
1012
|
+
this.id = { hostId: ref.hostId, localId: ref.localId };
|
|
1013
|
+
this.peer = peer;
|
|
1014
|
+
this.allowReconnect = ref.allowReconnect;
|
|
1015
|
+
this.isRealTime = ref.isRealTime;
|
|
1016
|
+
this.canSkipTo = () => true;
|
|
1017
|
+
this.ackPeriod = ref.ackPeriod;
|
|
1018
|
+
this.ackAdvance = ref.ackAdvance;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* For local streams: resolves when the sender finishes pumping (after `toRef()` was called).
|
|
1023
|
+
* Rejects if the source throws. Throws if `toRef()` hasn't been called yet.
|
|
1024
|
+
*/
|
|
1025
|
+
get whenSent() {
|
|
1026
|
+
if (!this._whenSent)
|
|
1027
|
+
throw new Error("toRef() must be called before awaiting whenSent.");
|
|
1028
|
+
return this._whenSent;
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* For local streams: bind to a peer, create and register an `RpcStreamSender`,
|
|
1032
|
+
* start pumping items from the local source in the background, and return the
|
|
1033
|
+
* stream reference.
|
|
1034
|
+
*
|
|
1035
|
+
* For binary peers the ref is a MessagePack-compatible object
|
|
1036
|
+
* (`{ SerializedId, AckPeriod, AckAdvance, AllowReconnect }`);
|
|
1037
|
+
* for text peers it is a comma-separated string.
|
|
1038
|
+
*
|
|
1039
|
+
* Can only be called once per stream.
|
|
1040
|
+
*/
|
|
1041
|
+
toRef(peer) {
|
|
1042
|
+
if (this.kind !== RpcObjectKind.Local)
|
|
1043
|
+
throw new Error("toRef() is only valid for local streams.");
|
|
1044
|
+
if (this._sender) {
|
|
1045
|
+
if (this._sender.peer !== peer)
|
|
1046
|
+
throw new Error("toRef() was already called with a different peer.");
|
|
1047
|
+
return this._toRefValue;
|
|
1048
|
+
}
|
|
1049
|
+
const sender = new RpcStreamSender(
|
|
1050
|
+
peer,
|
|
1051
|
+
this.ackPeriod,
|
|
1052
|
+
this.ackAdvance,
|
|
1053
|
+
this.allowReconnect,
|
|
1054
|
+
this.isRealTime,
|
|
1055
|
+
this.canSkipTo
|
|
1056
|
+
);
|
|
1057
|
+
peer.sharedObjects.register(sender);
|
|
1058
|
+
this._sender = sender;
|
|
1059
|
+
this._whenSent = sender.writeFrom(this.localSource);
|
|
1060
|
+
this._toRefValue = peer.format.isBinary ? {
|
|
1061
|
+
SerializedId: [sender.id.hostId, sender.id.localId],
|
|
1062
|
+
AckPeriod: sender.ackPeriod,
|
|
1063
|
+
AckAdvance: sender.ackAdvance,
|
|
1064
|
+
AllowReconnect: sender.allowReconnect
|
|
1065
|
+
} : sender.toRef();
|
|
1066
|
+
return this._toRefValue;
|
|
811
1067
|
}
|
|
812
1068
|
/** Called by system call handler when a single item arrives ($sys.I). */
|
|
813
1069
|
onItem(index, item) {
|
|
@@ -872,6 +1128,7 @@ var RpcStream = class _RpcStream {
|
|
|
872
1128
|
}
|
|
873
1129
|
// -- IRpcObject --
|
|
874
1130
|
reconnect() {
|
|
1131
|
+
if (this.kind === RpcObjectKind.Local) return;
|
|
875
1132
|
if (this.allowReconnect) {
|
|
876
1133
|
this._sendAck(this._nextExpectedIndex, true);
|
|
877
1134
|
} else {
|
|
@@ -879,6 +1136,7 @@ var RpcStream = class _RpcStream {
|
|
|
879
1136
|
}
|
|
880
1137
|
}
|
|
881
1138
|
disconnect() {
|
|
1139
|
+
if (this.kind === RpcObjectKind.Local) return;
|
|
882
1140
|
if (this._completed) return;
|
|
883
1141
|
this._completed = true;
|
|
884
1142
|
this._completionError = new Error("Peer disconnected.");
|
|
@@ -886,33 +1144,35 @@ var RpcStream = class _RpcStream {
|
|
|
886
1144
|
}
|
|
887
1145
|
// -- AsyncIterable --
|
|
888
1146
|
[Symbol.asyncIterator]() {
|
|
1147
|
+
if (this.kind === RpcObjectKind.Local) {
|
|
1148
|
+
return this.localSource[Symbol.asyncIterator]();
|
|
1149
|
+
}
|
|
889
1150
|
if (this._iterating)
|
|
890
1151
|
throw new Error("RpcStream can only be iterated once.");
|
|
891
1152
|
this._iterating = true;
|
|
892
|
-
const self = this;
|
|
893
1153
|
return {
|
|
894
|
-
async
|
|
895
|
-
if (!
|
|
896
|
-
|
|
897
|
-
|
|
1154
|
+
next: async () => {
|
|
1155
|
+
if (!this._started) {
|
|
1156
|
+
this._started = true;
|
|
1157
|
+
this._sendAck(0, true);
|
|
898
1158
|
}
|
|
899
|
-
|
|
900
|
-
if (!
|
|
901
|
-
const value =
|
|
1159
|
+
for (; ; ) {
|
|
1160
|
+
if (!this._buffer.isEmpty()) {
|
|
1161
|
+
const value = this._buffer.shift();
|
|
902
1162
|
return { value, done: false };
|
|
903
1163
|
}
|
|
904
|
-
if (
|
|
905
|
-
if (
|
|
1164
|
+
if (this._completed) {
|
|
1165
|
+
if (this._completionError) throw this._completionError;
|
|
906
1166
|
return { value: void 0, done: true };
|
|
907
1167
|
}
|
|
908
|
-
|
|
909
|
-
await
|
|
1168
|
+
this._consumerWaiting = new import_core5.PromiseSource();
|
|
1169
|
+
await this._consumerWaiting.promise;
|
|
910
1170
|
}
|
|
911
1171
|
},
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
return { value: void 0, done: true };
|
|
1172
|
+
return: () => {
|
|
1173
|
+
this._sendAckEnd();
|
|
1174
|
+
this.dispose();
|
|
1175
|
+
return Promise.resolve({ value: void 0, done: true });
|
|
916
1176
|
}
|
|
917
1177
|
};
|
|
918
1178
|
}
|
|
@@ -920,7 +1180,9 @@ var RpcStream = class _RpcStream {
|
|
|
920
1180
|
if (this._disposed) return;
|
|
921
1181
|
this._disposed = true;
|
|
922
1182
|
this._completed = true;
|
|
923
|
-
this.
|
|
1183
|
+
if (this.kind === RpcObjectKind.Remote) {
|
|
1184
|
+
this.peer.remoteObjects.unregister(this);
|
|
1185
|
+
}
|
|
924
1186
|
this._notifyConsumer();
|
|
925
1187
|
}
|
|
926
1188
|
// -- Private --
|
|
@@ -964,6 +1226,9 @@ var RpcStream = class _RpcStream {
|
|
|
964
1226
|
}
|
|
965
1227
|
}
|
|
966
1228
|
};
|
|
1229
|
+
function _isAsyncIterable(value) {
|
|
1230
|
+
return value !== null && typeof value === "object" && Symbol.asyncIterator in value;
|
|
1231
|
+
}
|
|
967
1232
|
function resolveStreamRefs(value, peer) {
|
|
968
1233
|
if (value === null || value === void 0) return value;
|
|
969
1234
|
if (typeof value === "string") {
|
|
@@ -1218,177 +1483,6 @@ var RpcSystemCallSender = class _RpcSystemCallSender {
|
|
|
1218
1483
|
}
|
|
1219
1484
|
};
|
|
1220
1485
|
|
|
1221
|
-
// src/rpc-stream-sender.ts
|
|
1222
|
-
var import_core5 = require("@actuallab/core");
|
|
1223
|
-
var DEFAULT_ACK_PERIOD = 256;
|
|
1224
|
-
var DEFAULT_ACK_ADVANCE = 128;
|
|
1225
|
-
var RpcStreamSender = class {
|
|
1226
|
-
id;
|
|
1227
|
-
kind = RpcObjectKind.Local;
|
|
1228
|
-
allowReconnect;
|
|
1229
|
-
isRealTime;
|
|
1230
|
-
canSkipTo;
|
|
1231
|
-
peer;
|
|
1232
|
-
ackPeriod;
|
|
1233
|
-
ackAdvance;
|
|
1234
|
-
_nextIndex = 0;
|
|
1235
|
-
_ended = false;
|
|
1236
|
-
_started = new import_core5.PromiseSource();
|
|
1237
|
-
_lastAckedIndex = 0;
|
|
1238
|
-
_ackWaiting = null;
|
|
1239
|
-
constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, ackAdvance = DEFAULT_ACK_ADVANCE, allowReconnect = true, isRealTime = false, canSkipTo = () => true) {
|
|
1240
|
-
const localId = peer.sharedObjects.nextId();
|
|
1241
|
-
this.id = { hostId: peer.hub.hubId, localId };
|
|
1242
|
-
this.allowReconnect = allowReconnect;
|
|
1243
|
-
this.isRealTime = isRealTime;
|
|
1244
|
-
this.canSkipTo = canSkipTo;
|
|
1245
|
-
this.peer = peer;
|
|
1246
|
-
this.ackPeriod = ackPeriod;
|
|
1247
|
-
this.ackAdvance = ackAdvance;
|
|
1248
|
-
}
|
|
1249
|
-
/** Returns the stream reference string for the $sys.Ok response. */
|
|
1250
|
-
toRef() {
|
|
1251
|
-
return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance},${this.allowReconnect ? "1" : "0"},${this.isRealTime ? "1" : "0"}`;
|
|
1252
|
-
}
|
|
1253
|
-
/** Called by system call handler when $sys.Ack is received from the client. */
|
|
1254
|
-
onAck(nextIndex, _hostId) {
|
|
1255
|
-
if (!this._started.isCompleted) {
|
|
1256
|
-
this._started.resolve();
|
|
1257
|
-
}
|
|
1258
|
-
if (nextIndex > this._lastAckedIndex) {
|
|
1259
|
-
this._lastAckedIndex = nextIndex;
|
|
1260
|
-
}
|
|
1261
|
-
if (this._ackWaiting) {
|
|
1262
|
-
this._ackWaiting.resolve();
|
|
1263
|
-
this._ackWaiting = null;
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
/** Called by system call handler when $sys.AckEnd is received from the client. */
|
|
1267
|
-
onAckEnd(_hostId) {
|
|
1268
|
-
this._ended = true;
|
|
1269
|
-
this.peer.sharedObjects.unregister(this);
|
|
1270
|
-
}
|
|
1271
|
-
/** Send a single item to the client. */
|
|
1272
|
-
sendItem(item) {
|
|
1273
|
-
if (this._ended) return;
|
|
1274
|
-
const conn = this.peer.connection;
|
|
1275
|
-
if (!conn) return;
|
|
1276
|
-
this.peer.hub.systemCallSender.item(
|
|
1277
|
-
conn,
|
|
1278
|
-
this.peer.format,
|
|
1279
|
-
this.id.localId,
|
|
1280
|
-
this._nextIndex,
|
|
1281
|
-
item
|
|
1282
|
-
);
|
|
1283
|
-
this._nextIndex++;
|
|
1284
|
-
}
|
|
1285
|
-
/** Send a batch of items to the client. */
|
|
1286
|
-
sendBatch(items) {
|
|
1287
|
-
if (this._ended || items.length === 0) return;
|
|
1288
|
-
const conn = this.peer.connection;
|
|
1289
|
-
if (!conn) return;
|
|
1290
|
-
this.peer.hub.systemCallSender.batch(
|
|
1291
|
-
conn,
|
|
1292
|
-
this.peer.format,
|
|
1293
|
-
this.id.localId,
|
|
1294
|
-
this._nextIndex,
|
|
1295
|
-
items
|
|
1296
|
-
);
|
|
1297
|
-
this._nextIndex += items.length;
|
|
1298
|
-
}
|
|
1299
|
-
/** Signal stream completion to the client. */
|
|
1300
|
-
sendEnd(error) {
|
|
1301
|
-
if (this._ended) return;
|
|
1302
|
-
this._ended = true;
|
|
1303
|
-
const conn = this.peer.connection;
|
|
1304
|
-
if (!conn) return;
|
|
1305
|
-
const errorInfo = error ? { TypeRef: "System.Exception", Message: error.message } : { TypeRef: "", Message: "" };
|
|
1306
|
-
this.peer.hub.systemCallSender.end(
|
|
1307
|
-
conn,
|
|
1308
|
-
this.peer.format,
|
|
1309
|
-
this.id.localId,
|
|
1310
|
-
this._nextIndex,
|
|
1311
|
-
errorInfo
|
|
1312
|
-
);
|
|
1313
|
-
}
|
|
1314
|
-
/**
|
|
1315
|
-
* Consume an AsyncIterable and send all items to the client.
|
|
1316
|
-
* Waits for the client's initial Ack before starting to pump items.
|
|
1317
|
-
*
|
|
1318
|
-
* When `isRealTime` is true, applies flow control based on `ackAdvance`:
|
|
1319
|
-
* if the sender is `ackAdvance` items ahead of the last ACK, it enters
|
|
1320
|
-
* skip mode — draining the source and discarding items until it finds
|
|
1321
|
-
* one where `canSkipTo` returns true.
|
|
1322
|
-
*/
|
|
1323
|
-
async writeFrom(source) {
|
|
1324
|
-
await this._started.promise;
|
|
1325
|
-
const iterator = source[Symbol.asyncIterator]();
|
|
1326
|
-
try {
|
|
1327
|
-
while (true) {
|
|
1328
|
-
const next = await iterator.next();
|
|
1329
|
-
if (next.done || this._ended) break;
|
|
1330
|
-
const item = next.value;
|
|
1331
|
-
if (this._nextIndex < this._lastAckedIndex + this.ackAdvance) {
|
|
1332
|
-
this.sendItem(item);
|
|
1333
|
-
continue;
|
|
1334
|
-
}
|
|
1335
|
-
if (!this.isRealTime) {
|
|
1336
|
-
await this._waitForAckBudget();
|
|
1337
|
-
if (this._ended) return;
|
|
1338
|
-
this.sendItem(item);
|
|
1339
|
-
continue;
|
|
1340
|
-
}
|
|
1341
|
-
let latestSkipTarget = this.canSkipTo(item) ? item : void 0;
|
|
1342
|
-
let sourceExhausted = false;
|
|
1343
|
-
while (this._nextIndex >= this._lastAckedIndex + this.ackAdvance) {
|
|
1344
|
-
if (this._ended) return;
|
|
1345
|
-
const n = await iterator.next();
|
|
1346
|
-
if (n.done) {
|
|
1347
|
-
sourceExhausted = true;
|
|
1348
|
-
break;
|
|
1349
|
-
}
|
|
1350
|
-
if (this.canSkipTo(n.value)) {
|
|
1351
|
-
latestSkipTarget = n.value;
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
if (this._ended) return;
|
|
1355
|
-
if (latestSkipTarget !== void 0) {
|
|
1356
|
-
this.sendItem(latestSkipTarget);
|
|
1357
|
-
}
|
|
1358
|
-
if (sourceExhausted) {
|
|
1359
|
-
if (!this._ended) this.sendEnd();
|
|
1360
|
-
return;
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
if (!this._ended) {
|
|
1364
|
-
this.sendEnd();
|
|
1365
|
-
}
|
|
1366
|
-
} catch (e) {
|
|
1367
|
-
if (!this._ended) {
|
|
1368
|
-
this.sendEnd(e instanceof Error ? e : new Error(String(e)));
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
/** Wait until _nextIndex < _lastAckedIndex + ackAdvance. */
|
|
1373
|
-
async _waitForAckBudget() {
|
|
1374
|
-
while (this._nextIndex >= this._lastAckedIndex + this.ackAdvance) {
|
|
1375
|
-
if (this._ended) return;
|
|
1376
|
-
this._ackWaiting = new import_core5.PromiseSource();
|
|
1377
|
-
await this._ackWaiting.promise;
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
// -- IRpcObject --
|
|
1381
|
-
reconnect() {
|
|
1382
|
-
}
|
|
1383
|
-
disconnect() {
|
|
1384
|
-
this._ended = true;
|
|
1385
|
-
if (!this._started.isCompleted) {
|
|
1386
|
-
this._started.resolve();
|
|
1387
|
-
}
|
|
1388
|
-
this.peer.sharedObjects.unregister(this);
|
|
1389
|
-
}
|
|
1390
|
-
};
|
|
1391
|
-
|
|
1392
1486
|
// src/rpc-remote-object-tracker.ts
|
|
1393
1487
|
var RpcRemoteObjectTracker = class {
|
|
1394
1488
|
_objects = /* @__PURE__ */ new Map();
|
|
@@ -1687,21 +1781,18 @@ var RpcPeer = class {
|
|
|
1687
1781
|
}
|
|
1688
1782
|
const signal = options?.signal;
|
|
1689
1783
|
if (signal !== void 0) {
|
|
1690
|
-
const peer = this;
|
|
1691
|
-
const hub = this._hub;
|
|
1692
|
-
const tracker = this.outbound;
|
|
1693
1784
|
const onAbort = () => {
|
|
1694
|
-
if (
|
|
1785
|
+
if (this.outbound.remove(callId) !== void 0) {
|
|
1695
1786
|
outboundCall.result.reject(new Error("Call cancelled."));
|
|
1696
1787
|
outboundCall.onDisconnect();
|
|
1697
|
-
if (
|
|
1698
|
-
|
|
1788
|
+
if (this._connection !== void 0)
|
|
1789
|
+
this._hub.systemCallSender.cancel(this._connection, this.format, callId);
|
|
1699
1790
|
} else {
|
|
1700
|
-
const idx =
|
|
1791
|
+
const idx = this._pendingSends.findIndex(
|
|
1701
1792
|
(c) => c.callId === callId
|
|
1702
1793
|
);
|
|
1703
1794
|
if (idx !== -1) {
|
|
1704
|
-
|
|
1795
|
+
this._pendingSends.splice(idx, 1);
|
|
1705
1796
|
outboundCall.result.reject(
|
|
1706
1797
|
new Error("Call cancelled.")
|
|
1707
1798
|
);
|
|
@@ -1795,53 +1886,49 @@ var RpcPeer = class {
|
|
|
1795
1886
|
this.inbound.register(call);
|
|
1796
1887
|
}
|
|
1797
1888
|
const serviceHost = this._hub.serviceHost;
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1889
|
+
void (async () => {
|
|
1890
|
+
try {
|
|
1891
|
+
const context = this._connection !== void 0 ? {
|
|
1892
|
+
__rpcDispatch: true,
|
|
1893
|
+
callId: relatedId,
|
|
1894
|
+
connection: this._connection
|
|
1895
|
+
} : void 0;
|
|
1896
|
+
const result = await serviceHost.dispatch(
|
|
1897
|
+
method,
|
|
1898
|
+
args,
|
|
1899
|
+
context
|
|
1900
|
+
);
|
|
1901
|
+
if (methodDef?.stream === true && this._connection !== void 0) {
|
|
1902
|
+
const stream = result instanceof RpcStream ? result : new RpcStream(result);
|
|
1903
|
+
this._hub.systemCallSender.ok(
|
|
1904
|
+
this._connection,
|
|
1905
|
+
this.format,
|
|
1906
|
+
relatedId,
|
|
1907
|
+
stream.toRef(this)
|
|
1908
|
+
);
|
|
1909
|
+
} else if (!isNoWait && this._connection !== void 0) {
|
|
1910
|
+
this._hub.systemCallSender.ok(
|
|
1911
|
+
this._connection,
|
|
1912
|
+
this.format,
|
|
1913
|
+
relatedId,
|
|
1914
|
+
result
|
|
1810
1915
|
);
|
|
1811
|
-
if (methodDef?.stream === true && this._connection !== void 0) {
|
|
1812
|
-
const sender = new RpcStreamSender(this);
|
|
1813
|
-
this.sharedObjects.register(sender);
|
|
1814
|
-
this._hub.systemCallSender.ok(
|
|
1815
|
-
this._connection,
|
|
1816
|
-
this.format,
|
|
1817
|
-
relatedId,
|
|
1818
|
-
sender.toRef()
|
|
1819
|
-
);
|
|
1820
|
-
void sender.writeFrom(result);
|
|
1821
|
-
} else if (!isNoWait && this._connection !== void 0) {
|
|
1822
|
-
this._hub.systemCallSender.ok(
|
|
1823
|
-
this._connection,
|
|
1824
|
-
this.format,
|
|
1825
|
-
relatedId,
|
|
1826
|
-
result
|
|
1827
|
-
);
|
|
1828
|
-
}
|
|
1829
|
-
} catch (e) {
|
|
1830
|
-
if (!isNoWait && this._connection !== void 0) {
|
|
1831
|
-
this._hub.systemCallSender.error(
|
|
1832
|
-
this._connection,
|
|
1833
|
-
this.format,
|
|
1834
|
-
relatedId,
|
|
1835
|
-
e
|
|
1836
|
-
);
|
|
1837
|
-
}
|
|
1838
|
-
} finally {
|
|
1839
|
-
if (!isNoWait) {
|
|
1840
|
-
this.inbound.remove(relatedId);
|
|
1841
|
-
}
|
|
1842
1916
|
}
|
|
1843
|
-
}
|
|
1844
|
-
|
|
1917
|
+
} catch (e) {
|
|
1918
|
+
if (!isNoWait && this._connection !== void 0) {
|
|
1919
|
+
this._hub.systemCallSender.error(
|
|
1920
|
+
this._connection,
|
|
1921
|
+
this.format,
|
|
1922
|
+
relatedId,
|
|
1923
|
+
e
|
|
1924
|
+
);
|
|
1925
|
+
}
|
|
1926
|
+
} finally {
|
|
1927
|
+
if (!isNoWait) {
|
|
1928
|
+
this.inbound.remove(relatedId);
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
})();
|
|
1845
1932
|
}
|
|
1846
1933
|
_startKeepAlive() {
|
|
1847
1934
|
this._keepAliveTimer = setInterval(() => {
|
|
@@ -2710,6 +2797,7 @@ var RpcHub = class {
|
|
|
2710
2797
|
this.registry.registerService(def.name, def.methods);
|
|
2711
2798
|
}
|
|
2712
2799
|
/** Create a typed client proxy for a service on a remote peer. */
|
|
2800
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- T is used for caller-specified proxy type
|
|
2713
2801
|
addClient(peer, defOrContract) {
|
|
2714
2802
|
const def = this._resolveServiceDef(defOrContract);
|
|
2715
2803
|
this.registry.registerService(def.name, def.methods);
|