@actuallab/rpc 14.3.32 → 14.3.34

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 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) {
@@ -3178,6 +3189,13 @@ var RpcConnectionState = /* @__PURE__ */ ((RpcConnectionState2) => {
3178
3189
  RpcConnectionState2[RpcConnectionState2["Connected"] = 3] = "Connected";
3179
3190
  return RpcConnectionState2;
3180
3191
  })(RpcConnectionState || {});
3192
+ var RpcPeerChangeKind = /* @__PURE__ */ ((RpcPeerChangeKind2) => {
3193
+ RpcPeerChangeKind2[RpcPeerChangeKind2["Unchanged"] = 0] = "Unchanged";
3194
+ RpcPeerChangeKind2[RpcPeerChangeKind2["ChangedToVeryFirst"] = 1] = "ChangedToVeryFirst";
3195
+ RpcPeerChangeKind2[RpcPeerChangeKind2["Changed"] = 2] = "Changed";
3196
+ return RpcPeerChangeKind2;
3197
+ })(RpcPeerChangeKind || {});
3198
+ var peerChangeKindNames = ["Unchanged", "ChangedToVeryFirst", "Changed"];
3181
3199
  var RpcClientPeer = class extends RpcPeer {
3182
3200
  _disposed = false;
3183
3201
  /** Aborted when the peer is stopped/disposed — threaded into the reconnect
@@ -3190,7 +3208,10 @@ var RpcClientPeer = class extends RpcPeer {
3190
3208
  * `connectionState.Handshake.Index` path in
3191
3209
  * `RpcOutboundCallTracker.TryReconnect`. */
3192
3210
  _remoteHandshakeIndex = 0;
3193
- _lastRemotePeerId;
3211
+ /** Mirrors .NET's `lastHandshake` local in `RpcPeer.OnRun`: first-ness is keyed
3212
+ * off "was there a previous handshake", not off `RemotePeerId` — a legacy server
3213
+ * sends none, so every one of its handshakes would look like the first. */
3214
+ _lastHandshake;
3194
3215
  _pendingHandshake;
3195
3216
  _reconnectsAt = 0;
3196
3217
  /** Server-issued reconnect secret, from the most recent handshake that
@@ -3271,10 +3292,11 @@ var RpcClientPeer = class extends RpcPeer {
3271
3292
  * self-invalidate and remote/shared objects are disposed). Pass `false`
3272
3293
  * to simulate a same-peer reconnect in tests. */
3273
3294
  connectWith(conn, isPeerChanged = true) {
3295
+ const sentCalls = this.outboundCalls.getSentCalls();
3274
3296
  this.setupConnection(conn);
3275
3297
  this._isConnected = true;
3276
3298
  this.connectionStateChanged.trigger(3 /* Connected */);
3277
- void this._reconnect(isPeerChanged);
3299
+ void this._reconnect(sentCalls, isPeerChanged);
3278
3300
  }
3279
3301
  /** Kick off the reconnect loop. Idempotent — subsequent calls are
3280
3302
  * no-ops; await `peer.whenRunning` if you need the loop's completion
@@ -3405,11 +3427,19 @@ var RpcClientPeer = class extends RpcPeer {
3405
3427
  this._remoteHandshakeIndex = remoteHandshake.Index ?? 0;
3406
3428
  if (remoteHandshake.Secret)
3407
3429
  this._reconnectSecret = remoteHandshake.Secret;
3408
- const isPeerChanged = this._detectPeerChange(remoteHandshake);
3409
- infoLog?.log(`'${this.ref}': Handshake succeeded, peerChanged=${isPeerChanged}`);
3430
+ const peerChangeKind = this._detectPeerChange(remoteHandshake);
3431
+ infoLog?.log(
3432
+ `'${this.ref}': Handshake succeeded, peerChangeKind=${peerChangeKindNames[peerChangeKind]}`
3433
+ );
3434
+ const sentCalls = this.outboundCalls.getSentCalls();
3410
3435
  this._setConnectionState(3 /* Connected */);
3411
3436
  connectedAt = Date.now();
3412
- await this._reconnect(isPeerChanged, closedController.signal);
3437
+ if (peerChangeKind !== 1 /* ChangedToVeryFirst */)
3438
+ await this._reconnect(
3439
+ sentCalls,
3440
+ peerChangeKind === 2 /* Changed */,
3441
+ closedController.signal
3442
+ );
3413
3443
  await whenClosed;
3414
3444
  } catch (e) {
3415
3445
  debugLog2?.log(`'${this.ref}': run() iteration failed:`, e);
@@ -3431,7 +3461,10 @@ var RpcClientPeer = class extends RpcPeer {
3431
3461
  }
3432
3462
  }
