@actuallab/rpc 14.3.5 → 14.3.26
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 +46 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +46 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1285,6 +1285,15 @@ var RpcSystemCallHandler = class {
|
|
|
1285
1285
|
);
|
|
1286
1286
|
return;
|
|
1287
1287
|
}
|
|
1288
|
+
if (!peer.tryClaimReconnect()) {
|
|
1289
|
+
peer.hub.systemCallSender.error(
|
|
1290
|
+
peer.connection,
|
|
1291
|
+
peer.serializationFormat,
|
|
1292
|
+
relatedId,
|
|
1293
|
+
new Error("TooLateToReconnect: this connection was already reconnected")
|
|
1294
|
+
);
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1288
1297
|
let unknownIds = [];
|
|
1289
1298
|
try {
|
|
1290
1299
|
const stagesRaw = args[1];
|
|
@@ -2025,6 +2034,7 @@ function _compactBufferedUnsentSuffix(buffer, firstUnsentIndex, canSkipTo) {
|
|
|
2025
2034
|
var { warnLog: warnLog6 } = getLogs("RpcStream");
|
|
2026
2035
|
var DEFAULT_ACK_PERIOD2 = 30;
|
|
2027
2036
|
var DEFAULT_ACK_ADVANCE2 = 61;
|
|
2037
|
+
var MAX_BATCH_SIZE = 1024;
|
|
2028
2038
|
function parseStreamRef(value) {
|
|
2029
2039
|
if (typeof value === "string") {
|
|
2030
2040
|
const parts = value.split(",");
|
|
@@ -2212,6 +2222,10 @@ var RpcStream = class _RpcStream {
|
|
|
2212
2222
|
this._failProtocol(`invalid $sys.B index/length: ${String(index)}/${String(items.length)}`);
|
|
2213
2223
|
return;
|
|
2214
2224
|
}
|
|
2225
|
+
if (items.length > MAX_BATCH_SIZE) {
|
|
2226
|
+
this._failProtocol(`$sys.B batch is too large: ${items.length} > ${MAX_BATCH_SIZE}`);
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
2215
2229
|
if (index > this._nextExpectedIndex) {
|
|
2216
2230
|
warnLog6?.log(`batch index gap: localId=${this.id.localId}, expected=${this._nextExpectedIndex}, received=${index}`);
|
|
2217
2231
|
if (!this.allowReconnect) {
|
|
@@ -2346,11 +2360,23 @@ var RpcStream = class _RpcStream {
|
|
|
2346
2360
|
// last acknowledged index. The sending side is the only place that window
|
|
2347
2361
|
// was ever enforced, which left a flooding remote free to grow our buffer
|
|
2348
2362
|
// without bound (I6) — so we enforce our half of the contract here.
|
|
2363
|
+
//
|
|
2364
|
+
// The window is credited by consumption, never by `_ackSentUpTo`: a reset ack
|
|
2365
|
+
// re-bases to what we've *received*, so crediting it would let a peer ratchet
|
|
2366
|
+
// its own limit up by alternating "fill the window" with a forced gap.
|
|
2367
|
+
// Twice ackAdvance is what a conforming sender can leave buffered (ackAdvance
|
|
2368
|
+
// in steady state, plus ackAdvance more after a reset re-bases it), and
|
|
2369
|
+
// ackPeriod covers the lag of acks behind consumption.
|
|
2349
2370
|
_isBeyondAckWindow(nextExpectedIndex) {
|
|
2350
|
-
|
|
2371
|
+
if (this.ackAdvance <= 0)
|
|
2372
|
+
return false;
|
|
2373
|
+
return nextExpectedIndex > this._nextConsumedIndex + this._ackWindowSize();
|
|
2374
|
+
}
|
|
2375
|
+
_ackWindowSize() {
|
|
2376
|
+
return 2 * this.ackAdvance + this.ackPeriod;
|
|
2351
2377
|
}
|
|
2352
2378
|
_ackWindowError(nextExpectedIndex) {
|
|
2353
|
-
return `Stream ran past its ack window: reached ${nextExpectedIndex},
|
|
2379
|
+
return `Stream ran past its ack window: reached ${nextExpectedIndex}, consumed ${this._nextConsumedIndex}, ackAdvance ${this.ackAdvance}`;
|
|
2354
2380
|
}
|
|
2355
2381
|
_gapError(index) {
|
|
2356
2382
|
return new Error(
|
|
@@ -2732,6 +2758,9 @@ var RpcPeer = class {
|
|
|
2732
2758
|
* stale `$sys.Reconnect` that references an older connection generation
|
|
2733
2759
|
* (C# `RpcSystemCalls.Reconnect`'s `ownHandshake.Index` check). */
|
|
2734
2760
|
_ownHandshakeIndex = 0;
|
|
2761
|
+
/** Whether the current connection generation has already used its single
|
|
2762
|
+
* `$sys.Reconnect`. Mirrors C# `RpcPeerConnectionState.TryClaimReconnect`. */
|
|
2763
|
+
_isReconnectClaimed = false;
|
|
2735
2764
|
_connection;
|
|
2736
2765
|
/** Mirror of `_connectionState === Connected`. True only after the
|
|
2737
2766
|
* handshake round-trip completes; outbound `call()` checks this to
|
|
@@ -2802,6 +2831,19 @@ var RpcPeer = class {
|
|
|
2802
2831
|
get ownHandshakeIndex() {
|
|
2803
2832
|
return this._ownHandshakeIndex;
|
|
2804
2833
|
}
|
|
2834
|
+
// A conforming peer sends `$sys.Reconnect` at most once per connection, so anything
|
|
2835
|
+
// beyond that is a replay - and the reconciliation it performs is proportional to the
|
|
2836
|
+
// call ids it carries, with nothing else capping how often it can arrive.
|
|
2837
|
+
tryClaimReconnect() {
|
|
2838
|
+
if (this._isReconnectClaimed)
|
|
2839
|
+
return false;
|
|
2840
|
+
this._isReconnectClaimed = true;
|
|
2841
|
+
return true;
|
|
2842
|
+
}
|
|
2843
|
+
nextOwnHandshakeIndex() {
|
|
2844
|
+
this._isReconnectClaimed = false;
|
|
2845
|
+
return ++this._ownHandshakeIndex;
|
|
2846
|
+
}
|
|
2805
2847
|
_setConnectionState(value) {
|
|
2806
2848
|
if (this._connectionState === value) return;
|
|
2807
2849
|
const previousState = this._connectionState;
|
|
@@ -3337,7 +3379,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
3337
3379
|
this.serializationFormat,
|
|
3338
3380
|
this.id,
|
|
3339
3381
|
this.hub.hubId,
|
|
3340
|
-
|
|
3382
|
+
this.nextOwnHandshakeIndex()
|
|
3341
3383
|
);
|
|
3342
3384
|
} catch (e) {
|
|
3343
3385
|
warnLog7?.log(`'${this.ref}': Failed to send handshake`, e);
|
|
@@ -3602,7 +3644,7 @@ var RpcServerPeer = class extends RpcPeer {
|
|
|
3602
3644
|
this.serializationFormat,
|
|
3603
3645
|
this.id,
|
|
3604
3646
|
this.hub.hubId,
|
|
3605
|
-
|
|
3647
|
+
this.nextOwnHandshakeIndex()
|
|
3606
3648
|
);
|
|
3607
3649
|
this._setConnectionState(3 /* Connected */);
|
|
3608
3650
|
}
|