@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.d.cts
CHANGED
|
@@ -981,6 +981,9 @@ declare abstract class RpcPeer {
|
|
|
981
981
|
* stale `$sys.Reconnect` that references an older connection generation
|
|
982
982
|
* (C# `RpcSystemCalls.Reconnect`'s `ownHandshake.Index` check). */
|
|
983
983
|
protected _ownHandshakeIndex: number;
|
|
984
|
+
/** Whether the current connection generation has already used its single
|
|
985
|
+
* `$sys.Reconnect`. Mirrors C# `RpcPeerConnectionState.TryClaimReconnect`. */
|
|
986
|
+
protected _isReconnectClaimed: boolean;
|
|
984
987
|
protected _connection: RpcConnection | undefined;
|
|
985
988
|
/** Mirror of `_connectionState === Connected`. True only after the
|
|
986
989
|
* handshake round-trip completes; outbound `call()` checks this to
|
|
@@ -1013,6 +1016,8 @@ declare abstract class RpcPeer {
|
|
|
1013
1016
|
get connectionState(): RpcConnectionState;
|
|
1014
1017
|
get isConnected(): boolean;
|
|
1015
1018
|
get ownHandshakeIndex(): number;
|
|
1019
|
+
tryClaimReconnect(): boolean;
|
|
1020
|
+
protected nextOwnHandshakeIndex(): number;
|
|
1016
1021
|
protected _setConnectionState(value: RpcConnectionState): void;
|
|
1017
1022
|
/** Resolves on the next transition into `Connected` — or immediately if
|
|
1018
1023
|
* already in that state. */
|
|
@@ -1516,6 +1521,7 @@ declare class RpcStream<T> implements AsyncIterable<T>, IRpcObject {
|
|
|
1516
1521
|
private _complete;
|
|
1517
1522
|
private _failProtocol;
|
|
1518
1523
|
private _isBeyondAckWindow;
|
|
1524
|
+
private _ackWindowSize;
|
|
1519
1525
|
private _ackWindowError;
|
|
1520
1526
|
private _gapError;
|
|
1521
1527
|
private _notifyConsumer;
|
package/dist/index.d.ts
CHANGED
|
@@ -981,6 +981,9 @@ declare abstract class RpcPeer {
|
|
|
981
981
|
* stale `$sys.Reconnect` that references an older connection generation
|
|
982
982
|
* (C# `RpcSystemCalls.Reconnect`'s `ownHandshake.Index` check). */
|
|
983
983
|
protected _ownHandshakeIndex: number;
|
|
984
|
+
/** Whether the current connection generation has already used its single
|
|
985
|
+
* `$sys.Reconnect`. Mirrors C# `RpcPeerConnectionState.TryClaimReconnect`. */
|
|
986
|
+
protected _isReconnectClaimed: boolean;
|
|
984
987
|
protected _connection: RpcConnection | undefined;
|
|
985
988
|
/** Mirror of `_connectionState === Connected`. True only after the
|
|
986
989
|
* handshake round-trip completes; outbound `call()` checks this to
|
|
@@ -1013,6 +1016,8 @@ declare abstract class RpcPeer {
|
|
|
1013
1016
|
get connectionState(): RpcConnectionState;
|
|
1014
1017
|
get isConnected(): boolean;
|
|
1015
1018
|
get ownHandshakeIndex(): number;
|
|
1019
|
+
tryClaimReconnect(): boolean;
|
|
1020
|
+
protected nextOwnHandshakeIndex(): number;
|
|
1016
1021
|
protected _setConnectionState(value: RpcConnectionState): void;
|
|
1017
1022
|
/** Resolves on the next transition into `Connected` — or immediately if
|
|
1018
1023
|
* already in that state. */
|
|
@@ -1516,6 +1521,7 @@ declare class RpcStream<T> implements AsyncIterable<T>, IRpcObject {
|
|
|
1516
1521
|
private _complete;
|
|
1517
1522
|
private _failProtocol;
|
|
1518
1523
|
private _isBeyondAckWindow;
|
|
1524
|
+
private _ackWindowSize;
|
|
1519
1525
|
private _ackWindowError;
|
|
1520
1526
|
private _gapError;
|
|
1521
1527
|
private _notifyConsumer;
|
package/dist/index.js
CHANGED
|
@@ -1172,6 +1172,15 @@ var RpcSystemCallHandler = class {
|
|
|
1172
1172
|
);
|
|
1173
1173
|
return;
|
|
1174
1174
|
}
|
|
1175
|
+
if (!peer.tryClaimReconnect()) {
|
|
1176
|
+
peer.hub.systemCallSender.error(
|
|
1177
|
+
peer.connection,
|
|
1178
|
+
peer.serializationFormat,
|
|
1179
|
+
relatedId,
|
|
1180
|
+
new Error("TooLateToReconnect: this connection was already reconnected")
|
|
1181
|
+
);
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1175
1184
|
let unknownIds = [];
|
|
1176
1185
|
try {
|
|
1177
1186
|
const stagesRaw = args[1];
|
|
@@ -1912,6 +1921,7 @@ function _compactBufferedUnsentSuffix(buffer, firstUnsentIndex, canSkipTo) {
|
|
|
1912
1921
|
var { warnLog: warnLog6 } = getLogs("RpcStream");
|
|
1913
1922
|
var DEFAULT_ACK_PERIOD2 = 30;
|
|
1914
1923
|
var DEFAULT_ACK_ADVANCE2 = 61;
|
|
1924
|
+
var MAX_BATCH_SIZE = 1024;
|
|
1915
1925
|
function parseStreamRef(value) {
|
|
1916
1926
|
if (typeof value === "string") {
|
|
1917
1927
|
const parts = value.split(",");
|
|
@@ -2099,6 +2109,10 @@ var RpcStream = class _RpcStream {
|
|
|
2099
2109
|
this._failProtocol(`invalid $sys.B index/length: ${String(index)}/${String(items.length)}`);
|
|
2100
2110
|
return;
|
|
2101
2111
|
}
|
|
2112
|
+
if (items.length > MAX_BATCH_SIZE) {
|
|
2113
|
+
this._failProtocol(`$sys.B batch is too large: ${items.length} > ${MAX_BATCH_SIZE}`);
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2102
2116
|
if (index > this._nextExpectedIndex) {
|
|
2103
2117
|
warnLog6?.log(`batch index gap: localId=${this.id.localId}, expected=${this._nextExpectedIndex}, received=${index}`);
|
|
2104
2118
|
if (!this.allowReconnect) {
|
|
@@ -2233,11 +2247,23 @@ var RpcStream = class _RpcStream {
|
|
|
2233
2247
|
// last acknowledged index. The sending side is the only place that window
|
|
2234
2248
|
// was ever enforced, which left a flooding remote free to grow our buffer
|
|
2235
2249
|
// without bound (I6) — so we enforce our half of the contract here.
|
|
2250
|
+
//
|
|
2251
|
+
// The window is credited by consumption, never by `_ackSentUpTo`: a reset ack
|
|
2252
|
+
// re-bases to what we've *received*, so crediting it would let a peer ratchet
|
|
2253
|
+
// its own limit up by alternating "fill the window" with a forced gap.
|
|
2254
|
+
// Twice ackAdvance is what a conforming sender can leave buffered (ackAdvance
|
|
2255
|
+
// in steady state, plus ackAdvance more after a reset re-bases it), and
|
|
2256
|
+
// ackPeriod covers the lag of acks behind consumption.
|
|
2236
2257
|
_isBeyondAckWindow(nextExpectedIndex) {
|
|
2237
|
-
|
|
2258
|
+
if (this.ackAdvance <= 0)
|
|
2259
|
+
return false;
|
|
2260
|
+
return nextExpectedIndex > this._nextConsumedIndex + this._ackWindowSize();
|
|
2261
|
+
}
|
|
2262
|
+
_ackWindowSize() {
|
|
2263
|
+
return 2 * this.ackAdvance + this.ackPeriod;
|
|
2238
2264
|
}
|
|
2239
2265
|
_ackWindowError(nextExpectedIndex) {
|
|
2240
|
-
return `Stream ran past its ack window: reached ${nextExpectedIndex},
|
|
2266
|
+
return `Stream ran past its ack window: reached ${nextExpectedIndex}, consumed ${this._nextConsumedIndex}, ackAdvance ${this.ackAdvance}`;
|
|
2241
2267
|
}
|
|
2242
2268
|
_gapError(index) {
|
|
2243
2269
|
return new Error(
|
|
@@ -2619,6 +2645,9 @@ var RpcPeer = class {
|
|
|
2619
2645
|
* stale `$sys.Reconnect` that references an older connection generation
|
|
2620
2646
|
* (C# `RpcSystemCalls.Reconnect`'s `ownHandshake.Index` check). */
|
|
2621
2647
|
_ownHandshakeIndex = 0;
|
|
2648
|
+
/** Whether the current connection generation has already used its single
|
|
2649
|
+
* `$sys.Reconnect`. Mirrors C# `RpcPeerConnectionState.TryClaimReconnect`. */
|
|
2650
|
+
_isReconnectClaimed = false;
|
|
2622
2651
|
_connection;
|
|
2623
2652
|
/** Mirror of `_connectionState === Connected`. True only after the
|
|
2624
2653
|
* handshake round-trip completes; outbound `call()` checks this to
|
|
@@ -2689,6 +2718,19 @@ var RpcPeer = class {
|
|
|
2689
2718
|
get ownHandshakeIndex() {
|
|
2690
2719
|
return this._ownHandshakeIndex;
|
|
2691
2720
|
}
|
|
2721
|
+
// A conforming peer sends `$sys.Reconnect` at most once per connection, so anything
|
|
2722
|
+
// beyond that is a replay - and the reconciliation it performs is proportional to the
|
|
2723
|
+
// call ids it carries, with nothing else capping how often it can arrive.
|
|
2724
|
+
tryClaimReconnect() {
|
|
2725
|
+
if (this._isReconnectClaimed)
|
|
2726
|
+
return false;
|
|
2727
|
+
this._isReconnectClaimed = true;
|
|
2728
|
+
return true;
|
|
2729
|
+
}
|
|
2730
|
+
nextOwnHandshakeIndex() {
|
|
2731
|
+
this._isReconnectClaimed = false;
|
|
2732
|
+
return ++this._ownHandshakeIndex;
|
|
2733
|
+
}
|
|
2692
2734
|
_setConnectionState(value) {
|
|
2693
2735
|
if (this._connectionState === value) return;
|
|
2694
2736
|
const previousState = this._connectionState;
|
|
@@ -3224,7 +3266,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
3224
3266
|
this.serializationFormat,
|
|
3225
3267
|
this.id,
|
|
3226
3268
|
this.hub.hubId,
|
|
3227
|
-
|
|
3269
|
+
this.nextOwnHandshakeIndex()
|
|
3228
3270
|
);
|
|
3229
3271
|
} catch (e) {
|
|
3230
3272
|
warnLog7?.log(`'${this.ref}': Failed to send handshake`, e);
|
|
@@ -3489,7 +3531,7 @@ var RpcServerPeer = class extends RpcPeer {
|
|
|
3489
3531
|
this.serializationFormat,
|
|
3490
3532
|
this.id,
|
|
3491
3533
|
this.hub.hubId,
|
|
3492
|
-
|
|
3534
|
+
this.nextOwnHandshakeIndex()
|
|
3493
3535
|
);
|
|
3494
3536
|
this._setConnectionState(3 /* Connected */);
|
|
3495
3537
|
}
|