3433
3463
  /**
3434
- * Reconcile outbound calls after reconnection, then flush pending sends.
3464
+ * Reconcile the calls that were in flight on the connection that just died
3465
+ * (`sentCalls`), then resend the survivors. Calls still waiting for a
3466
+ * connection are deliberately NOT passed in — `AllowReconnect`/`AllowResend`
3467
+ * don't apply to them, and entering `Connected` already sends them.
3435
3468
  *
3436
3469
  * On peer change (different server hubId) or with binary wire format —
3437
3470
  * we blind-resend every eligible call.
@@ -3449,7 +3482,7 @@ var RpcClientPeer = class extends RpcPeer {
3449
3482
  * are always self-invalidated — TS doesn't track compute-call stages
3450
3483
  * across reconnect, so the safest behavior is to force a fresh compute.
3451
3484
  */
3452
- async _reconnect(isPeerChanged, closedSignal) {
3485
+ async _reconnect(sentCalls, isPeerChanged, closedSignal) {
3453
3486
  if (this._connection === void 0) return;
3454
3487
  if (isPeerChanged) {
3455
3488
  this.remoteObjects.disconnectAll();
@@ -3458,7 +3491,7 @@ var RpcClientPeer = class extends RpcPeer {
3458
3491
  this.remoteObjects.reconnectAll();
3459
3492
  }
3460
3493
  const eligible = [];
3461
- for (const call of [...this.outboundCalls.values()]) {
3494
+ for (const call of sentCalls) {
3462
3495
  const mode = call.remoteExecutionMode;
3463
3496
  if (!(mode & 2)) {
3464
3497
  if (!call.result.isCompleted)
@@ -3479,8 +3512,7 @@ var RpcClientPeer = class extends RpcPeer {
3479
3512
  continue;
3480
3513
  }
3481
3514
  eligible.push(call);
3482
- if (call.sentAt !== 0)
3483
- call.sentAt = Date.now();
3515
+ call.sentAt = Date.now();
3484
3516
  }
3485
3517
  let unknownIds = null;
3486
3518
  if (!isPeerChanged && eligible.length > 0) {
@@ -3568,19 +3600,18 @@ var RpcClientPeer = class extends RpcPeer {
3568
3600
  * `RpcHandshake.GetPeerChangeKind` keys off `RemotePeerId`, not
3569
3601
  * `RemoteHubId` (an idle server peer can be replaced while keeping the
3570
3602
  * same hub id). On a change it clears the inbound tracker and fires
3571
- * `peerChanged`; the very first handshake is never a change. */
3603
+ * `peerChanged`; the very first handshake is `ChangedToVeryFirst`. */
3572
3604
  _detectPeerChange(handshake) {
3573
- const remotePeerId = handshake.RemotePeerId;
3574
- if (remotePeerId === void 0)
3575
- return false;
3576
- const isPeerChanged = this._lastRemotePeerId !== void 0 && this._lastRemotePeerId !== remotePeerId;
3577
- this._lastRemotePeerId = remotePeerId;
3578
- if (isPeerChanged) {
3579
- this.inboundCalls.clear();
3580
- infoLog?.log(`'${this.ref}': Peer changed`);
3581
- this.peerChanged.trigger();
3582
- }
3583
- return isPeerChanged;
3605
+ const lastHandshake = this._lastHandshake;
3606
+ this._lastHandshake = handshake;
3607
+ if (lastHandshake === void 0)
3608
+ return 1 /* ChangedToVeryFirst */;
3609
+ if (handshake.RemotePeerId === lastHandshake.RemotePeerId)
3610
+ return 0 /* Unchanged */;
3611
+ this.inboundCalls.clear();
3612
+ infoLog?.log(`'${this.ref}': Peer changed`);
3613
+ this.peerChanged.trigger();
3614
+ return 2 /* Changed */;
3584
3615
  }
3585
3616
  _onHandshakeReceived(handshake) {
3586
3617
  this._pendingHandshake?.resolve(handshake);
@@ -4880,6 +4911,7 @@ var RpcPeerStateMonitor = class {
4880
4911
  RpcOutboundCall,
4881
4912
  RpcOutboundCallTracker,
4882
4913
  RpcPeer,
4914
+ RpcPeerChangeKind,
4883
4915
  RpcPeerStateKind,
4884
4916
  RpcPeerStateMonitor,
4885
4917
  RpcRefBuilder,