@actuallab/rpc 14.3.32 → 14.4.3
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 +73 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -8
- package/dist/index.d.ts +30 -8
- package/dist/index.js +72 -28
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -56,6 +56,7 @@ __export(index_exports, {
|
|
|
56
56
|
RpcOutboundCall: () => RpcOutboundCall,
|
|
57
57
|
RpcOutboundCallTracker: () => RpcOutboundCallTracker,
|
|
58
58
|
RpcPeer: () => RpcPeer,
|
|
59
|
+
RpcPeerChangeKind: () => RpcPeerChangeKind,
|
|
59
60
|
RpcPeerStateKind: () => RpcPeerStateKind,
|
|
60
61
|
RpcPeerStateMonitor: () => RpcPeerStateMonitor,
|
|
61
62
|
RpcRefBuilder: () => RpcRefBuilder,
|
|
@@ -864,6 +865,16 @@ var RpcOutboundCallTracker = class {
|
|
|
864
865
|
activeCallIds() {
|
|
865
866
|
return [...this._calls.keys()];
|
|
866
867
|
}
|
|
868
|
+
/** Calls handed to the transport at least once. Must be snapshotted BEFORE the
|
|
869
|
+
* peer enters `Connected` — that flushes every queued call, making queued and
|
|
870
|
+
* in-flight calls indistinguishable. Mirrors .NET's `GetSentCalls`. */
|
|
871
|
+
getSentCalls() {
|
|
872
|
+
const calls = [];
|
|
873
|
+
for (const call of this._calls.values())
|
|
874
|
+
if (call.sentAt !== 0)
|
|
875
|
+
calls.push(call);
|
|
876
|
+
return calls;
|
|
877
|
+
}
|
|
867
878
|
/** Reject all pending calls with the given error.
|
|
868
879
|
* Stage-3 compute calls (result resolved, awaiting invalidation) are kept in the tracker. */
|
|
869
880
|
rejectAll(error) {
|
|
@@ -982,7 +993,8 @@ var RpcCallTimeouts = class _RpcCallTimeouts {
|
|
|
982
993
|
static None = new _RpcCallTimeouts(Infinity, Infinity);
|
|
983
994
|
/** Command default: 1.5 s connect / 10 s run (.NET `RpcCallTimeouts.Default.Command`). */
|
|
984
995
|
static Command = new _RpcCallTimeouts(1500, 1e4);
|
|
985
|
-
/** Max time a call may wait for
|
|
996
|
+
/** Max time a call may wait for a connection - before it is sent, and again for the
|
|
997
|
+
* duration of any outage it is still pending through. */
|
|
986
998
|
connectTimeoutMs;
|
|
987
999
|
/** Max time a sent call may stay unanswered before it is failed. */
|
|
988
1000
|
runTimeoutMs;
|
|
@@ -2776,6 +2788,8 @@ var RpcPeer = class {
|
|
|
2776
2788
|
/** Wall-clock time of the last inbound `$sys.KeepAlive`, or 0 if none yet
|
|
2777
2789
|
* for the current connection. Useful for diagnostics. */
|
|
2778
2790
|
_lastKeepAliveAt = 0;
|
|
2791
|
+
/** When the current outage began; 0 until the peer has been connected at least once. */
|
|
2792
|
+
_disconnectedAt = 0;
|
|
2779
2793
|
/** Outbound keep-alive send period. Initialized from `hub.limits` at
|
|
2780
2794
|
* construction; set directly to override on a single peer. */
|
|
2781
2795
|
keepAlivePeriodMs;
|
|
@@ -2796,9 +2810,10 @@ var RpcPeer = class {
|
|
|
2796
2810
|
}
|
|
2797
2811
|
/** Fail outbound calls whose per-call {@link RpcCallTimeouts} elapsed —
|
|
2798
2812
|
* the connect timeout while a call still awaits the wire, the run timeout
|
|
2799
|
-
* once it has been sent
|
|
2800
|
-
*
|
|
2801
|
-
*
|
|
2813
|
+
* once it has been sent, and the connect timeout again for any pending call while the
|
|
2814
|
+
* peer is away. Mirrors .NET `RpcOutboundCallTracker.HandleDisconnect` (ConnectTimeout,
|
|
2815
|
+
* per outage) plus `Maintain` (RunTimeout, per connection). Calls without timeouts
|
|
2816
|
+
* (queries) never time out here. */
|
|
2802
2817
|
_maintainOutboundCalls() {
|
|
2803
2818
|
const now = Date.now();
|
|
2804
2819
|
const canRunTimeout = this._isConnected && (this._lastKeepAliveAt === 0 || now - this._lastKeepAliveAt <= this.keepAliveTimeoutMs);
|
|
@@ -2807,7 +2822,11 @@ var RpcPeer = class {
|
|
|
2807
2822
|
if (timeouts === void 0 || call.result.isCompleted)
|
|
2808
2823
|
continue;
|
|
2809
2824
|
let timedOut;
|
|
2810
|
-
if (
|
|
2825
|
+
if (!this._isConnected) {
|
|
2826
|
+
const since = Math.max(this._disconnectedAt, call.startedAt);
|
|
2827
|
+
if (now - since >= timeouts.connectTimeoutMs)
|
|
2828
|
+
timedOut = `connect timeout (${timeouts.connectTimeoutMs}ms)`;
|
|
2829
|
+
} else if (call.sentAt === 0) {
|
|
2811
2830
|
if (now - call.startedAt >= timeouts.connectTimeoutMs)
|
|
2812
2831
|
timedOut = `connect timeout (${timeouts.connectTimeoutMs}ms)`;
|
|
2813
2832
|
} else if (canRunTimeout && now - call.sentAt >= timeouts.runTimeoutMs)
|
|
@@ -2850,10 +2869,13 @@ var RpcPeer = class {
|
|
|
2850
2869
|
this._connectionState = value;
|
|
2851
2870
|
if (value === 3 /* Connected */) {
|
|
2852
2871
|
this._isConnected = true;
|
|
2872
|
+
this._disconnectedAt = 0;
|
|
2853
2873
|
this._armKeepAliveWatchdog();
|
|
2854
2874
|
} else if (value === 0 /* Disconnected */) {
|
|
2855
|
-
if (previousState === 3 /* Connected */)
|
|
2875
|
+
if (previousState === 3 /* Connected */) {
|
|
2856
2876
|
this._isConnected = false;
|
|
2877
|
+
this._disconnectedAt = Date.now();
|
|
2878
|
+
}
|
|
2857
2879
|
this._disarmKeepAliveWatchdog();
|
|
2858
2880
|
}
|
|
2859
2881
|
this.connectionStateChanged.trigger(value);
|
|
@@ -2878,6 +2900,8 @@ var RpcPeer = class {
|
|
|
2878
2900
|
conn.closed.add((ev) => {
|
|
2879
2901
|
if (this._connection !== conn) return;
|
|
2880
2902
|
this._connection = void 0;
|
|
2903
|
+
if (this._isConnected)
|
|
2904
|
+
this._disconnectedAt = Date.now();
|
|
2881
2905
|
this._isConnected = false;
|
|
2882
2906
|
if (ev.reason)
|
|
2883
2907
|
infoLog?.log(`'${this.ref}': Disconnected: ${ev.reason} (code=${ev.code})`);
|
|
@@ -3178,6 +3202,13 @@ var RpcConnectionState = /* @__PURE__ */ ((RpcConnectionState2) => {
|
|
|
3178
3202
|
RpcConnectionState2[RpcConnectionState2["Connected"] = 3] = "Connected";
|
|
3179
3203
|
return RpcConnectionState2;
|
|
3180
3204
|
})(RpcConnectionState || {});
|
|
3205
|
+
var RpcPeerChangeKind = /* @__PURE__ */ ((RpcPeerChangeKind2) => {
|
|
3206
|
+
RpcPeerChangeKind2[RpcPeerChangeKind2["Unchanged"] = 0] = "Unchanged";
|
|
3207
|
+
RpcPeerChangeKind2[RpcPeerChangeKind2["ChangedToVeryFirst"] = 1] = "ChangedToVeryFirst";
|
|
3208
|
+
RpcPeerChangeKind2[RpcPeerChangeKind2["Changed"] = 2] = "Changed";
|
|
3209
|
+
return RpcPeerChangeKind2;
|
|
3210
|
+
})(RpcPeerChangeKind || {});
|
|
3211
|
+
var peerChangeKindNames = ["Unchanged", "ChangedToVeryFirst", "Changed"];
|
|
3181
3212
|
var RpcClientPeer = class extends RpcPeer {
|
|
3182
3213
|
_disposed = false;
|
|
3183
3214
|
/** Aborted when the peer is stopped/disposed — threaded into the reconnect
|
|
@@ -3190,7 +3221,10 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
3190
3221
|
* `connectionState.Handshake.Index` path in
|
|
3191
3222
|
* `RpcOutboundCallTracker.TryReconnect`. */
|
|
3192
3223
|
_remoteHandshakeIndex = 0;
|
|
3193
|
-
|
|
3224
|
+
/** Mirrors .NET's `lastHandshake` local in `RpcPeer.OnRun`: first-ness is keyed
|
|
3225
|
+
* off "was there a previous handshake", not off `RemotePeerId` — a legacy server
|
|
3226
|
+
* sends none, so every one of its handshakes would look like the first. */
|
|
3227
|
+
_lastHandshake;
|
|
3194
3228
|
_pendingHandshake;
|
|
3195
3229
|
_reconnectsAt = 0;
|
|
3196
3230
|
/** Server-issued reconnect secret, from the most recent handshake that
|
|
@@ -3271,10 +3305,11 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
3271
3305
|
* self-invalidate and remote/shared objects are disposed). Pass `false`
|
|
3272
3306
|
* to simulate a same-peer reconnect in tests. */
|
|
3273
3307
|
connectWith(conn, isPeerChanged = true) {
|
|
3308
|
+
const sentCalls = this.outboundCalls.getSentCalls();
|
|
3274
3309
|
this.setupConnection(conn);
|
|
3275
3310
|
this._isConnected = true;
|
|
3276
3311
|
this.connectionStateChanged.trigger(3 /* Connected */);
|
|
3277
|
-
void this._reconnect(isPeerChanged);
|
|
3312
|
+
void this._reconnect(sentCalls, isPeerChanged);
|
|
3278
3313
|
}
|
|
3279
3314
|
/** Kick off the reconnect loop. Idempotent — subsequent calls are
|
|
3280
3315
|
* no-ops; await `peer.whenRunning` if you need the loop's completion
|
|
@@ -3405,11 +3440,19 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
3405
3440
|
this._remoteHandshakeIndex = remoteHandshake.Index ?? 0;
|
|
3406
3441
|
if (remoteHandshake.Secret)
|
|
3407
3442
|
this._reconnectSecret = remoteHandshake.Secret;
|
|
3408
|
-
const
|
|
3409
|
-
infoLog?.log(
|
|
3443
|
+
const peerChangeKind = this._detectPeerChange(remoteHandshake);
|
|
3444
|
+
infoLog?.log(
|
|
3445
|
+
`'${this.ref}': Handshake succeeded, peerChangeKind=${peerChangeKindNames[peerChangeKind]}`
|
|
3446
|
+
);
|
|
3447
|
+
const sentCalls = this.outboundCalls.getSentCalls();
|
|
3410
3448
|
this._setConnectionState(3 /* Connected */);
|
|
3411
3449
|
connectedAt = Date.now();
|
|
3412
|
-
|
|
3450
|
+
if (peerChangeKind !== 1 /* ChangedToVeryFirst */)
|
|
3451
|
+
await this._reconnect(
|
|
3452
|
+
sentCalls,
|
|
3453
|
+
peerChangeKind === 2 /* Changed */,
|
|
3454
|
+
closedController.signal
|
|
3455
|
+
);
|
|
3413
3456
|
await whenClosed;
|
|
3414
3457
|
} catch (e) {
|
|
3415
3458
|
debugLog2?.log(`'${this.ref}': run() iteration failed:`, e);
|
|
@@ -3431,7 +3474,10 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
3431
3474
|
}
|
|
3432
3475
|
}
|
|
3433
3476
|
/**
|
|
3434
|
-
* Reconcile
|
|
3477
|
+
* Reconcile the calls that were in flight on the connection that just died
|
|
3478
|
+
* (`sentCalls`), then resend the survivors. Calls still waiting for a
|
|
3479
|
+
* connection are deliberately NOT passed in — `AllowReconnect`/`AllowResend`
|
|
3480
|
+
* don't apply to them, and entering `Connected` already sends them.
|
|
3435
3481
|
*
|
|
3436
3482
|
* On peer change (different server hubId) or with binary wire format —
|
|
3437
3483
|
* we blind-resend every eligible call.
|
|
@@ -3449,7 +3495,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
3449
3495
|
* are always self-invalidated — TS doesn't track compute-call stages
|
|
3450
3496
|
* across reconnect, so the safest behavior is to force a fresh compute.
|
|
3451
3497
|
*/
|
|
3452
|
-
async _reconnect(isPeerChanged, closedSignal) {
|
|
3498
|
+
async _reconnect(sentCalls, isPeerChanged, closedSignal) {
|
|
3453
3499
|
if (this._connection === void 0) return;
|
|
3454
3500
|
if (isPeerChanged) {
|
|
3455
3501
|
this.remoteObjects.disconnectAll();
|
|
@@ -3458,7 +3504,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
3458
3504
|
this.remoteObjects.reconnectAll();
|
|
3459
3505
|
}
|
|
3460
3506
|
const eligible = [];
|
|
3461
|
-
for (const call of
|
|
3507
|
+
for (const call of sentCalls) {
|
|
3462
3508
|
const mode = call.remoteExecutionMode;
|
|
3463
3509
|
if (!(mode & 2)) {
|
|
3464
3510
|
if (!call.result.isCompleted)
|
|
@@ -3479,8 +3525,7 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
3479
3525
|
continue;
|
|
3480
3526
|
}
|
|
3481
3527
|
eligible.push(call);
|
|
3482
|
-
|
|
3483
|
-
call.sentAt = Date.now();
|
|
3528
|
+
call.sentAt = Date.now();
|
|
3484
3529
|
}
|
|
3485
3530
|
let unknownIds = null;
|
|
3486
3531
|
if (!isPeerChanged && eligible.length > 0) {
|
|
@@ -3568,19 +3613,18 @@ var RpcClientPeer = class extends RpcPeer {
|
|
|
3568
3613
|
* `RpcHandshake.GetPeerChangeKind` keys off `RemotePeerId`, not
|
|
3569
3614
|
* `RemoteHubId` (an idle server peer can be replaced while keeping the
|
|
3570
3615
|
* same hub id). On a change it clears the inbound tracker and fires
|
|
3571
|
-
* `peerChanged`; the very first handshake is
|
|
3616
|
+
* `peerChanged`; the very first handshake is `ChangedToVeryFirst`. */
|
|
3572
3617
|
_detectPeerChange(handshake) {
|
|
3573
|
-
const
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
return isPeerChanged;
|
|
3618
|
+
const lastHandshake = this._lastHandshake;
|
|
3619
|
+
this._lastHandshake = handshake;
|
|
3620
|
+
if (lastHandshake === void 0)
|
|
3621
|
+
return 1 /* ChangedToVeryFirst */;
|
|
3622
|
+
if (handshake.RemotePeerId === lastHandshake.RemotePeerId)
|
|
3623
|
+
return 0 /* Unchanged */;
|
|
3624
|
+
this.inboundCalls.clear();
|
|
3625
|
+
infoLog?.log(`'${this.ref}': Peer changed`);
|
|
3626
|
+
this.peerChanged.trigger();
|
|
3627
|
+
return 2 /* Changed */;
|
|
3584
3628
|
}
|
|
3585
3629
|
_onHandshakeReceived(handshake) {
|
|
3586
3630
|
this._pendingHandshake?.resolve(handshake);
|
|
@@ -4880,6 +4924,7 @@ var RpcPeerStateMonitor = class {
|
|
|
4880
4924
|
RpcOutboundCall,
|
|
4881
4925
|
RpcOutboundCallTracker,
|
|
4882
4926
|
RpcPeer,
|
|
4927
|
+
RpcPeerChangeKind,
|
|
4883
4928
|
RpcPeerStateKind,
|
|
4884
4929
|
RpcPeerStateMonitor,
|
|
4885
4930
|
RpcRefBuilder,
|