@actuallab/rpc 12.2.21 → 12.3.2
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 +352 -225
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +74 -20
- package/dist/index.d.ts +74 -20
- package/dist/index.js +351 -224
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -31,7 +31,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
ARG_DELIMITER: () => ARG_DELIMITER,
|
|
34
|
-
DEFAULT_SERIALIZATION_FORMAT: () => DEFAULT_SERIALIZATION_FORMAT,
|
|
35
34
|
ENVELOPE_DELIMITER: () => ENVELOPE_DELIMITER,
|
|
36
35
|
FRAME_DELIMITER: () => FRAME_DELIMITER,
|
|
37
36
|
RPC_CLOSE_CODE_UNSUPPORTED_FORMAT: () => RPC_CLOSE_CODE_UNSUPPORTED_FORMAT,
|
|
@@ -55,6 +54,7 @@ __export(index_exports, {
|
|
|
55
54
|
RpcPeerStateMonitor: () => RpcPeerStateMonitor,
|
|
56
55
|
RpcRemoteObjectTracker: () => RpcRemoteObjectTracker,
|
|
57
56
|
RpcSerializationFormat: () => RpcSerializationFormat,
|
|
57
|
+
RpcSerializationFormatResolver: () => RpcSerializationFormatResolver,
|
|
58
58
|
RpcServerPeer: () => RpcServerPeer,
|
|
59
59
|
RpcServiceHost: () => RpcServiceHost,
|
|
60
60
|
RpcSharedObjectTracker: () => RpcSharedObjectTracker,
|
|
@@ -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
|
+
while (true) {
|
|
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,6 +1144,9 @@ 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;
|
|
@@ -905,7 +1166,7 @@ var RpcStream = class _RpcStream {
|
|
|
905
1166
|
if (self._completionError) throw self._completionError;
|
|
906
1167
|
return { value: void 0, done: true };
|
|
907
1168
|
}
|
|
908
|
-
self._consumerWaiting = new
|
|
1169
|
+
self._consumerWaiting = new import_core5.PromiseSource();
|
|
909
1170
|
await self._consumerWaiting.promise;
|
|
910
1171
|
}
|
|
911
1172
|
},
|
|
@@ -920,7 +1181,9 @@ var RpcStream = class _RpcStream {
|
|
|
920
1181
|
if (this._disposed) return;
|
|
921
1182
|
this._disposed = true;
|
|
922
1183
|
this._completed = true;
|
|
923
|
-
this.
|
|
1184
|
+
if (this.kind === RpcObjectKind.Remote) {
|
|
1185
|
+
this.peer.remoteObjects.unregister(this);
|
|
1186
|
+
}
|
|
924
1187
|
this._notifyConsumer();
|
|
925
1188
|
}
|
|
926
1189
|
// -- Private --
|
|
@@ -964,6 +1227,9 @@ var RpcStream = class _RpcStream {
|
|
|
964
1227
|
}
|
|
965
1228
|
}
|
|
966
1229
|
};
|
|
1230
|
+
function _isAsyncIterable(value) {
|
|
1231
|
+
return value !== null && typeof value === "object" && Symbol.asyncIterator in value;
|
|
1232
|
+
}
|
|
967
1233
|
function resolveStreamRefs(value, peer) {
|
|
968
1234
|
if (value === null || value === void 0) return value;
|
|
969
1235
|
if (typeof value === "string") {
|
|
@@ -1218,177 +1484,6 @@ var RpcSystemCallSender = class _RpcSystemCallSender {
|
|
|
1218
1484
|
}
|
|
1219
1485
|
};
|
|
1220
1486
|
|
|
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
1487
|
// src/rpc-remote-object-tracker.ts
|
|
1393
1488
|
var RpcRemoteObjectTracker = class {
|
|
1394
1489
|
_objects = /* @__PURE__ */ new Map();
|
|
@@ -1459,7 +1554,7 @@ var RpcClientPeerReconnectDelayer = class extends import_core6.RetryDelayer {
|
|
|
1459
1554
|
|
|
1460
1555
|
// src/rpc-serialization-format.ts
|
|
1461
1556
|
var import_msgpack3 = require("@msgpack/msgpack");
|
|
1462
|
-
var RpcSerializationFormat = class
|
|
1557
|
+
var RpcSerializationFormat = class {
|
|
1463
1558
|
/** Create a reusable binary encoder for this format. Returns undefined for text formats. */
|
|
1464
1559
|
createEncoder() {
|
|
1465
1560
|
return void 0;
|
|
@@ -1468,19 +1563,23 @@ var RpcSerializationFormat = class _RpcSerializationFormat {
|
|
|
1468
1563
|
createDecoder() {
|
|
1469
1564
|
return void 0;
|
|
1470
1565
|
}
|
|
1471
|
-
//
|
|
1472
|
-
static
|
|
1473
|
-
static
|
|
1474
|
-
|
|
1475
|
-
|
|
1566
|
+
// Named format instances — initialized in initFormats() below (after subclasses are defined)
|
|
1567
|
+
static SystemJsonV5;
|
|
1568
|
+
static SystemJsonV5NP;
|
|
1569
|
+
static NewtonsoftJsonV5;
|
|
1570
|
+
static NewtonsoftJsonV5NP;
|
|
1571
|
+
static MessagePackV6;
|
|
1572
|
+
static MemoryPackV6;
|
|
1573
|
+
static MessagePackV6C;
|
|
1574
|
+
static MemoryPackV6C;
|
|
1575
|
+
static All;
|
|
1576
|
+
/** Convenience shortcut for RpcSerializationFormatResolver.Default.get(key). */
|
|
1476
1577
|
static get(key) {
|
|
1477
|
-
|
|
1478
|
-
if (!format)
|
|
1479
|
-
throw new Error(`Unknown RPC serialization format: "${key}"`);
|
|
1480
|
-
return format;
|
|
1578
|
+
return RpcSerializationFormatResolver.Default.get(key);
|
|
1481
1579
|
}
|
|
1580
|
+
/** Convenience shortcut for RpcSerializationFormatResolver.Default.tryGet(key). */
|
|
1482
1581
|
static tryGet(key) {
|
|
1483
|
-
return
|
|
1582
|
+
return RpcSerializationFormatResolver.Default.tryGet(key);
|
|
1484
1583
|
}
|
|
1485
1584
|
};
|
|
1486
1585
|
var RpcJsonSerializationFormat = class extends RpcSerializationFormat {
|
|
@@ -1563,25 +1662,54 @@ var RpcMessagePackCompactSerializationFormat = class extends RpcSerializationFor
|
|
|
1563
1662
|
return new import_msgpack3.Decoder();
|
|
1564
1663
|
}
|
|
1565
1664
|
};
|
|
1566
|
-
RpcSerializationFormat.
|
|
1567
|
-
RpcSerializationFormat.
|
|
1568
|
-
RpcSerializationFormat.
|
|
1569
|
-
RpcSerializationFormat.
|
|
1570
|
-
RpcSerializationFormat.
|
|
1571
|
-
|
|
1572
|
-
);
|
|
1573
|
-
RpcSerializationFormat.
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
RpcSerializationFormat.
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
RpcSerializationFormat.
|
|
1580
|
-
|
|
1581
|
-
|
|
1665
|
+
RpcSerializationFormat.SystemJsonV5 = new RpcJsonSerializationFormat("json5");
|
|
1666
|
+
RpcSerializationFormat.SystemJsonV5NP = new RpcJsonSerializationFormat("json5np");
|
|
1667
|
+
RpcSerializationFormat.NewtonsoftJsonV5 = new RpcJsonSerializationFormat("njson5");
|
|
1668
|
+
RpcSerializationFormat.NewtonsoftJsonV5NP = new RpcJsonSerializationFormat("njson5np");
|
|
1669
|
+
RpcSerializationFormat.MessagePackV6 = new RpcMessagePackSerializationFormat("msgpack6");
|
|
1670
|
+
RpcSerializationFormat.MemoryPackV6 = new RpcMessagePackSerializationFormat("mempack6");
|
|
1671
|
+
RpcSerializationFormat.MessagePackV6C = new RpcMessagePackCompactSerializationFormat("msgpack6c");
|
|
1672
|
+
RpcSerializationFormat.MemoryPackV6C = new RpcMessagePackCompactSerializationFormat("mempack6c");
|
|
1673
|
+
RpcSerializationFormat.All = [
|
|
1674
|
+
RpcSerializationFormat.SystemJsonV5,
|
|
1675
|
+
RpcSerializationFormat.SystemJsonV5NP,
|
|
1676
|
+
RpcSerializationFormat.NewtonsoftJsonV5,
|
|
1677
|
+
RpcSerializationFormat.NewtonsoftJsonV5NP,
|
|
1678
|
+
RpcSerializationFormat.MessagePackV6,
|
|
1679
|
+
RpcSerializationFormat.MemoryPackV6,
|
|
1680
|
+
RpcSerializationFormat.MessagePackV6C,
|
|
1681
|
+
RpcSerializationFormat.MemoryPackV6C
|
|
1682
|
+
];
|
|
1683
|
+
var RpcSerializationFormatResolver = class _RpcSerializationFormatResolver {
|
|
1684
|
+
static DefaultFormats = RpcSerializationFormat.All;
|
|
1685
|
+
static Default = new _RpcSerializationFormatResolver("json5np");
|
|
1686
|
+
defaultFormatKey;
|
|
1687
|
+
formats;
|
|
1688
|
+
get defaultFormat() {
|
|
1689
|
+
return this.get(this.defaultFormatKey);
|
|
1690
|
+
}
|
|
1691
|
+
constructor(defaultFormatKey, formats) {
|
|
1692
|
+
const map = /* @__PURE__ */ new Map();
|
|
1693
|
+
for (const f of formats ?? _RpcSerializationFormatResolver.DefaultFormats)
|
|
1694
|
+
map.set(f.key, f);
|
|
1695
|
+
if (!map.has(defaultFormatKey))
|
|
1696
|
+
throw new Error(`No format with key '${defaultFormatKey}'.`);
|
|
1697
|
+
this.defaultFormatKey = defaultFormatKey;
|
|
1698
|
+
this.formats = map;
|
|
1699
|
+
}
|
|
1700
|
+
get(key) {
|
|
1701
|
+
const format = this.tryGet(key);
|
|
1702
|
+
if (!format)
|
|
1703
|
+
throw new Error(`No format with key '${key}'.`);
|
|
1704
|
+
return format;
|
|
1705
|
+
}
|
|
1706
|
+
tryGet(key) {
|
|
1707
|
+
if (!key) key = this.defaultFormatKey;
|
|
1708
|
+
return this.formats.get(key);
|
|
1709
|
+
}
|
|
1710
|
+
};
|
|
1582
1711
|
|
|
1583
1712
|
// src/rpc-peer.ts
|
|
1584
|
-
var DEFAULT_SERIALIZATION_FORMAT = "json5np";
|
|
1585
1713
|
var RPC_CLOSE_CODE_UNSUPPORTED_FORMAT = 4001;
|
|
1586
1714
|
var defaultConnectionUrlResolver = (peer) => {
|
|
1587
1715
|
const sep = peer.ref.includes("?") ? "&" : "?";
|
|
@@ -1630,7 +1758,7 @@ var RpcPeer = class {
|
|
|
1630
1758
|
}
|
|
1631
1759
|
/** The serialization format used by this peer. Override in subclasses. */
|
|
1632
1760
|
get format() {
|
|
1633
|
-
return
|
|
1761
|
+
return RpcSerializationFormatResolver.Default.defaultFormat;
|
|
1634
1762
|
}
|
|
1635
1763
|
call(method, args, options) {
|
|
1636
1764
|
const callId = this.outbound.nextId();
|
|
@@ -1776,15 +1904,13 @@ var RpcPeer = class {
|
|
|
1776
1904
|
context
|
|
1777
1905
|
);
|
|
1778
1906
|
if (methodDef?.stream === true && this._connection !== void 0) {
|
|
1779
|
-
const
|
|
1780
|
-
this.sharedObjects.register(sender);
|
|
1907
|
+
const stream = result instanceof RpcStream ? result : new RpcStream(result);
|
|
1781
1908
|
this._hub.systemCallSender.ok(
|
|
1782
1909
|
this._connection,
|
|
1783
1910
|
this.format,
|
|
1784
1911
|
relatedId,
|
|
1785
|
-
|
|
1912
|
+
stream.toRef(this)
|
|
1786
1913
|
);
|
|
1787
|
-
void sender.writeFrom(result);
|
|
1788
1914
|
} else if (!isNoWait && this._connection !== void 0) {
|
|
1789
1915
|
this._hub.systemCallSender.ok(
|
|
1790
1916
|
this._connection,
|
|
@@ -1867,8 +1993,9 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
1867
1993
|
constructor(hub, url, serializationFormat) {
|
|
1868
1994
|
super(url, hub);
|
|
1869
1995
|
this.clientId = guidToBase64Url(this.id);
|
|
1870
|
-
|
|
1871
|
-
this.
|
|
1996
|
+
const resolver = RpcSerializationFormatResolver.Default;
|
|
1997
|
+
this.serializationFormat = serializationFormat ?? resolver.defaultFormatKey;
|
|
1998
|
+
this._format = resolver.get(this.serializationFormat);
|
|
1872
1999
|
}
|
|
1873
2000
|
get format() {
|
|
1874
2001
|
return this._format;
|
|
@@ -3016,7 +3143,6 @@ var RpcPeerStateMonitor = class {
|
|
|
3016
3143
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3017
3144
|
0 && (module.exports = {
|
|
3018
3145
|
ARG_DELIMITER,
|
|
3019
|
-
DEFAULT_SERIALIZATION_FORMAT,
|
|
3020
3146
|
ENVELOPE_DELIMITER,
|
|
3021
3147
|
FRAME_DELIMITER,
|
|
3022
3148
|
RPC_CLOSE_CODE_UNSUPPORTED_FORMAT,
|
|
@@ -3040,6 +3166,7 @@ var RpcPeerStateMonitor = class {
|
|
|
3040
3166
|
RpcPeerStateMonitor,
|
|
3041
3167
|
RpcRemoteObjectTracker,
|
|
3042
3168
|
RpcSerializationFormat,
|
|
3169
|
+
RpcSerializationFormatResolver,
|
|
3043
3170
|
RpcServerPeer,
|
|
3044
3171
|
RpcServiceHost,
|
|
3045
3172
|
RpcSharedObjectTracker,
|