@actuallab/rpc 13.0.20 → 13.0.163
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 +624 -178
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +167 -11
- package/dist/index.d.ts +167 -11
- package/dist/index.js +619 -177
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -34,8 +34,10 @@ __export(index_exports, {
|
|
|
34
34
|
ENVELOPE_DELIMITER: () => ENVELOPE_DELIMITER,
|
|
35
35
|
FRAME_DELIMITER: () => FRAME_DELIMITER,
|
|
36
36
|
IncreasingSeqCompressor: () => IncreasingSeqCompressor,
|
|
37
|
+
REMOTE_EXCEPTION_TYPE_REF: () => REMOTE_EXCEPTION_TYPE_REF,
|
|
37
38
|
RPC_CLOSE_CODE_UNSUPPORTED_FORMAT: () => RPC_CLOSE_CODE_UNSUPPORTED_FORMAT,
|
|
38
39
|
RpcCallStage: () => RpcCallStage,
|
|
40
|
+
RpcCallTimeouts: () => RpcCallTimeouts,
|
|
39
41
|
RpcCallTypeId: () => RpcCallTypeId,
|
|
40
42
|
RpcClientPeer: () => RpcClientPeer,
|
|
41
43
|
RpcClientPeerReconnectDelayer: () => RpcClientPeerReconnectDelayer,
|
|
@@ -90,6 +92,7 @@ __export(index_exports, {
|
|
|
90
92
|
isConnected: () => isConnected,
|
|
91
93
|
likelyConnected: () => likelyConnected,
|
|
92
94
|
parseStreamRef: () => parseStreamRef,
|
|
95
|
+
readPolymorphismMarker: () => readPolymorphismMarker,
|
|
93
96
|
resolveStreamRefs: () => resolveStreamRefs,
|
|
94
97
|
rpcMethod: () => rpcMethod,
|
|
95
98
|
rpcService: () => rpcService,
|
|
@@ -101,6 +104,7 @@ __export(index_exports, {
|
|
|
101
104
|
splitBinaryFrame: () => splitBinaryFrame,
|
|
102
105
|
splitCompactBinaryFrame: () => splitCompactBinaryFrame,
|
|
103
106
|
splitFrame: () => splitFrame,
|
|
107
|
+
toExceptionInfo: () => toExceptionInfo,
|
|
104
108
|
wireMethodName: () => wireMethodName,
|
|
105
109
|
xxh3_64: () => xxh3_64,
|
|
106
110
|
xxh3_64str: () => xxh3_64str
|
|
@@ -271,6 +275,24 @@ function readVarUint(data, offset) {
|
|
|
271
275
|
} while (shift < 35);
|
|
272
276
|
return { value: value >>> 0, bytesRead };
|
|
273
277
|
}
|
|
278
|
+
function skipHeaders(data, pos, headerCount) {
|
|
279
|
+
for (let h = 0; h < headerCount; h++) {
|
|
280
|
+
const keyLen = data[pos++];
|
|
281
|
+
pos += keyLen;
|
|
282
|
+
const valLen = readVarUint(data, pos);
|
|
283
|
+
pos += valLen.bytesRead + valLen.value;
|
|
284
|
+
}
|
|
285
|
+
return pos;
|
|
286
|
+
}
|
|
287
|
+
function readPolymorphismMarker(data, offset) {
|
|
288
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
289
|
+
const length = view.getUint16(offset, true);
|
|
290
|
+
if (length === 0)
|
|
291
|
+
return { bytesRead: 2, isNull: false };
|
|
292
|
+
if (length === 1)
|
|
293
|
+
return { bytesRead: 2, isNull: true };
|
|
294
|
+
throw new Error("polymorphic payloads are not supported by the TS client");
|
|
295
|
+
}
|
|
274
296
|
function concatUint8Arrays(arrays) {
|
|
275
297
|
let totalLength = 0;
|
|
276
298
|
for (const arr of arrays) totalLength += arr.length;
|
|
@@ -334,14 +356,6 @@ function deserializeBinaryMessage(data, offset, decoder = defaultBinaryDecoder)
|
|
|
334
356
|
pos += methodLen.value;
|
|
335
357
|
const argDataLen = view.getInt32(pos, true);
|
|
336
358
|
pos += 4;
|
|
337
|
-
if (headerCount > 0) {
|
|
338
|
-
for (let h = 0; h < headerCount; h++) {
|
|
339
|
-
const keyLen = data[pos++];
|
|
340
|
-
pos += keyLen;
|
|
341
|
-
const valLen = readVarUint(data, pos);
|
|
342
|
-
pos += valLen.bytesRead + valLen.value;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
359
|
const args = [];
|
|
346
360
|
const argEnd = pos + argDataLen;
|
|
347
361
|
if (argDataLen > 0) {
|
|
@@ -349,8 +363,10 @@ function deserializeBinaryMessage(data, offset, decoder = defaultBinaryDecoder)
|
|
|
349
363
|
for (const decoded of decoder.decodeMulti(argSlice)) {
|
|
350
364
|
args.push(decoded);
|
|
351
365
|
}
|
|
352
|
-
pos = argEnd;
|
|
353
366
|
}
|
|
367
|
+
pos = argEnd;
|
|
368
|
+
if (headerCount > 0)
|
|
369
|
+
pos = skipHeaders(data, pos, headerCount);
|
|
354
370
|
const message = {
|
|
355
371
|
Method: method,
|
|
356
372
|
RelatedId: relId.value,
|
|
@@ -428,14 +444,6 @@ function deserializeCompactBinaryMessage(data, offset, registry, decoder = defau
|
|
|
428
444
|
const method = registry?.getName(methodHash | 0) ?? `<hash:0x${methodHash.toString(16).padStart(8, "0")}>`;
|
|
429
445
|
const argDataLen = view.getInt32(pos, true);
|
|
430
446
|
pos += 4;
|
|
431
|
-
if (headerCount > 0) {
|
|
432
|
-
for (let h = 0; h < headerCount; h++) {
|
|
433
|
-
const keyLen = data[pos++];
|
|
434
|
-
pos += keyLen;
|
|
435
|
-
const valLen = readVarUint(data, pos);
|
|
436
|
-
pos += valLen.bytesRead + valLen.value;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
447
|
const args = [];
|
|
440
448
|
const argEnd = pos + argDataLen;
|
|
441
449
|
if (argDataLen > 0) {
|
|
@@ -443,8 +451,10 @@ function deserializeCompactBinaryMessage(data, offset, registry, decoder = defau
|
|
|
443
451
|
for (const decoded of decoder.decodeMulti(argSlice)) {
|
|
444
452
|
args.push(decoded);
|
|
445
453
|
}
|
|
446
|
-
pos = argEnd;
|
|
447
454
|
}
|
|
455
|
+
pos = argEnd;
|
|
456
|
+
if (headerCount > 0)
|
|
457
|
+
pos = skipHeaders(data, pos, headerCount);
|
|
448
458
|
const message = {
|
|
449
459
|
Method: method,
|
|
450
460
|
RelatedId: relId.value,
|
|
@@ -714,7 +724,8 @@ function defineRpcService(name, methods) {
|
|
|
714
724
|
callTypeId: input.callTypeId ?? 0,
|
|
715
725
|
stream: input.returns === RpcType.stream,
|
|
716
726
|
noWait: isNoWait,
|
|
717
|
-
remoteExecutionMode: isNoWait ? 0 : input.remoteExecutionMode ?? RpcRemoteExecutionMode.Default
|
|
727
|
+
remoteExecutionMode: isNoWait ? 0 : input.remoteExecutionMode ?? RpcRemoteExecutionMode.Default,
|
|
728
|
+
timeouts: input.timeouts
|
|
718
729
|
});
|
|
719
730
|
}
|
|
720
731
|
return { name, methods: map };
|
|
@@ -748,6 +759,14 @@ var RpcOutboundCall = class {
|
|
|
748
759
|
removeOnOk = true;
|
|
749
760
|
/** The serialized wire data — stored for re-sending on reconnect. */
|
|
750
761
|
serializedWireData = "";
|
|
762
|
+
/** Per-call connect/run timeouts enforced by the peer's maintenance loop
|
|
763
|
+
* (R12). `undefined` means unbounded (the query default). */
|
|
764
|
+
timeouts;
|
|
765
|
+
/** `Date.now()` when this call was created (used for the connect timeout). */
|
|
766
|
+
startedAt = Date.now();
|
|
767
|
+
/** `Date.now()` when this call was last put on the wire, or 0 if it hasn't
|
|
768
|
+
* been sent yet (used for the run timeout). */
|
|
769
|
+
sentAt = 0;
|
|
751
770
|
/**
|
|
752
771
|
* Bitfield of {@link RpcCallStage} flags indicating how far this call
|
|
753
772
|
* has progressed. Used by the `$sys.Reconnect` protocol to tell the
|
|
@@ -837,20 +856,77 @@ var RpcInboundCall = class {
|
|
|
837
856
|
callId;
|
|
838
857
|
method;
|
|
839
858
|
args;
|
|
859
|
+
/** 0 = still processing, 1 = result computed and sent at least once. */
|
|
860
|
+
completedStage = 0;
|
|
861
|
+
_resend;
|
|
862
|
+
_abortController = new AbortController();
|
|
840
863
|
constructor(callId, method, args) {
|
|
841
864
|
this.callId = callId;
|
|
842
865
|
this.method = method;
|
|
843
866
|
this.args = args;
|
|
844
867
|
}
|
|
868
|
+
/** Aborted when the remote peer cancels this call via `$sys.Cancel` (R17).
|
|
869
|
+
* Threaded into the service handler so it can abort in-flight work. */
|
|
870
|
+
get signal() {
|
|
871
|
+
return this._abortController.signal;
|
|
872
|
+
}
|
|
873
|
+
/** True once the remote peer cancelled this call — the response is then
|
|
874
|
+
* suppressed even though the call may still be registered (R9 dedup). */
|
|
875
|
+
get isCancelled() {
|
|
876
|
+
return this._abortController.signal.aborted;
|
|
877
|
+
}
|
|
878
|
+
cancel() {
|
|
879
|
+
this._abortController.abort();
|
|
880
|
+
}
|
|
881
|
+
// Records the closure that re-sends this call's already-computed result;
|
|
882
|
+
// marks the call as completed. Mirrors .NET's post-`ProcessStage1Plus` state.
|
|
883
|
+
setResult(resend) {
|
|
884
|
+
this._resend = resend;
|
|
885
|
+
this.completedStage = 1;
|
|
886
|
+
}
|
|
887
|
+
// Re-sends the result to the remote peer if it was already computed.
|
|
888
|
+
// A resent frame for an in-flight call is a no-op — the original dispatch
|
|
889
|
+
// will send the result when it completes. Mirrors .NET
|
|
890
|
+
// `RpcInboundCall.TryReprocess`.
|
|
891
|
+
resendResult() {
|
|
892
|
+
if (this.completedStage >= 1)
|
|
893
|
+
this._resend?.();
|
|
894
|
+
}
|
|
845
895
|
};
|
|
846
896
|
var RpcInboundCallTracker = class {
|
|
847
897
|
_calls = /* @__PURE__ */ new Map();
|
|
898
|
+
_completedIds = [];
|
|
899
|
+
/** Cap on retained completed calls — see `RpcLimits.completedInboundCallsLimit`. */
|
|
900
|
+
completedCallsLimit = 1e3;
|
|
848
901
|
get size() {
|
|
849
902
|
return this._calls.size;
|
|
850
903
|
}
|
|
851
904
|
register(call) {
|
|
852
905
|
this._calls.set(call.callId, call);
|
|
853
906
|
}
|
|
907
|
+
// Registers `call` unless its id is already tracked, in which case the
|
|
908
|
+
// existing call is returned. Mirrors .NET `RpcInboundCallTracker.GetOrRegister`.
|
|
909
|
+
getOrRegister(call) {
|
|
910
|
+
const existing = this._calls.get(call.callId);
|
|
911
|
+
if (existing !== void 0)
|
|
912
|
+
return existing;
|
|
913
|
+
this._calls.set(call.callId, call);
|
|
914
|
+
return call;
|
|
915
|
+
}
|
|
916
|
+
// Keeps the completed `call` registered for duplicate-frame dedup, evicting
|
|
917
|
+
// the oldest completed calls beyond `completedCallsLimit`. Deviation from
|
|
918
|
+
// .NET (which unregisters a call once its result is sent): TS peers
|
|
919
|
+
// blind-resend after reconnect, so a completed call must stay resolvable
|
|
920
|
+
// to avoid re-executing the handler — but only within a bounded window.
|
|
921
|
+
markCompleted(call) {
|
|
922
|
+
if (this._calls.get(call.callId) !== call)
|
|
923
|
+
return;
|
|
924
|
+
this._completedIds.push(call.callId);
|
|
925
|
+
while (this._completedIds.length > this.completedCallsLimit) {
|
|
926
|
+
const id = this._completedIds.shift();
|
|
927
|
+
this._calls.delete(id);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
854
930
|
get(callId) {
|
|
855
931
|
return this._calls.get(callId);
|
|
856
932
|
}
|
|
@@ -861,6 +937,23 @@ var RpcInboundCallTracker = class {
|
|
|
861
937
|
}
|
|
862
938
|
clear() {
|
|
863
939
|
this._calls.clear();
|
|
940
|
+
this._completedIds.length = 0;
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
// src/rpc-call-timeouts.ts
|
|
945
|
+
var RpcCallTimeouts = class _RpcCallTimeouts {
|
|
946
|
+
/** Both timeouts unbounded — the query default (.NET `RpcCallTimeouts.None`). */
|
|
947
|
+
static None = new _RpcCallTimeouts(Infinity, Infinity);
|
|
948
|
+
/** Command default: 1.5 s connect / 10 s run (.NET `RpcCallTimeouts.Default.Command`). */
|
|
949
|
+
static Command = new _RpcCallTimeouts(1500, 1e4);
|
|
950
|
+
/** Max time a call may wait for the connection before it is sent. */
|
|
951
|
+
connectTimeoutMs;
|
|
952
|
+
/** Max time a sent call may stay unanswered before it is failed. */
|
|
953
|
+
runTimeoutMs;
|
|
954
|
+
constructor(connectTimeoutMs = Infinity, runTimeoutMs = Infinity) {
|
|
955
|
+
this.connectTimeoutMs = connectTimeoutMs;
|
|
956
|
+
this.runTimeoutMs = runTimeoutMs;
|
|
864
957
|
}
|
|
865
958
|
};
|
|
866
959
|
|
|
@@ -874,6 +967,12 @@ var RpcError = class extends Error {
|
|
|
874
967
|
this.typeName = typeName;
|
|
875
968
|
}
|
|
876
969
|
};
|
|
970
|
+
var REMOTE_EXCEPTION_TYPE_REF = "ActualLab.Serialization.RemoteException, ActualLab.Core";
|
|
971
|
+
function toExceptionInfo(error) {
|
|
972
|
+
const name = error instanceof Error ? error.name : "Error";
|
|
973
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
974
|
+
return { TypeRef: REMOTE_EXCEPTION_TYPE_REF, Message: `${name}: ${message}` };
|
|
975
|
+
}
|
|
877
976
|
|
|
878
977
|
// src/increasing-seq-compressor.ts
|
|
879
978
|
var IncreasingSeqCompressor = {
|
|
@@ -1078,13 +1177,38 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
1078
1177
|
*/
|
|
1079
1178
|
onAck(nextIndex, hostId) {
|
|
1080
1179
|
const mustReset = hostId !== "" && hostId !== _RpcStreamSender._emptyGuid;
|
|
1081
|
-
if (
|
|
1180
|
+
if (mustReset && hostId !== this.id.hostId) {
|
|
1181
|
+
this._sendDisconnect();
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
if (!this._started.isCompleted) {
|
|
1185
|
+
if (mustReset && nextIndex === 0)
|
|
1186
|
+
this._started.resolve();
|
|
1187
|
+
else {
|
|
1188
|
+
this._sendDisconnect();
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
} else if (this._ended) {
|
|
1192
|
+
this._sendDisconnect();
|
|
1193
|
+
return;
|
|
1194
|
+
} else if (mustReset && !this.allowReconnect) {
|
|
1195
|
+
this._sendDisconnect();
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1082
1198
|
this._acks.push({ nextIndex, mustReset });
|
|
1083
1199
|
if (this._whenAckReady) {
|
|
1084
1200
|
this._whenAckReady.resolve();
|
|
1085
1201
|
this._whenAckReady = null;
|
|
1086
1202
|
}
|
|
1087
1203
|
}
|
|
1204
|
+
// Tells the consumer this shared stream is gone, then disposes it —
|
|
1205
|
+
// mirrors RpcSharedStream.SendDisconnect (RpcSharedStream.cs:284-289).
|
|
1206
|
+
_sendDisconnect() {
|
|
1207
|
+
const conn = this.peer.connection;
|
|
1208
|
+
if (conn)
|
|
1209
|
+
this.peer.hub.systemCallSender.disconnect(conn, this.peer.serializationFormat, [this.id.localId]);
|
|
1210
|
+
this.disconnect();
|
|
1211
|
+
}
|
|
1088
1212
|
/** Called by system call handler when $sys.AckEnd is received from the client. */
|
|
1089
1213
|
onAckEnd(_hostId) {
|
|
1090
1214
|
this.disconnect();
|
|
@@ -1133,7 +1257,7 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
1133
1257
|
this._ended = true;
|
|
1134
1258
|
const conn = this.peer.connection;
|
|
1135
1259
|
if (!conn) return;
|
|
1136
|
-
const errorInfo = error ?
|
|
1260
|
+
const errorInfo = error ? toExceptionInfo(error) : { TypeRef: "", Message: "" };
|
|
1137
1261
|
this.peer.hub.systemCallSender.end(
|
|
1138
1262
|
conn,
|
|
1139
1263
|
this.peer.serializationFormat,
|
|
@@ -1575,11 +1699,7 @@ var RpcStream = class _RpcStream {
|
|
|
1575
1699
|
if (index > this._nextExpectedIndex) {
|
|
1576
1700
|
warnLog4?.log(`item index gap: localId=${this.id.localId}, expected=${this._nextExpectedIndex}, received=${index}`);
|
|
1577
1701
|
if (!this.allowReconnect) {
|
|
1578
|
-
this.
|
|
1579
|
-
this._completionError = new Error(
|
|
1580
|
-
`Stream gap at index ${index} (expected ${this._nextExpectedIndex}); reconnect not allowed`
|
|
1581
|
-
);
|
|
1582
|
-
this._notifyConsumer();
|
|
1702
|
+
this._complete(this._gapError(index));
|
|
1583
1703
|
return;
|
|
1584
1704
|
}
|
|
1585
1705
|
this._sendAck(this._nextExpectedIndex, true);
|
|
@@ -1599,11 +1719,7 @@ var RpcStream = class _RpcStream {
|
|
|
1599
1719
|
if (index > this._nextExpectedIndex) {
|
|
1600
1720
|
warnLog4?.log(`batch index gap: localId=${this.id.localId}, expected=${this._nextExpectedIndex}, received=${index}`);
|
|
1601
1721
|
if (!this.allowReconnect) {
|
|
1602
|
-
this.
|
|
1603
|
-
this._completionError = new Error(
|
|
1604
|
-
`Stream gap at index ${index} (expected ${this._nextExpectedIndex}); reconnect not allowed`
|
|
1605
|
-
);
|
|
1606
|
-
this._notifyConsumer();
|
|
1722
|
+
this._complete(this._gapError(index));
|
|
1607
1723
|
return;
|
|
1608
1724
|
}
|
|
1609
1725
|
this._sendAck(this._nextExpectedIndex, true);
|
|
@@ -1620,9 +1736,20 @@ var RpcStream = class _RpcStream {
|
|
|
1620
1736
|
/** Called by system call handler when the stream ends ($sys.End). */
|
|
1621
1737
|
onEnd(index, error) {
|
|
1622
1738
|
if (this._disposed || this._completed) return;
|
|
1623
|
-
this.
|
|
1624
|
-
|
|
1625
|
-
|
|
1739
|
+
if (index > this._nextExpectedIndex) {
|
|
1740
|
+
warnLog4?.log(`end index gap: localId=${this.id.localId}, expected=${this._nextExpectedIndex}, received=${index}`);
|
|
1741
|
+
if (!this.allowReconnect) {
|
|
1742
|
+
this._complete(this._gapError(index));
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
this._sendAck(this._nextExpectedIndex, true);
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
if (index < this._nextExpectedIndex) {
|
|
1749
|
+
this._maybeSendAck(Math.min(index, this._nextConsumedIndex));
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
this._complete(error);
|
|
1626
1753
|
}
|
|
1627
1754
|
// -- IRpcObject --
|
|
1628
1755
|
reconnect() {
|
|
@@ -1656,6 +1783,7 @@ var RpcStream = class _RpcStream {
|
|
|
1656
1783
|
next: async () => {
|
|
1657
1784
|
if (!this._started) {
|
|
1658
1785
|
this._started = true;
|
|
1786
|
+
this.peer.remoteObjects.register(this);
|
|
1659
1787
|
this._sendAck(0, true);
|
|
1660
1788
|
}
|
|
1661
1789
|
this._maybeSendAck(this._nextConsumedIndex);
|
|
@@ -1666,6 +1794,7 @@ var RpcStream = class _RpcStream {
|
|
|
1666
1794
|
return { value, done: false };
|
|
1667
1795
|
}
|
|
1668
1796
|
if (this._completed) {
|
|
1797
|
+
this.dispose();
|
|
1669
1798
|
if (this._completionError) throw this._completionError;
|
|
1670
1799
|
return { value: void 0, done: true };
|
|
1671
1800
|
}
|
|
@@ -1674,7 +1803,8 @@ var RpcStream = class _RpcStream {
|
|
|
1674
1803
|
}
|
|
1675
1804
|
},
|
|
1676
1805
|
return: () => {
|
|
1677
|
-
this.
|
|
1806
|
+
if (this._started && !this._completed)
|
|
1807
|
+
this._sendAckEnd();
|
|
1678
1808
|
this.dispose();
|
|
1679
1809
|
return Promise.resolve({ value: void 0, done: true });
|
|
1680
1810
|
}
|
|
@@ -1690,6 +1820,22 @@ var RpcStream = class _RpcStream {
|
|
|
1690
1820
|
this._notifyConsumer();
|
|
1691
1821
|
}
|
|
1692
1822
|
// -- Private --
|
|
1823
|
+
// Mirrors .NET RpcStream.CloseFromLock: acknowledge the end to the sender
|
|
1824
|
+
// (so its RpcSharedStream is released) and unregister immediately.
|
|
1825
|
+
_complete(error) {
|
|
1826
|
+
if (this._completed) return;
|
|
1827
|
+
this._completed = true;
|
|
1828
|
+
this._completionError = error;
|
|
1829
|
+
this._sendAckEnd();
|
|
1830
|
+
if (this.kind === RpcObjectKind.Remote)
|
|
1831
|
+
this.peer.remoteObjects.unregister(this);
|
|
1832
|
+
this._notifyConsumer();
|
|
1833
|
+
}
|
|
1834
|
+
_gapError(index) {
|
|
1835
|
+
return new Error(
|
|
1836
|
+
`Stream gap at index ${index} (expected ${this._nextExpectedIndex}); reconnect not allowed`
|
|
1837
|
+
);
|
|
1838
|
+
}
|
|
1693
1839
|
_notifyConsumer() {
|
|
1694
1840
|
if (this._consumerWaiting !== null) {
|
|
1695
1841
|
this._consumerWaiting.resolve();
|
|
@@ -1706,7 +1852,7 @@ var RpcStream = class _RpcStream {
|
|
|
1706
1852
|
static _emptyGuid = "00000000-0000-0000-0000-000000000000";
|
|
1707
1853
|
_sendAck(nextIndex, mustReset) {
|
|
1708
1854
|
this._ackSentUpTo = nextIndex;
|
|
1709
|
-
const conn = this.peer.connection;
|
|
1855
|
+
const conn = this.peer.isConnected ? this.peer.connection : void 0;
|
|
1710
1856
|
if (conn) {
|
|
1711
1857
|
const hostId = mustReset ? this.id.hostId : _RpcStream._emptyGuid;
|
|
1712
1858
|
this.peer.hub.systemCallSender.ack(
|
|
@@ -1719,7 +1865,7 @@ var RpcStream = class _RpcStream {
|
|
|
1719
1865
|
}
|
|
1720
1866
|
}
|
|
1721
1867
|
_sendAckEnd() {
|
|
1722
|
-
const conn = this.peer.connection;
|
|
1868
|
+
const conn = this.peer.isConnected ? this.peer.connection : void 0;
|
|
1723
1869
|
if (conn) {
|
|
1724
1870
|
this.peer.hub.systemCallSender.ackEnd(
|
|
1725
1871
|
conn,
|
|
@@ -1739,9 +1885,7 @@ function resolveStreamRefs(value, peer) {
|
|
|
1739
1885
|
if (typeof value === "string") {
|
|
1740
1886
|
const ref = parseStreamRef(value);
|
|
1741
1887
|
if (ref !== null) {
|
|
1742
|
-
|
|
1743
|
-
peer.remoteObjects.register(stream);
|
|
1744
|
-
return stream;
|
|
1888
|
+
return new RpcStream(ref, peer);
|
|
1745
1889
|
}
|
|
1746
1890
|
return value;
|
|
1747
1891
|
}
|
|
@@ -1794,6 +1938,11 @@ var RpcSystemCallHandler = class {
|
|
|
1794
1938
|
case RpcSystemCalls.ok: {
|
|
1795
1939
|
const call = peer.outboundCalls.get(relatedId);
|
|
1796
1940
|
if (call !== void 0) {
|
|
1941
|
+
if (args.length > 1) {
|
|
1942
|
+
peer.outboundCalls.remove(relatedId);
|
|
1943
|
+
call.result.reject(polymorphicPayloadError(method, args.length, 1));
|
|
1944
|
+
break;
|
|
1945
|
+
}
|
|
1797
1946
|
call.completedStage |= RpcCallStage.ResultReady;
|
|
1798
1947
|
if (call.removeOnOk) {
|
|
1799
1948
|
peer.outboundCalls.remove(relatedId);
|
|
@@ -1807,19 +1956,15 @@ var RpcSystemCallHandler = class {
|
|
|
1807
1956
|
break;
|
|
1808
1957
|
}
|
|
1809
1958
|
case RpcSystemCalls.error: {
|
|
1810
|
-
const call = peer.outboundCalls.
|
|
1959
|
+
const call = peer.outboundCalls.get(relatedId);
|
|
1811
1960
|
if (call !== void 0) {
|
|
1961
|
+
call.completedStage |= RpcCallStage.ResultReady;
|
|
1962
|
+
if (call.removeOnOk) {
|
|
1963
|
+
peer.outboundCalls.remove(relatedId);
|
|
1964
|
+
}
|
|
1812
1965
|
const errorInfo = args[0];
|
|
1813
1966
|
const msg = errorInfo?.Message ?? errorInfo?.message ?? "RPC error";
|
|
1814
|
-
const
|
|
1815
|
-
let typeName;
|
|
1816
|
-
if (typeof errorType === "string") {
|
|
1817
|
-
const commaIdx = errorType.indexOf(",");
|
|
1818
|
-
typeName = commaIdx >= 0 ? errorType.slice(0, commaIdx) : errorType;
|
|
1819
|
-
} else if (errorType !== null && typeof errorType === "object") {
|
|
1820
|
-
const t = errorType;
|
|
1821
|
-
typeName = t.TypeName ?? t.typeName;
|
|
1822
|
-
}
|
|
1967
|
+
const typeName = extractTypeName(errorInfo?.TypeRef ?? errorInfo?.typeRef);
|
|
1823
1968
|
if (typeName === "ActualLab.Rpc.RpcRerouteException")
|
|
1824
1969
|
warnLog5?.log("Got RpcRerouteException from remote peer:", msg);
|
|
1825
1970
|
call.result.reject(new RpcError(msg, typeName));
|
|
@@ -1827,17 +1972,29 @@ var RpcSystemCallHandler = class {
|
|
|
1827
1972
|
break;
|
|
1828
1973
|
}
|
|
1829
1974
|
case RpcSystemCalls.cancel: {
|
|
1830
|
-
peer.inboundCalls.remove(relatedId);
|
|
1975
|
+
const call = peer.inboundCalls.remove(relatedId);
|
|
1976
|
+
call?.cancel();
|
|
1831
1977
|
break;
|
|
1832
1978
|
}
|
|
1833
1979
|
case RpcSystemCalls.keepAlive: {
|
|
1834
1980
|
peer.notifyKeepAliveReceived();
|
|
1981
|
+
const keepAliveIds = args[0];
|
|
1982
|
+
if (Array.isArray(keepAliveIds) && keepAliveIds.length > 0 && peer.connection !== void 0) {
|
|
1983
|
+
const unknownIds = [];
|
|
1984
|
+
for (const id of keepAliveIds)
|
|
1985
|
+
if (peer.sharedObjects.get(id) === void 0)
|
|
1986
|
+
unknownIds.push(id);
|
|
1987
|
+
if (unknownIds.length > 0)
|
|
1988
|
+
peer.hub.systemCallSender.disconnect(peer.connection, peer.serializationFormat, unknownIds);
|
|
1989
|
+
}
|
|
1835
1990
|
break;
|
|
1836
1991
|
}
|
|
1837
1992
|
case RpcSystemCalls.item: {
|
|
1838
1993
|
const stream = peer.remoteObjects.get(relatedId);
|
|
1839
1994
|
if (!stream)
|
|
1840
1995
|
debugLog?.log(`$sys.I: no stream for relatedId=${relatedId}`);
|
|
1996
|
+
else if (args.length > 2)
|
|
1997
|
+
stream.onEnd(args[0], polymorphicPayloadError(method, args.length, 2));
|
|
1841
1998
|
else
|
|
1842
1999
|
stream.onItem(
|
|
1843
2000
|
args[0],
|
|
@@ -1849,6 +2006,8 @@ var RpcSystemCallHandler = class {
|
|
|
1849
2006
|
const stream = peer.remoteObjects.get(relatedId);
|
|
1850
2007
|
if (!stream)
|
|
1851
2008
|
debugLog?.log(`$sys.B: no stream for relatedId=${relatedId}`);
|
|
2009
|
+
else if (args.length > 2)
|
|
2010
|
+
stream.onEnd(args[0], polymorphicPayloadError(method, args.length, 2));
|
|
1852
2011
|
else if (Array.isArray(args[1])) {
|
|
1853
2012
|
const items = args[1];
|
|
1854
2013
|
for (let i = 0; i < items.length; i++)
|
|
@@ -1863,20 +2022,32 @@ var RpcSystemCallHandler = class {
|
|
|
1863
2022
|
debugLog?.log(`$sys.End: no stream for relatedId=${relatedId}`);
|
|
1864
2023
|
} else {
|
|
1865
2024
|
const errorInfo = args[1];
|
|
2025
|
+
const typeName = extractTypeName(errorInfo?.TypeRef ?? errorInfo?.typeRef);
|
|
1866
2026
|
const msg = errorInfo?.Message ?? errorInfo?.message;
|
|
1867
|
-
|
|
2027
|
+
let error = null;
|
|
2028
|
+
if (typeName || msg) {
|
|
2029
|
+
error = new Error(msg ?? typeName ?? "RPC stream error");
|
|
2030
|
+
if (typeName)
|
|
2031
|
+
error.name = typeName;
|
|
2032
|
+
}
|
|
1868
2033
|
stream.onEnd(args[0], error);
|
|
1869
2034
|
}
|
|
1870
2035
|
break;
|
|
1871
2036
|
}
|
|
1872
2037
|
case RpcSystemCalls.ack: {
|
|
1873
2038
|
const sender = peer.sharedObjects.get(relatedId);
|
|
1874
|
-
sender
|
|
2039
|
+
if (sender)
|
|
2040
|
+
sender.onAck(args[0], args[1]);
|
|
2041
|
+
else if (peer.connection !== void 0)
|
|
2042
|
+
peer.hub.systemCallSender.disconnect(peer.connection, peer.serializationFormat, [relatedId]);
|
|
1875
2043
|
break;
|
|
1876
2044
|
}
|
|
1877
2045
|
case RpcSystemCalls.ackEnd: {
|
|
1878
2046
|
const sender = peer.sharedObjects.get(relatedId);
|
|
1879
|
-
sender
|
|
2047
|
+
if (sender)
|
|
2048
|
+
sender.onAckEnd(args[0]);
|
|
2049
|
+
else if (peer.connection !== void 0)
|
|
2050
|
+
peer.hub.systemCallSender.disconnect(peer.connection, peer.serializationFormat, [relatedId]);
|
|
1880
2051
|
break;
|
|
1881
2052
|
}
|
|
1882
2053
|
case RpcSystemCalls.disconnect: {
|
|
@@ -1910,19 +2081,34 @@ var RpcSystemCallHandler = class {
|
|
|
1910
2081
|
*/
|
|
1911
2082
|
_handleReconnect(relatedId, args, peer) {
|
|
1912
2083
|
if (peer.connection === void 0) return;
|
|
2084
|
+
const handshakeIndex = args[0];
|
|
2085
|
+
if (typeof handshakeIndex === "number" && handshakeIndex !== peer.ownHandshakeIndex) {
|
|
2086
|
+
peer.hub.systemCallSender.error(
|
|
2087
|
+
peer.connection,
|
|
2088
|
+
peer.serializationFormat,
|
|
2089
|
+
relatedId,
|
|
2090
|
+
new Error(
|
|
2091
|
+
`TooLateToReconnect: own handshake index ${peer.ownHandshakeIndex} != ${handshakeIndex}`
|
|
2092
|
+
)
|
|
2093
|
+
);
|
|
2094
|
+
return;
|
|
2095
|
+
}
|
|
1913
2096
|
let unknownIds = [];
|
|
1914
2097
|
try {
|
|
1915
2098
|
const stagesRaw = args[1];
|
|
1916
2099
|
if (stagesRaw && typeof stagesRaw === "object" && !Array.isArray(stagesRaw)) {
|
|
1917
2100
|
const unknownSet = /* @__PURE__ */ new Set();
|
|
1918
|
-
for (const [, rawValue] of Object.entries(stagesRaw)) {
|
|
2101
|
+
for (const [stageKey, rawValue] of Object.entries(stagesRaw)) {
|
|
1919
2102
|
const bytes = _toBytes(rawValue);
|
|
1920
2103
|
if (bytes === null) continue;
|
|
2104
|
+
const completedStage = parseInt(stageKey, 10) || 0;
|
|
1921
2105
|
const callIds = IncreasingSeqCompressor.deserialize(bytes);
|
|
1922
2106
|
for (const callId of callIds) {
|
|
1923
|
-
|
|
2107
|
+
const call = peer.inboundCalls.get(callId);
|
|
2108
|
+
if (call === void 0)
|
|
1924
2109
|
unknownSet.add(callId);
|
|
1925
|
-
|
|
2110
|
+
else if (completedStage < 1)
|
|
2111
|
+
call.resendResult();
|
|
1926
2112
|
}
|
|
1927
2113
|
}
|
|
1928
2114
|
unknownIds = [...unknownSet].sort((a, b) => a - b);
|
|
@@ -1934,6 +2120,29 @@ var RpcSystemCallHandler = class {
|
|
|
1934
2120
|
peer.hub.systemCallSender.ok(peer.connection, peer.serializationFormat, relatedId, responseValue);
|
|
1935
2121
|
}
|
|
1936
2122
|
};
|
|
2123
|
+
function polymorphicPayloadError(method, got, expected) {
|
|
2124
|
+
return new Error(
|
|
2125
|
+
`${method}: expected ${expected} value(s), got ${got} \u2014 polymorphic payloads are not supported by the TS client`
|
|
2126
|
+
);
|
|
2127
|
+
}
|
|
2128
|
+
function extractTypeName(typeRef) {
|
|
2129
|
+
if (typeof typeRef === "string") {
|
|
2130
|
+
if (typeRef.length === 0)
|
|
2131
|
+
return void 0;
|
|
2132
|
+
const commaIdx = typeRef.indexOf(",");
|
|
2133
|
+
return commaIdx >= 0 ? typeRef.slice(0, commaIdx) : typeRef;
|
|
2134
|
+
}
|
|
2135
|
+
if (typeRef !== null && typeof typeRef === "object") {
|
|
2136
|
+
const t = typeRef;
|
|
2137
|
+
const aqn = t.AssemblyQualifiedName ?? t.assemblyQualifiedName;
|
|
2138
|
+
if (typeof aqn === "string" && aqn.length > 0) {
|
|
2139
|
+
const commaIdx = aqn.indexOf(",");
|
|
2140
|
+
return commaIdx >= 0 ? aqn.slice(0, commaIdx) : aqn;
|
|
2141
|
+
}
|
|
2142
|
+
return t.TypeName ?? t.typeName;
|
|
2143
|
+
}
|
|
2144
|
+
return void 0;
|
|
2145
|
+
}
|
|
1937
2146
|
function _toBytes(value) {
|
|
1938
2147
|
if (value instanceof Uint8Array) return value;
|
|
1939
2148
|
if (typeof value === "string") {
|
|
@@ -2017,12 +2226,11 @@ var RpcSystemCallSender = class _RpcSystemCallSender {
|
|
|
2017
2226
|
}
|
|
2018
2227
|
}
|
|
2019
2228
|
error(conn, format, relatedId, error) {
|
|
2020
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2021
2229
|
this._send(
|
|
2022
2230
|
conn,
|
|
2023
2231
|
format,
|
|
2024
2232
|
{ Method: RpcSystemCalls.error, RelatedId: relatedId },
|
|
2025
|
-
[
|
|
2233
|
+
[toExceptionInfo(error)]
|
|
2026
2234
|
);
|
|
2027
2235
|
}
|
|
2028
2236
|
cancel(conn, format, relatedId) {
|
|
@@ -2036,6 +2244,14 @@ var RpcSystemCallSender = class _RpcSystemCallSender {
|
|
|
2036
2244
|
activeCallIds
|
|
2037
2245
|
]);
|
|
2038
2246
|
}
|
|
2247
|
+
// Tells the remote peer the listed shared-object ids are gone on our side,
|
|
2248
|
+
// so its matching remote objects (e.g. RpcStream consumers) fail fast
|
|
2249
|
+
// instead of hanging. Mirrors .NET RpcSystemCallSender.Disconnect.
|
|
2250
|
+
disconnect(conn, format, localIds) {
|
|
2251
|
+
this._send(conn, format, { Method: RpcSystemCalls.disconnect }, [
|
|
2252
|
+
localIds
|
|
2253
|
+
]);
|
|
2254
|
+
}
|
|
2039
2255
|
item(conn, format, localId, index, item) {
|
|
2040
2256
|
this._itemEnv.RelatedId = localId;
|
|
2041
2257
|
this._itemArgs[0] = index;
|
|
@@ -2073,7 +2289,13 @@ var RpcSystemCallSender = class _RpcSystemCallSender {
|
|
|
2073
2289
|
var RpcRemoteObjectTracker = class {
|
|
2074
2290
|
_objects = /* @__PURE__ */ new Map();
|
|
2075
2291
|
register(obj) {
|
|
2076
|
-
|
|
2292
|
+
const localId = obj.id.localId;
|
|
2293
|
+
const existing = this._objects.get(localId);
|
|
2294
|
+
if (existing === obj)
|
|
2295
|
+
return;
|
|
2296
|
+
if (existing !== void 0)
|
|
2297
|
+
existing.disconnect();
|
|
2298
|
+
this._objects.set(localId, obj);
|
|
2077
2299
|
}
|
|
2078
2300
|
get(localId) {
|
|
2079
2301
|
return this._objects.get(localId);
|
|
@@ -2082,7 +2304,9 @@ var RpcRemoteObjectTracker = class {
|
|
|
2082
2304
|
return this._objects.keys();
|
|
2083
2305
|
}
|
|
2084
2306
|
unregister(obj) {
|
|
2085
|
-
|
|
2307
|
+
const localId = obj.id.localId;
|
|
2308
|
+
if (this._objects.get(localId) === obj)
|
|
2309
|
+
this._objects.delete(localId);
|
|
2086
2310
|
}
|
|
2087
2311
|
disconnectAll() {
|
|
2088
2312
|
for (const obj of this._objects.values()) {
|
|
@@ -2106,7 +2330,10 @@ var RpcSharedObjectTracker = class {
|
|
|
2106
2330
|
return this._nextId++;
|
|
2107
2331
|
}
|
|
2108
2332
|
register(obj) {
|
|
2109
|
-
|
|
2333
|
+
const localId = obj.id.localId;
|
|
2334
|
+
if (this._objects.has(localId))
|
|
2335
|
+
throw new Error(`RPC shared object with localId ${localId} is already registered.`);
|
|
2336
|
+
this._objects.set(localId, obj);
|
|
2110
2337
|
}
|
|
2111
2338
|
get(localId) {
|
|
2112
2339
|
return this._objects.get(localId);
|
|
@@ -2115,7 +2342,9 @@ var RpcSharedObjectTracker = class {
|
|
|
2115
2342
|
return this._objects.keys();
|
|
2116
2343
|
}
|
|
2117
2344
|
unregister(obj) {
|
|
2118
|
-
|
|
2345
|
+
const localId = obj.id.localId;
|
|
2346
|
+
if (this._objects.get(localId) === obj)
|
|
2347
|
+
this._objects.delete(localId);
|
|
2119
2348
|
}
|
|
2120
2349
|
disconnectAll() {
|
|
2121
2350
|
for (const obj of this._objects.values()) {
|
|
@@ -2145,9 +2374,7 @@ var RpcSerializationFormat = class {
|
|
|
2145
2374
|
static NewtonsoftJsonV5;
|
|
2146
2375
|
static NewtonsoftJsonV5NP;
|
|
2147
2376
|
static MessagePackV6;
|
|
2148
|
-
static MemoryPackV6;
|
|
2149
2377
|
static MessagePackV6C;
|
|
2150
|
-
static MemoryPackV6C;
|
|
2151
2378
|
static All;
|
|
2152
2379
|
/** Convenience shortcut for RpcSerializationFormatResolver.Default.get(key). */
|
|
2153
2380
|
static get(key) {
|
|
@@ -2243,18 +2470,14 @@ RpcSerializationFormat.SystemJsonV5NP = new RpcJsonSerializationFormat("json5np"
|
|
|
2243
2470
|
RpcSerializationFormat.NewtonsoftJsonV5 = new RpcJsonSerializationFormat("njson5");
|
|
2244
2471
|
RpcSerializationFormat.NewtonsoftJsonV5NP = new RpcJsonSerializationFormat("njson5np");
|
|
2245
2472
|
RpcSerializationFormat.MessagePackV6 = new RpcMessagePackSerializationFormat("msgpack6");
|
|
2246
|
-
RpcSerializationFormat.MemoryPackV6 = new RpcMessagePackSerializationFormat("mempack6");
|
|
2247
2473
|
RpcSerializationFormat.MessagePackV6C = new RpcMessagePackCompactSerializationFormat("msgpack6c");
|
|
2248
|
-
RpcSerializationFormat.MemoryPackV6C = new RpcMessagePackCompactSerializationFormat("mempack6c");
|
|
2249
2474
|
RpcSerializationFormat.All = [
|
|
2250
2475
|
RpcSerializationFormat.SystemJsonV5,
|
|
2251
2476
|
RpcSerializationFormat.SystemJsonV5NP,
|
|
2252
2477
|
RpcSerializationFormat.NewtonsoftJsonV5,
|
|
2253
2478
|
RpcSerializationFormat.NewtonsoftJsonV5NP,
|
|
2254
2479
|
RpcSerializationFormat.MessagePackV6,
|
|
2255
|
-
RpcSerializationFormat.
|
|
2256
|
-
RpcSerializationFormat.MessagePackV6C,
|
|
2257
|
-
RpcSerializationFormat.MemoryPackV6C
|
|
2480
|
+
RpcSerializationFormat.MessagePackV6C
|
|
2258
2481
|
];
|
|
2259
2482
|
var RpcSerializationFormatResolver = class _RpcSerializationFormatResolver {
|
|
2260
2483
|
static DefaultFormats = RpcSerializationFormat.All;
|
|
@@ -2274,6 +2497,8 @@ var RpcSerializationFormatResolver = class _RpcSerializationFormatResolver {
|
|
|
2274
2497
|
this.formats = map;
|
|
2275
2498
|
}
|
|
2276
2499
|
get(key) {
|
|
2500
|
+
if (isMemoryPackKey(key))
|
|
2501
|
+
throw new Error(`Serialization format '${key}': MemoryPack formats are not supported by the TS client.`);
|
|
2277
2502
|
const format = this.tryGet(key);
|
|
2278
2503
|
if (!format)
|
|
2279
2504
|
throw new Error(`No format with key '${key}'.`);
|
|
@@ -2281,9 +2506,14 @@ var RpcSerializationFormatResolver = class _RpcSerializationFormatResolver {
|
|
|
2281
2506
|
}
|
|
2282
2507
|
tryGet(key) {
|
|
2283
2508
|
if (!key) key = this.defaultFormatKey;
|
|
2509
|
+
if (isMemoryPackKey(key))
|
|
2510
|
+
throw new Error(`Serialization format '${key}': MemoryPack formats are not supported by the TS client.`);
|
|
2284
2511
|
return this.formats.get(key);
|
|
2285
2512
|
}
|
|
2286
2513
|
};
|
|
2514
|
+
function isMemoryPackKey(key) {
|
|
2515
|
+
return key.startsWith("mempack");
|
|
2516
|
+
}
|
|
2287
2517
|
|
|
2288
2518
|
// src/rpc-peer.ts
|
|
2289
2519
|
function yieldToEventLoop() {
|
|
@@ -2337,6 +2567,10 @@ var RpcPeer = class {
|
|
|
2337
2567
|
* attempt (the reconnect delay is part of this phase); `Connected` is
|
|
2338
2568
|
* entered only after the handshake has succeeded. */
|
|
2339
2569
|
connectionStateChanged = new import_core7.EventHandlerSet();
|
|
2570
|
+
/** Index of the handshake THIS peer most recently sent. Used to reject a
|
|
2571
|
+
* stale `$sys.Reconnect` that references an older connection generation
|
|
2572
|
+
* (C# `RpcSystemCalls.Reconnect`'s `ownHandshake.Index` check). */
|
|
2573
|
+
_ownHandshakeIndex = 0;
|
|
2340
2574
|
_connection;
|
|
2341
2575
|
/** Mirror of `_connectionState === Connected`. True only after the
|
|
2342
2576
|
* handshake round-trip completes; outbound `call()` checks this to
|
|
@@ -2348,6 +2582,7 @@ var RpcPeer = class {
|
|
|
2348
2582
|
_connectionState = 0 /* Disconnected */;
|
|
2349
2583
|
_keepAliveTimer;
|
|
2350
2584
|
_keepAliveWatchdog;
|
|
2585
|
+
_maintainTimer;
|
|
2351
2586
|
/** Wall-clock time of the last inbound `$sys.KeepAlive`, or 0 if none yet
|
|
2352
2587
|
* for the current connection. Useful for diagnostics. */
|
|
2353
2588
|
_lastKeepAliveAt = 0;
|
|
@@ -2362,6 +2597,37 @@ var RpcPeer = class {
|
|
|
2362
2597
|
this.ref = ref;
|
|
2363
2598
|
this.keepAlivePeriodMs = hub.limits.keepAlivePeriodMs;
|
|
2364
2599
|
this.keepAliveTimeoutMs = hub.limits.keepAliveTimeoutMs;
|
|
2600
|
+
this.inboundCalls.completedCallsLimit = hub.limits.completedInboundCallsLimit;
|
|
2601
|
+
const checkPeriod = hub.limits.callTimeoutCheckPeriodMs;
|
|
2602
|
+
if (checkPeriod > 0 && Number.isFinite(checkPeriod)) {
|
|
2603
|
+
this._maintainTimer = setInterval(() => this._maintainOutboundCalls(), checkPeriod);
|
|
2604
|
+
this._maintainTimer.unref?.();
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
/** Fail outbound calls whose per-call {@link RpcCallTimeouts} elapsed —
|
|
2608
|
+
* the connect timeout while a call still awaits the wire, the run timeout
|
|
2609
|
+
* once it has been sent. Mirrors .NET `RpcOutboundCallTracker.Maintain`
|
|
2610
|
+
* (RpcCallTrackers.cs:108-207). Calls without timeouts (queries) never
|
|
2611
|
+
* time out here. */
|
|
2612
|
+
_maintainOutboundCalls() {
|
|
2613
|
+
const now = Date.now();
|
|
2614
|
+
const canRunTimeout = this._isConnected && (this._lastKeepAliveAt === 0 || now - this._lastKeepAliveAt <= this.keepAliveTimeoutMs);
|
|
2615
|
+
for (const call of [...this.outboundCalls.values()]) {
|
|
2616
|
+
const timeouts = call.timeouts;
|
|
2617
|
+
if (timeouts === void 0 || call.result.isCompleted)
|
|
2618
|
+
continue;
|
|
2619
|
+
let timedOut;
|
|
2620
|
+
if (call.sentAt === 0) {
|
|
2621
|
+
if (now - call.startedAt >= timeouts.connectTimeoutMs)
|
|
2622
|
+
timedOut = `connect timeout (${timeouts.connectTimeoutMs}ms)`;
|
|
2623
|
+
} else if (canRunTimeout && now - call.sentAt >= timeouts.runTimeoutMs)
|
|
2624
|
+
timedOut = `run timeout (${timeouts.runTimeoutMs}ms)`;
|
|
2625
|
+
if (timedOut === void 0)
|
|
2626
|
+
continue;
|
|
2627
|
+
this.outboundCalls.remove(call.callId);
|
|
2628
|
+
call.result.reject(new import_core7.TimeoutError(`'${this.ref}': call ${call.method}#${call.callId} timed out \u2014 ${timedOut}`));
|
|
2629
|
+
call.onDisconnect();
|
|
2630
|
+
}
|
|
2365
2631
|
}
|
|
2366
2632
|
get connection() {
|
|
2367
2633
|
return this._connection;
|
|
@@ -2372,6 +2638,9 @@ var RpcPeer = class {
|
|
|
2372
2638
|
get isConnected() {
|
|
2373
2639
|
return this._isConnected;
|
|
2374
2640
|
}
|
|
2641
|
+
get ownHandshakeIndex() {
|
|
2642
|
+
return this._ownHandshakeIndex;
|
|
2643
|
+
}
|
|
2375
2644
|
_setConnectionState(value) {
|
|
2376
2645
|
if (this._connectionState === value) return;
|
|
2377
2646
|
const previousState = this._connectionState;
|
|
@@ -2443,14 +2712,17 @@ var RpcPeer = class {
|
|
|
2443
2712
|
this._connection?.encoder,
|
|
2444
2713
|
this.hub.registry
|
|
2445
2714
|
);
|
|
2715
|
+
outboundCall.timeouts = options?.timeouts;
|
|
2446
2716
|
this.outboundCalls.register(outboundCall);
|
|
2447
2717
|
if (this._isConnected) {
|
|
2718
|
+
outboundCall.sentAt = Date.now();
|
|
2448
2719
|
this._sendWireData(outboundCall.serializedWireData);
|
|
2449
2720
|
} else {
|
|
2450
2721
|
const stateHandler = (state) => {
|
|
2451
2722
|
if (state !== 3 /* Connected */) return;
|
|
2452
2723
|
this.connectionStateChanged.remove(stateHandler);
|
|
2453
2724
|
if (outboundCall.result.isCompleted) return;
|
|
2725
|
+
outboundCall.sentAt = Date.now();
|
|
2454
2726
|
this._sendWireData(outboundCall.serializedWireData);
|
|
2455
2727
|
};
|
|
2456
2728
|
this.connectionStateChanged.add(stateHandler);
|
|
@@ -2460,9 +2732,9 @@ var RpcPeer = class {
|
|
|
2460
2732
|
if (signal !== void 0) {
|
|
2461
2733
|
const onAbort = () => {
|
|
2462
2734
|
if (this.outboundCalls.remove(callId) !== void 0) {
|
|
2463
|
-
outboundCall.result.reject(
|
|
2735
|
+
outboundCall.result.reject((0, import_core7.cancellationError)("Call cancelled."));
|
|
2464
2736
|
outboundCall.onDisconnect();
|
|
2465
|
-
if (this._connection !== void 0)
|
|
2737
|
+
if (this._isConnected && this._connection !== void 0)
|
|
2466
2738
|
this.hub.systemCallSender.cancel(this._connection, this.serializationFormat, callId);
|
|
2467
2739
|
}
|
|
2468
2740
|
};
|
|
@@ -2492,10 +2764,15 @@ var RpcPeer = class {
|
|
|
2492
2764
|
}
|
|
2493
2765
|
close() {
|
|
2494
2766
|
infoLog?.log(`'${this.ref}': Stopping`);
|
|
2767
|
+
if (this._maintainTimer !== void 0) {
|
|
2768
|
+
clearInterval(this._maintainTimer);
|
|
2769
|
+
this._maintainTimer = void 0;
|
|
2770
|
+
}
|
|
2495
2771
|
this.remoteObjects.disconnectAll();
|
|
2496
2772
|
this.sharedObjects.disconnectAll();
|
|
2497
|
-
this.outboundCalls.rejectAll(
|
|
2773
|
+
this.outboundCalls.rejectAll((0, import_core7.cancellationError)("Peer closed."));
|
|
2498
2774
|
this.outboundCalls.invalidateAll();
|
|
2775
|
+
this.inboundCalls.clear();
|
|
2499
2776
|
this._connection?.close();
|
|
2500
2777
|
this.hub.peers.delete(this.ref);
|
|
2501
2778
|
this._setConnectionState(0 /* Disconnected */);
|
|
@@ -2534,12 +2811,18 @@ var RpcPeer = class {
|
|
|
2534
2811
|
if (method === RpcSystemCalls.handshake) {
|
|
2535
2812
|
const raw = args[0];
|
|
2536
2813
|
if (raw !== void 0) {
|
|
2814
|
+
const obj = raw ?? {};
|
|
2537
2815
|
const handshake = Array.isArray(raw) ? {
|
|
2538
2816
|
RemotePeerId: raw[0],
|
|
2539
2817
|
RemoteHubId: raw[2],
|
|
2540
2818
|
ProtocolVersion: raw[3],
|
|
2541
2819
|
Index: raw[4]
|
|
2542
|
-
} :
|
|
2820
|
+
} : {
|
|
2821
|
+
RemotePeerId: obj.RemotePeerId ?? obj.remotePeerId,
|
|
2822
|
+
RemoteHubId: obj.RemoteHubId ?? obj.remoteHubId,
|
|
2823
|
+
ProtocolVersion: obj.ProtocolVersion ?? obj.protocolVersion,
|
|
2824
|
+
Index: obj.Index ?? obj.index
|
|
2825
|
+
};
|
|
2543
2826
|
this._onHandshakeReceived(handshake);
|
|
2544
2827
|
}
|
|
2545
2828
|
return;
|
|
@@ -2553,57 +2836,85 @@ var RpcPeer = class {
|
|
|
2553
2836
|
_handleInbound(message, args) {
|
|
2554
2837
|
const method = message.Method ?? "";
|
|
2555
2838
|
const relatedId = message.RelatedId ?? 0;
|
|
2556
|
-
const call = new RpcInboundCall(relatedId, method, args);
|
|
2557
2839
|
const methodDef = this.hub.serviceHost.getMethodDef(method);
|
|
2558
2840
|
const isNoWait = methodDef?.noWait === true;
|
|
2559
|
-
if (!isNoWait) {
|
|
2560
|
-
this.inboundCalls.register(call);
|
|
2561
|
-
}
|
|
2562
2841
|
const serviceHost = this.hub.serviceHost;
|
|
2563
|
-
|
|
2842
|
+
const dispatch = async (call2) => {
|
|
2564
2843
|
try {
|
|
2565
2844
|
const context = this._connection !== void 0 ? {
|
|
2566
2845
|
__rpcDispatch: true,
|
|
2567
2846
|
callId: relatedId,
|
|
2568
|
-
|
|
2847
|
+
callType: message.CallType ?? 0,
|
|
2848
|
+
connection: this._connection,
|
|
2849
|
+
signal: call2?.signal,
|
|
2850
|
+
peer: this
|
|
2569
2851
|
} : void 0;
|
|
2570
|
-
const result = await serviceHost.dispatch(
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2852
|
+
const result = await serviceHost.dispatch(method, args, context);
|
|
2853
|
+
if (methodDef?.stream === true) {
|
|
2854
|
+
if (this._connection !== void 0) {
|
|
2855
|
+
const stream = result instanceof RpcStream ? result : new RpcStream(result);
|
|
2856
|
+
const ref = stream.toRef(this);
|
|
2857
|
+
const send = () => {
|
|
2858
|
+
if (call2?.isCancelled === true) return;
|
|
2859
|
+
if (this._connection !== void 0)
|
|
2860
|
+
this.hub.systemCallSender.ok(
|
|
2861
|
+
this._connection,
|
|
2862
|
+
this.serializationFormat,
|
|
2863
|
+
relatedId,
|
|
2864
|
+
ref
|
|
2865
|
+
);
|
|
2866
|
+
};
|
|
2867
|
+
if (call2 !== void 0) {
|
|
2868
|
+
call2.setResult(send);
|
|
2869
|
+
this.inboundCalls.markCompleted(call2);
|
|
2870
|
+
}
|
|
2871
|
+
send();
|
|
2872
|
+
}
|
|
2873
|
+
} else if (call2 !== void 0) {
|
|
2874
|
+
const send = () => {
|
|
2875
|
+
if (call2.isCancelled) return;
|
|
2876
|
+
if (this._connection !== void 0)
|
|
2877
|
+
this.hub.systemCallSender.ok(
|
|
2878
|
+
this._connection,
|
|
2879
|
+
this.serializationFormat,
|
|
2880
|
+
relatedId,
|
|
2881
|
+
result
|
|
2882
|
+
);
|
|
2883
|
+
};
|
|
2884
|
+
call2.setResult(send);
|
|
2885
|
+
this.inboundCalls.markCompleted(call2);
|
|
2886
|
+
send();
|
|
2590
2887
|
}
|
|
2591
2888
|
} catch (e) {
|
|
2592
|
-
if (
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
this.
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2889
|
+
if (call2 !== void 0) {
|
|
2890
|
+
const send = () => {
|
|
2891
|
+
if (call2.isCancelled) return;
|
|
2892
|
+
if (this._connection !== void 0)
|
|
2893
|
+
this.hub.systemCallSender.error(
|
|
2894
|
+
this._connection,
|
|
2895
|
+
this.serializationFormat,
|
|
2896
|
+
relatedId,
|
|
2897
|
+
e
|
|
2898
|
+
);
|
|
2899
|
+
};
|
|
2900
|
+
call2.setResult(send);
|
|
2901
|
+
this.inboundCalls.markCompleted(call2);
|
|
2902
|
+
send();
|
|
2599
2903
|
}
|
|
2600
2904
|
debugLog2?.log(`'${this.ref}': Inbound call failed: ${method}#${relatedId}`, e);
|
|
2601
|
-
} finally {
|
|
2602
|
-
if (!isNoWait) {
|
|
2603
|
-
this.inboundCalls.remove(relatedId);
|
|
2604
|
-
}
|
|
2605
2905
|
}
|
|
2606
|
-
}
|
|
2906
|
+
};
|
|
2907
|
+
if (isNoWait) {
|
|
2908
|
+
void dispatch(void 0);
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2911
|
+
const call = new RpcInboundCall(relatedId, method, args);
|
|
2912
|
+
const existing = this.inboundCalls.getOrRegister(call);
|
|
2913
|
+
if (existing !== call) {
|
|
2914
|
+
existing.resendResult();
|
|
2915
|
+
return;
|
|
2916
|
+
}
|
|
2917
|
+
void dispatch(call);
|
|
2607
2918
|
}
|
|
2608
2919
|
/** Start sending `$sys.KeepAlive` pings and arm the silence watchdog.
|
|
2609
2920
|
* Called when the peer transitions into `Connected` (after handshake).
|
|
@@ -2659,15 +2970,17 @@ var RpcConnectionState = /* @__PURE__ */ ((RpcConnectionState2) => {
|
|
|
2659
2970
|
})(RpcConnectionState || {});
|
|
2660
2971
|
var RpcClientPeer = class extends RpcPeer {
|
|
2661
2972
|
_disposed = false;
|
|
2973
|
+
/** Aborted when the peer is stopped/disposed — threaded into the reconnect
|
|
2974
|
+
* delay so a pending delay rejects promptly instead of parking. */
|
|
2975
|
+
_stopController = new AbortController();
|
|
2662
2976
|
_tryIndex = 0;
|
|
2663
|
-
_handshakeIndex = 0;
|
|
2664
2977
|
/** Index from the MOST RECENTLY RECEIVED server handshake — sent back
|
|
2665
2978
|
* to the server in `$sys.Reconnect:3` so the server can verify we're
|
|
2666
2979
|
* talking about the current connection. Mirrors .NET's
|
|
2667
2980
|
* `connectionState.Handshake.Index` path in
|
|
2668
2981
|
* `RpcOutboundCallTracker.TryReconnect`. */
|
|
2669
2982
|
_remoteHandshakeIndex = 0;
|
|
2670
|
-
|
|
2983
|
+
_lastRemotePeerId;
|
|
2671
2984
|
_pendingHandshake;
|
|
2672
2985
|
_reconnectsAt = 0;
|
|
2673
2986
|
/** Base64url-encoded peer ID — matches .NET's RpcClientPeer.ClientId (Guid.ToBase64Url). */
|
|
@@ -2688,6 +3001,10 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2688
3001
|
* the connection attempt. Initialized from `hub.limits` at construction;
|
|
2689
3002
|
* set directly to override on a single peer. */
|
|
2690
3003
|
handshakeTimeoutMs;
|
|
3004
|
+
/** A connection that lived less than this before dropping does NOT reset
|
|
3005
|
+
* `_tryIndex`, so a crash-looping server sees growing reconnect delays.
|
|
3006
|
+
* Initialized from `hub.limits` at construction; override per peer. */
|
|
3007
|
+
prematureDisconnectTimeoutMs;
|
|
2691
3008
|
peerChanged = new import_core7.EventHandlerSet();
|
|
2692
3009
|
/** Fired when the server rejects the connection due to an unsupported serialization format (close code 4010). */
|
|
2693
3010
|
unsupportedFormat = new import_core7.EventHandlerSet();
|
|
@@ -2720,6 +3037,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2720
3037
|
super(hub, url);
|
|
2721
3038
|
this.connectTimeoutMs = hub.limits.connectTimeoutMs;
|
|
2722
3039
|
this.handshakeTimeoutMs = hub.limits.handshakeTimeoutMs;
|
|
3040
|
+
this.prematureDisconnectTimeoutMs = hub.limits.prematureDisconnectTimeoutMs;
|
|
2723
3041
|
this.clientId = guidToBase64Url(this.id);
|
|
2724
3042
|
const resolver = RpcSerializationFormatResolver.Default;
|
|
2725
3043
|
this.serializationFormat = resolver.get(parseFormatFromUrl(url) ?? resolver.defaultFormatKey);
|
|
@@ -2756,7 +3074,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2756
3074
|
try {
|
|
2757
3075
|
while (!this._disposed) {
|
|
2758
3076
|
this._setConnectionState(1 /* Connecting */);
|
|
2759
|
-
const retryDelay = this.hub.reconnectDelayer.getDelay(this._tryIndex);
|
|
3077
|
+
const retryDelay = this.hub.reconnectDelayer.getDelay(this._tryIndex, this._stopController.signal);
|
|
2760
3078
|
if (retryDelay.isLimitExceeded) {
|
|
2761
3079
|
warnLog6?.log(`'${this.ref}': Reconnect limit exceeded - shutting down`);
|
|
2762
3080
|
this._disposed = true;
|
|
@@ -2765,12 +3083,14 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2765
3083
|
this._setReconnectsAt(retryDelay.endsAt);
|
|
2766
3084
|
try {
|
|
2767
3085
|
await retryDelay.promise;
|
|
3086
|
+
} catch {
|
|
2768
3087
|
} finally {
|
|
2769
3088
|
this._setReconnectsAt(0);
|
|
2770
3089
|
}
|
|
2771
3090
|
if (this._disposed) break;
|
|
2772
3091
|
let lastCloseCode = 0;
|
|
2773
3092
|
let lastCloseReason = "";
|
|
3093
|
+
let connectedAt = 0;
|
|
2774
3094
|
try {
|
|
2775
3095
|
const connUrl = await this.connectionUrlResolver(this);
|
|
2776
3096
|
infoLog?.log(`'${this.ref}': Connecting to ${sanitizeUrl(connUrl)}`);
|
|
@@ -2817,7 +3137,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2817
3137
|
this.serializationFormat,
|
|
2818
3138
|
this.id,
|
|
2819
3139
|
this.hub.hubId,
|
|
2820
|
-
++this.
|
|
3140
|
+
++this._ownHandshakeIndex
|
|
2821
3141
|
);
|
|
2822
3142
|
} catch (e) {
|
|
2823
3143
|
warnLog6?.log(`'${this.ref}': Failed to send handshake`, e);
|
|
@@ -2841,20 +3161,10 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2841
3161
|
this._pendingHandshake = void 0;
|
|
2842
3162
|
}
|
|
2843
3163
|
this._remoteHandshakeIndex = remoteHandshake.Index ?? 0;
|
|
2844
|
-
const
|
|
2845
|
-
let isPeerChanged = false;
|
|
2846
|
-
if (remoteHubId !== void 0) {
|
|
2847
|
-
isPeerChanged = this._lastRemoteHubId !== void 0 && this._lastRemoteHubId !== remoteHubId;
|
|
2848
|
-
if (isPeerChanged) {
|
|
2849
|
-
this.inboundCalls.clear();
|
|
2850
|
-
infoLog?.log(`'${this.ref}': Peer changed`);
|
|
2851
|
-
this.peerChanged.trigger();
|
|
2852
|
-
}
|
|
2853
|
-
this._lastRemoteHubId = remoteHubId;
|
|
2854
|
-
}
|
|
3164
|
+
const isPeerChanged = this._detectPeerChange(remoteHandshake);
|
|
2855
3165
|
infoLog?.log(`'${this.ref}': Handshake succeeded, peerChanged=${isPeerChanged}`);
|
|
2856
3166
|
this._setConnectionState(3 /* Connected */);
|
|
2857
|
-
|
|
3167
|
+
connectedAt = Date.now();
|
|
2858
3168
|
await this._reconnect(isPeerChanged, closedController.signal);
|
|
2859
3169
|
await whenClosed;
|
|
2860
3170
|
} catch (e) {
|
|
@@ -2868,6 +3178,8 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2868
3178
|
this._disposed = true;
|
|
2869
3179
|
break;
|
|
2870
3180
|
}
|
|
3181
|
+
if (connectedAt !== 0 && Date.now() - connectedAt >= this.prematureDisconnectTimeoutMs)
|
|
3182
|
+
this._tryIndex = 0;
|
|
2871
3183
|
this._tryIndex++;
|
|
2872
3184
|
}
|
|
2873
3185
|
} finally {
|
|
@@ -2923,6 +3235,8 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2923
3235
|
continue;
|
|
2924
3236
|
}
|
|
2925
3237
|
eligible.push(call);
|
|
3238
|
+
if (call.sentAt !== 0)
|
|
3239
|
+
call.sentAt = Date.now();
|
|
2926
3240
|
}
|
|
2927
3241
|
let unknownIds = null;
|
|
2928
3242
|
if (!isPeerChanged && eligible.length > 0) {
|
|
@@ -2939,6 +3253,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
2939
3253
|
if (unknownIds !== null && !unknownIds.has(call.callId)) {
|
|
2940
3254
|
continue;
|
|
2941
3255
|
}
|
|
3256
|
+
call.sentAt = Date.now();
|
|
2942
3257
|
this._sendWireData(call.serializedWireData);
|
|
2943
3258
|
resentSinceYield++;
|
|
2944
3259
|
if (resentSinceYield >= YIELD_BATCH) {
|
|
@@ -3004,30 +3319,80 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
3004
3319
|
return new Set(eligible.map((c) => c.callId));
|
|
3005
3320
|
}
|
|
3006
3321
|
}
|
|
3322
|
+
/** Detects a server-peer identity change by comparing the incoming
|
|
3323
|
+
* handshake's `RemotePeerId` with the last one — C#
|
|
3324
|
+
* `RpcHandshake.GetPeerChangeKind` keys off `RemotePeerId`, not
|
|
3325
|
+
* `RemoteHubId` (an idle server peer can be replaced while keeping the
|
|
3326
|
+
* same hub id). On a change it clears the inbound tracker and fires
|
|
3327
|
+
* `peerChanged`; the very first handshake is never a change. */
|
|
3328
|
+
_detectPeerChange(handshake) {
|
|
3329
|
+
const remotePeerId = handshake.RemotePeerId;
|
|
3330
|
+
if (remotePeerId === void 0)
|
|
3331
|
+
return false;
|
|
3332
|
+
const isPeerChanged = this._lastRemotePeerId !== void 0 && this._lastRemotePeerId !== remotePeerId;
|
|
3333
|
+
this._lastRemotePeerId = remotePeerId;
|
|
3334
|
+
if (isPeerChanged) {
|
|
3335
|
+
this.inboundCalls.clear();
|
|
3336
|
+
infoLog?.log(`'${this.ref}': Peer changed`);
|
|
3337
|
+
this.peerChanged.trigger();
|
|
3338
|
+
}
|
|
3339
|
+
return isPeerChanged;
|
|
3340
|
+
}
|
|
3007
3341
|
_onHandshakeReceived(handshake) {
|
|
3008
3342
|
this._pendingHandshake?.resolve(handshake);
|
|
3009
3343
|
}
|
|
3010
3344
|
close() {
|
|
3011
3345
|
this._disposed = true;
|
|
3346
|
+
this._stopController.abort();
|
|
3012
3347
|
super.close();
|
|
3013
3348
|
}
|
|
3014
3349
|
};
|
|
3015
3350
|
var RpcServerPeer = class extends RpcPeer {
|
|
3016
|
-
|
|
3017
|
-
|
|
3351
|
+
/** Grace period before an idle (disconnected) server peer is stopped and
|
|
3352
|
+
* removed from `hub.peers`; a re-`accept()` within it cancels removal.
|
|
3353
|
+
* Snapshotted from `hub.limits` at construction (F11). */
|
|
3354
|
+
serverPeerCloseTimeoutMs;
|
|
3355
|
+
_closeTimer;
|
|
3356
|
+
_isClosed = false;
|
|
3018
3357
|
constructor(hub, ref) {
|
|
3019
3358
|
super(hub, ref);
|
|
3359
|
+
this.serverPeerCloseTimeoutMs = hub.limits.serverPeerCloseTimeoutMs;
|
|
3020
3360
|
}
|
|
3021
3361
|
/** Accept an incoming connection — sets up message handling and handshake response. */
|
|
3022
3362
|
accept(conn) {
|
|
3363
|
+
this._clearCloseTimer();
|
|
3023
3364
|
this._setConnectionState(1 /* Connecting */);
|
|
3024
3365
|
this.setupConnection(conn);
|
|
3025
3366
|
this._setConnectionState(2 /* Handshaking */);
|
|
3026
3367
|
conn.closed.add(() => {
|
|
3027
3368
|
if (this._connection !== conn && this._connection !== void 0) return;
|
|
3028
3369
|
this.sharedObjects.disconnectAll();
|
|
3370
|
+
this._armCloseTimer();
|
|
3029
3371
|
});
|
|
3030
3372
|
}
|
|
3373
|
+
close() {
|
|
3374
|
+
this._isClosed = true;
|
|
3375
|
+
this._clearCloseTimer();
|
|
3376
|
+
super.close();
|
|
3377
|
+
}
|
|
3378
|
+
_armCloseTimer() {
|
|
3379
|
+
this._clearCloseTimer();
|
|
3380
|
+
if (this._isClosed) return;
|
|
3381
|
+
const timeout = this.serverPeerCloseTimeoutMs;
|
|
3382
|
+
if (!(timeout > 0) || !Number.isFinite(timeout)) return;
|
|
3383
|
+
this._closeTimer = setTimeout(() => {
|
|
3384
|
+
this._closeTimer = void 0;
|
|
3385
|
+
if (this._connection === void 0)
|
|
3386
|
+
this.close();
|
|
3387
|
+
}, timeout);
|
|
3388
|
+
this._closeTimer.unref?.();
|
|
3389
|
+
}
|
|
3390
|
+
_clearCloseTimer() {
|
|
3391
|
+
if (this._closeTimer !== void 0) {
|
|
3392
|
+
clearTimeout(this._closeTimer);
|
|
3393
|
+
this._closeTimer = void 0;
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3031
3396
|
_onHandshakeReceived(_handshake) {
|
|
3032
3397
|
if (this._connection !== void 0) {
|
|
3033
3398
|
this.hub.systemCallSender.handshake(
|
|
@@ -3035,7 +3400,7 @@ var RpcServerPeer = class extends RpcPeer {
|
|
|
3035
3400
|
this.serializationFormat,
|
|
3036
3401
|
this.id,
|
|
3037
3402
|
this.hub.hubId,
|
|
3038
|
-
|
|
3403
|
+
++this._ownHandshakeIndex
|
|
3039
3404
|
);
|
|
3040
3405
|
this._setConnectionState(3 /* Connected */);
|
|
3041
3406
|
}
|
|
@@ -3074,6 +3439,12 @@ var RpcLimits = class _RpcLimits {
|
|
|
3074
3439
|
/** Process-wide default. Hubs read from this when they're constructed
|
|
3075
3440
|
* unless caller assigns a custom instance to `hub.limits`. */
|
|
3076
3441
|
static Default = new _RpcLimits();
|
|
3442
|
+
/** Dev preset with a relaxed keep-alive timeout (5 min), matching .NET's
|
|
3443
|
+
* debugger-attached `KeepAlivePeriod` (RpcLimits.cs:46-54). Activate
|
|
3444
|
+
* explicitly — e.g. `RpcLimits.Default = RpcLimits.Debug` or per-hub
|
|
3445
|
+
* `hub.limits = RpcLimits.Debug`. Never switched on automatically: the
|
|
3446
|
+
* TS side can't detect a debugger on the server (decision D4). */
|
|
3447
|
+
static Debug = new _RpcLimits({ keepAliveTimeoutMs: 3e5 });
|
|
3077
3448
|
/** Max time to wait for the WebSocket to enter the OPEN state. On a
|
|
3078
3449
|
* hung connect (mobile after network change, half-open after sleep)
|
|
3079
3450
|
* the browser can take ~2 min to emit `onerror`/`onclose`; without
|
|
@@ -3081,6 +3452,22 @@ var RpcLimits = class _RpcLimits {
|
|
|
3081
3452
|
connectTimeoutMs = 1e4;
|
|
3082
3453
|
/** Max time to wait for the server's handshake response after WS opens. */
|
|
3083
3454
|
handshakeTimeoutMs = 1e4;
|
|
3455
|
+
/** If a connection lived less than this before dropping, a graceful close
|
|
3456
|
+
* still counts as a failed attempt — the client keeps its growing
|
|
3457
|
+
* `_tryIndex` instead of resetting it, so a crash-looping server sees
|
|
3458
|
+
* increasing reconnect delays via `RetryDelaySeq`. Mirrors .NET
|
|
3459
|
+
* `RpcLimits.PrematureDisconnectTimeout` (RpcLimits.cs:18). */
|
|
3460
|
+
prematureDisconnectTimeoutMs = 15e3;
|
|
3461
|
+
/** Max completed inbound calls retained for duplicate-frame dedup
|
|
3462
|
+
* (a resent call id re-sends the computed result instead of
|
|
3463
|
+
* re-executing the handler). Deviation from .NET, which unregisters a
|
|
3464
|
+
* call once its result is sent: TS clients blind-resend on reconnect,
|
|
3465
|
+
* so completed calls are retained — bounded by this limit (oldest
|
|
3466
|
+
* evicted first) to cap memory on long-lived connections. */
|
|
3467
|
+
completedInboundCallsLimit = 1e3;
|
|
3468
|
+
/** How often each peer's maintenance loop scans outbound calls for
|
|
3469
|
+
* timeouts (R12). Mirrors .NET `RpcLimits.CallTimeoutCheckPeriod`. */
|
|
3470
|
+
callTimeoutCheckPeriodMs = 1e3;
|
|
3084
3471
|
/** Outbound `$sys.KeepAlive` send period. */
|
|
3085
3472
|
keepAlivePeriodMs = 1e4;
|
|
3086
3473
|
/** Reaper threshold — connection is force-closed (and the reconnect
|
|
@@ -3090,6 +3477,13 @@ var RpcLimits = class _RpcLimits {
|
|
|
3090
3477
|
* `keepAlivePeriodMs + stall_duration`, so with these defaults the
|
|
3091
3478
|
* tolerated stall is ~15 s. */
|
|
3092
3479
|
keepAliveTimeoutMs = 25e3;
|
|
3480
|
+
/** How long a server peer stays in `hub.peers` after its connection closes
|
|
3481
|
+
* before it's stopped and removed. A same-peer reconnect (`accept()`)
|
|
3482
|
+
* within this window cancels the pending removal, so brief drops don't
|
|
3483
|
+
* discard the peer's trackers. Mirrors .NET's server-peer shutdown timeout
|
|
3484
|
+
* (`RpcPeerOptions.ServerPeerShutdownTimeoutProvider`, 3-15 min); the TS
|
|
3485
|
+
* port uses a fixed floor-matching default. */
|
|
3486
|
+
serverPeerCloseTimeoutMs = 18e4;
|
|
3093
3487
|
constructor(overrides) {
|
|
3094
3488
|
if (overrides) Object.assign(this, overrides);
|
|
3095
3489
|
}
|
|
@@ -3100,7 +3494,7 @@ var import_core8 = require("@actuallab/core");
|
|
|
3100
3494
|
var RpcClientPeerReconnectDelayer = class extends import_core8.RetryDelayer {
|
|
3101
3495
|
constructor() {
|
|
3102
3496
|
super();
|
|
3103
|
-
this.delays = import_core8.RetryDelaySeq.
|
|
3497
|
+
this.delays = import_core8.RetryDelaySeq.exp(100, 1e4);
|
|
3104
3498
|
}
|
|
3105
3499
|
};
|
|
3106
3500
|
|
|
@@ -3108,13 +3502,14 @@ var RpcClientPeerReconnectDelayer = class extends import_core8.RetryDelayer {
|
|
|
3108
3502
|
var { warnLog: warnLog7 } = getLogs("RpcServiceHost");
|
|
3109
3503
|
var RpcServiceHost = class {
|
|
3110
3504
|
_methods = /* @__PURE__ */ new Map();
|
|
3111
|
-
register(def, impl) {
|
|
3505
|
+
register(def, impl, receiver = impl) {
|
|
3112
3506
|
for (const methodDef of def.methods.values()) {
|
|
3113
3507
|
const fn = impl[methodDef.name];
|
|
3114
3508
|
if (!fn) continue;
|
|
3115
3509
|
this._methods.set(wireMethodName(methodDef), {
|
|
3116
3510
|
def: methodDef,
|
|
3117
|
-
fn
|
|
3511
|
+
fn,
|
|
3512
|
+
receiver
|
|
3118
3513
|
});
|
|
3119
3514
|
}
|
|
3120
3515
|
}
|
|
@@ -3125,9 +3520,9 @@ var RpcServiceHost = class {
|
|
|
3125
3520
|
throw new Error(`Method not found: ${wireMethod}`);
|
|
3126
3521
|
}
|
|
3127
3522
|
if (context !== void 0 && entry.def.callTypeId !== 0) {
|
|
3128
|
-
return await entry.fn(...args, context);
|
|
3523
|
+
return await entry.fn.call(entry.receiver, ...args, context);
|
|
3129
3524
|
}
|
|
3130
|
-
return await entry.fn(...args);
|
|
3525
|
+
return await entry.fn.call(entry.receiver, ...args);
|
|
3131
3526
|
}
|
|
3132
3527
|
getMethodDef(wireMethod) {
|
|
3133
3528
|
return this._methods.get(wireMethod)?.def;
|
|
@@ -3135,6 +3530,7 @@ var RpcServiceHost = class {
|
|
|
3135
3530
|
};
|
|
3136
3531
|
|
|
3137
3532
|
// src/rpc-decorators.ts
|
|
3533
|
+
var import_core9 = require("@actuallab/core");
|
|
3138
3534
|
var METHODS_META = /* @__PURE__ */ Symbol.for("actuallab.methods");
|
|
3139
3535
|
var SERVICE_META = /* @__PURE__ */ Symbol.for("actuallab.service");
|
|
3140
3536
|
function getMethodsMeta(cls) {
|
|
@@ -3145,17 +3541,17 @@ function getServiceMeta(cls) {
|
|
|
3145
3541
|
}
|
|
3146
3542
|
function rpcService(serviceName) {
|
|
3147
3543
|
return function(_target, context) {
|
|
3148
|
-
const meta = context.metadata
|
|
3544
|
+
const meta = (0, import_core9.ownMetadata)(context.metadata, SERVICE_META);
|
|
3149
3545
|
meta.name = serviceName;
|
|
3150
3546
|
};
|
|
3151
3547
|
}
|
|
3152
3548
|
function rpcMethod(options) {
|
|
3153
3549
|
return function(target, context) {
|
|
3154
3550
|
const methodName = String(context.name);
|
|
3155
|
-
const methods = context.metadata
|
|
3551
|
+
const methods = (0, import_core9.ownMetadata)(context.metadata, METHODS_META);
|
|
3156
3552
|
methods[methodName] = {
|
|
3157
3553
|
...methods[methodName],
|
|
3158
|
-
argCount: target
|
|
3554
|
+
argCount: (0, import_core9.resolveArgCount)(target, options?.argCount, methodName),
|
|
3159
3555
|
returns: options?.returns,
|
|
3160
3556
|
remoteExecutionMode: options?.remoteExecutionMode
|
|
3161
3557
|
};
|
|
@@ -3701,7 +4097,7 @@ var RpcHub = class {
|
|
|
3701
4097
|
hubId;
|
|
3702
4098
|
peers = /* @__PURE__ */ new Map();
|
|
3703
4099
|
serviceHost;
|
|
3704
|
-
systemCallSender
|
|
4100
|
+
systemCallSender;
|
|
3705
4101
|
systemCallHandler = new RpcSystemCallHandler();
|
|
3706
4102
|
/** Method registry for compact format hash ↔ name resolution.
|
|
3707
4103
|
* Created lazily, populated by addService/addClient. */
|
|
@@ -3709,6 +4105,14 @@ var RpcHub = class {
|
|
|
3709
4105
|
/** Shared reconnect delayer used by every client peer in this hub. Swap in
|
|
3710
4106
|
* a custom subclass (e.g. app-level signal-gated) before peers start. */
|
|
3711
4107
|
reconnectDelayer = new RpcClientPeerReconnectDelayer();
|
|
4108
|
+
/** Cache of client proxies keyed by peer, then by service name — so
|
|
4109
|
+
* repeated {@link addClient} calls for the same service+peer return one
|
|
4110
|
+
* proxy. All consumers then share a single computed/call/invalidation
|
|
4111
|
+
* stream per logical value, matching .NET's singleton client proxies (F8).
|
|
4112
|
+
* A later def registered under the same name extends the existing proxy
|
|
4113
|
+
* with its extra methods (superset), so no consumer ever receives a proxy
|
|
4114
|
+
* missing methods the def declares. */
|
|
4115
|
+
_clientProxies = /* @__PURE__ */ new WeakMap();
|
|
3712
4116
|
/** Connection-lifecycle timing limits. Peers constructed against this hub
|
|
3713
4117
|
* read their initial `*Ms` field values from this instance; later
|
|
3714
4118
|
* mutations affect only peers built afterward (existing peers can be
|
|
@@ -3726,6 +4130,7 @@ var RpcHub = class {
|
|
|
3726
4130
|
constructor(hubId) {
|
|
3727
4131
|
this.hubId = hubId ?? crypto.randomUUID();
|
|
3728
4132
|
this.serviceHost = new RpcServiceHost();
|
|
4133
|
+
this.systemCallSender = this._createSystemCallSender();
|
|
3729
4134
|
this.systemCallSender.registry = this.registry;
|
|
3730
4135
|
for (const methodName of Object.values(RpcSystemCalls)) {
|
|
3731
4136
|
this.registry.register(methodName);
|
|
@@ -3786,50 +4191,38 @@ var RpcHub = class {
|
|
|
3786
4191
|
if (!fn) continue;
|
|
3787
4192
|
wrappedImpl[methodDef.name] = methodDef.callTypeId !== 0 ? this._wrapServerMethod(methodDef, fn, impl) : fn;
|
|
3788
4193
|
}
|
|
3789
|
-
this.serviceHost.register(def, wrappedImpl);
|
|
4194
|
+
this.serviceHost.register(def, wrappedImpl, impl);
|
|
3790
4195
|
this.registry.registerService(def.name, def.methods);
|
|
3791
4196
|
}
|
|
3792
|
-
/** Create a typed client proxy for a service on a remote peer.
|
|
4197
|
+
/** Create a typed client proxy for a service on a remote peer. Repeated calls
|
|
4198
|
+
* for the same (peer, service name) return one shared proxy; a def carrying
|
|
4199
|
+
* extra methods extends it (F8). */
|
|
3793
4200
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- T is used for caller-specified proxy type
|
|
3794
4201
|
addClient(peer, defOrContract) {
|
|
3795
4202
|
const def = this._resolveServiceDef(defOrContract);
|
|
3796
4203
|
this.registry.registerService(def.name, def.methods);
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
byArgCount = /* @__PURE__ */ new Map();
|
|
3802
|
-
overloads.set(methodDef.name, byArgCount);
|
|
3803
|
-
}
|
|
3804
|
-
byArgCount.set(
|
|
3805
|
-
methodDef.argCount,
|
|
3806
|
-
this._createClientMethod(peer, methodDef)
|
|
3807
|
-
);
|
|
4204
|
+
let byService = this._clientProxies.get(peer);
|
|
4205
|
+
if (byService === void 0) {
|
|
4206
|
+
byService = /* @__PURE__ */ new Map();
|
|
4207
|
+
this._clientProxies.set(peer, byService);
|
|
3808
4208
|
}
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
methods.set(name, singleFn);
|
|
3814
|
-
} else {
|
|
3815
|
-
methods.set(name, (...args) => {
|
|
3816
|
-
const fn = byArgCount.get(args.length);
|
|
3817
|
-
if (!fn)
|
|
3818
|
-
throw new Error(
|
|
3819
|
-
`No overload of ${name} accepts ${args.length} arguments`
|
|
3820
|
-
);
|
|
3821
|
-
return fn(...args);
|
|
3822
|
-
});
|
|
3823
|
-
}
|
|
4209
|
+
let entry = byService.get(def.name);
|
|
4210
|
+
if (entry === void 0) {
|
|
4211
|
+
entry = this._createClientProxy();
|
|
4212
|
+
byService.set(def.name, entry);
|
|
3824
4213
|
}
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
});
|
|
4214
|
+
this._extendClientProxy(peer, entry, def);
|
|
4215
|
+
return entry.proxy;
|
|
3828
4216
|
}
|
|
3829
4217
|
close() {
|
|
3830
4218
|
for (const peer of this.peers.values()) peer.close();
|
|
3831
4219
|
this.peers.clear();
|
|
3832
4220
|
}
|
|
4221
|
+
/** Create the hub's system-call sender. Override in FusionHub to add
|
|
4222
|
+
* Fusion-specific system calls (e.g. $sys-c.Invalidate). */
|
|
4223
|
+
_createSystemCallSender() {
|
|
4224
|
+
return new RpcSystemCallSender();
|
|
4225
|
+
}
|
|
3833
4226
|
/** Build an RpcServiceDef from decorator metadata on a contract class. Override in FusionHub for compute support. */
|
|
3834
4227
|
_buildServiceDef(cls) {
|
|
3835
4228
|
const svcMeta = getServiceMeta(cls);
|
|
@@ -3887,12 +4280,10 @@ var RpcHub = class {
|
|
|
3887
4280
|
throw new Error(
|
|
3888
4281
|
`Expected stream reference, got: ${JSON.stringify(result)}`
|
|
3889
4282
|
);
|
|
3890
|
-
|
|
3891
|
-
peer.remoteObjects.register(stream);
|
|
3892
|
-
return stream;
|
|
4283
|
+
return new RpcStream(ref, peer);
|
|
3893
4284
|
};
|
|
3894
4285
|
}
|
|
3895
|
-
const callOptions = methodDef.callTypeId !== 0 || mode !== RpcRemoteExecutionMode.Default ? { callTypeId: methodDef.callTypeId, remoteExecutionMode: mode } : void 0;
|
|
4286
|
+
const callOptions = methodDef.callTypeId !== 0 || mode !== RpcRemoteExecutionMode.Default || methodDef.timeouts !== void 0 ? { callTypeId: methodDef.callTypeId, remoteExecutionMode: mode, timeouts: methodDef.timeouts } : void 0;
|
|
3896
4287
|
return async (...args) => {
|
|
3897
4288
|
if (mustCheckConnection && !peer.isConnected)
|
|
3898
4289
|
throw new Error("Outbound call failed: not connected and AwaitForConnection is not set.");
|
|
@@ -3902,7 +4293,58 @@ var RpcHub = class {
|
|
|
3902
4293
|
return resolveStreamRefs(result, peer);
|
|
3903
4294
|
};
|
|
3904
4295
|
}
|
|
4296
|
+
// Private methods
|
|
4297
|
+
_createClientProxy() {
|
|
4298
|
+
const methods = /* @__PURE__ */ new Map();
|
|
4299
|
+
const proxy = new Proxy({}, {
|
|
4300
|
+
get: (_target, prop) => typeof prop === "string" ? methods.get(prop) : void 0
|
|
4301
|
+
});
|
|
4302
|
+
return { proxy, methods, overloads: /* @__PURE__ */ new Map() };
|
|
4303
|
+
}
|
|
4304
|
+
_extendClientProxy(peer, entry, def) {
|
|
4305
|
+
for (const methodDef of def.methods.values()) {
|
|
4306
|
+
const existing = entry.overloads.get(methodDef.name)?.get(methodDef.argCount);
|
|
4307
|
+
if (existing !== void 0 && !isSameWireMethod(existing.def, methodDef))
|
|
4308
|
+
throw new Error(
|
|
4309
|
+
`RpcHub.addClient: conflicting definition of '${wireMethodName(methodDef)}' under service '${def.name}' \u2014 a different signature is already registered.`
|
|
4310
|
+
);
|
|
4311
|
+
}
|
|
4312
|
+
const changed = /* @__PURE__ */ new Set();
|
|
4313
|
+
for (const methodDef of def.methods.values()) {
|
|
4314
|
+
let byArgCount = entry.overloads.get(methodDef.name);
|
|
4315
|
+
if (byArgCount === void 0) {
|
|
4316
|
+
byArgCount = /* @__PURE__ */ new Map();
|
|
4317
|
+
entry.overloads.set(methodDef.name, byArgCount);
|
|
4318
|
+
}
|
|
4319
|
+
if (byArgCount.has(methodDef.argCount))
|
|
4320
|
+
continue;
|
|
4321
|
+
byArgCount.set(methodDef.argCount, {
|
|
4322
|
+
def: methodDef,
|
|
4323
|
+
fn: this._createClientMethod(peer, methodDef)
|
|
4324
|
+
});
|
|
4325
|
+
changed.add(methodDef.name);
|
|
4326
|
+
}
|
|
4327
|
+
for (const name of changed) {
|
|
4328
|
+
const byArgCount = entry.overloads.get(name);
|
|
4329
|
+
entry.methods.set(name, buildOverloadFn(name, byArgCount));
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
3905
4332
|
};
|
|
4333
|
+
function buildOverloadFn(name, byArgCount) {
|
|
4334
|
+
if (byArgCount.size === 1) {
|
|
4335
|
+
const [[, only]] = byArgCount;
|
|
4336
|
+
return only.fn;
|
|
4337
|
+
}
|
|
4338
|
+
return (...args) => {
|
|
4339
|
+
const overload = byArgCount.get(args.length);
|
|
4340
|
+
if (overload === void 0)
|
|
4341
|
+
throw new Error(`No overload of ${name} accepts ${args.length} arguments`);
|
|
4342
|
+
return overload.fn(...args);
|
|
4343
|
+
};
|
|
4344
|
+
}
|
|
4345
|
+
function isSameWireMethod(a, b) {
|
|
4346
|
+
return a.wireArgCount === b.wireArgCount && a.callTypeId === b.callTypeId && a.stream === b.stream && a.noWait === b.noWait && a.remoteExecutionMode === b.remoteExecutionMode;
|
|
4347
|
+
}
|
|
3906
4348
|
|
|
3907
4349
|
// src/rpc-peer-ref-builder.ts
|
|
3908
4350
|
var RpcPeerRefBuilder = class {
|
|
@@ -3969,7 +4411,7 @@ function createMethod(peer, methodDef) {
|
|
|
3969
4411
|
throw new Error("Outbound call failed: not connected and AwaitForConnection is not set.");
|
|
3970
4412
|
const callArgs = args.slice(0, methodDef.argCount);
|
|
3971
4413
|
const wireName = wireMethodName(methodDef);
|
|
3972
|
-
const options = methodDef.callTypeId !== 0 || mode !== RpcRemoteExecutionMode.Default ? { callTypeId: methodDef.callTypeId, remoteExecutionMode: mode } : void 0;
|
|
4414
|
+
const options = methodDef.callTypeId !== 0 || mode !== RpcRemoteExecutionMode.Default || methodDef.timeouts !== void 0 ? { callTypeId: methodDef.callTypeId, remoteExecutionMode: mode, timeouts: methodDef.timeouts } : void 0;
|
|
3973
4415
|
const outboundCall = peer.call(wireName, callArgs, options);
|
|
3974
4416
|
return outboundCall.result.promise;
|
|
3975
4417
|
};
|
|
@@ -4009,14 +4451,14 @@ function getStateDescription(state, useLastError = false) {
|
|
|
4009
4451
|
}
|
|
4010
4452
|
|
|
4011
4453
|
// src/rpc-peer-state-monitor.ts
|
|
4012
|
-
var
|
|
4454
|
+
var import_core10 = require("@actuallab/core");
|
|
4013
4455
|
var { infoLog: infoLog2 } = getLogs("RpcPeerStateMonitor");
|
|
4014
4456
|
var JustConnectedPeriodMs = 1500;
|
|
4015
4457
|
var JustDisconnectedPeriodMs = 3e3;
|
|
4016
4458
|
var MinReconnectsInMs = 1e3;
|
|
4017
4459
|
var RpcPeerStateMonitor = class {
|
|
4018
4460
|
peer;
|
|
4019
|
-
stateChanged = new
|
|
4461
|
+
stateChanged = new import_core10.EventHandlerSet();
|
|
4020
4462
|
_rawConnected;
|
|
4021
4463
|
_connectedAt = 0;
|
|
4022
4464
|
_disconnectedAt = 0;
|
|
@@ -4165,8 +4607,10 @@ var RpcPeerStateMonitor = class {
|
|
|
4165
4607
|
ENVELOPE_DELIMITER,
|
|
4166
4608
|
FRAME_DELIMITER,
|
|
4167
4609
|
IncreasingSeqCompressor,
|
|
4610
|
+
REMOTE_EXCEPTION_TYPE_REF,
|
|
4168
4611
|
RPC_CLOSE_CODE_UNSUPPORTED_FORMAT,
|
|
4169
4612
|
RpcCallStage,
|
|
4613
|
+
RpcCallTimeouts,
|
|
4170
4614
|
RpcCallTypeId,
|
|
4171
4615
|
RpcClientPeer,
|
|
4172
4616
|
RpcClientPeerReconnectDelayer,
|
|
@@ -4221,6 +4665,7 @@ var RpcPeerStateMonitor = class {
|
|
|
4221
4665
|
isConnected,
|
|
4222
4666
|
likelyConnected,
|
|
4223
4667
|
parseStreamRef,
|
|
4668
|
+
readPolymorphismMarker,
|
|
4224
4669
|
resolveStreamRefs,
|
|
4225
4670
|
rpcMethod,
|
|
4226
4671
|
rpcService,
|
|
@@ -4232,6 +4677,7 @@ var RpcPeerStateMonitor = class {
|
|
|
4232
4677
|
splitBinaryFrame,
|
|
4233
4678
|
splitCompactBinaryFrame,
|
|
4234
4679
|
splitFrame,
|
|
4680
|
+
toExceptionInfo,
|
|
4235
4681
|
wireMethodName,
|
|
4236
4682
|
xxh3_64,
|
|
4237
4683
|
xxh3_64str
|