@actuallab/rpc 14.4.14 → 15.0.21

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.d.cts CHANGED
@@ -1213,7 +1213,8 @@ declare class RpcClientPeer extends RpcPeer {
1213
1213
  /**
1214
1214
  * Ask the server which call IDs in `eligible` it no longer recognizes
1215
1215
  * via `$sys.Reconnect:3`. Returns the set of call IDs that need to be
1216
- * resent. On any failure, falls back to "resend everything".
1216
+ * resent, or `null` if the reconciliation failed: the caller then
1217
+ * resends everything that allows it.
1217
1218
  *
1218
1219
  * Wire format of the `completedStages` argument is chosen per wire format:
1219
1220
  * - JSON (json5np): plain object `{"<stage>": "<base64-of-bytes>"}` —
package/dist/index.d.ts CHANGED
@@ -1213,7 +1213,8 @@ declare class RpcClientPeer extends RpcPeer {
1213
1213
  /**
1214
1214
  * Ask the server which call IDs in `eligible` it no longer recognizes
1215
1215
  * via `$sys.Reconnect:3`. Returns the set of call IDs that need to be
1216
- * resent. On any failure, falls back to "resend everything".
1216
+ * resent, or `null` if the reconciliation failed: the caller then
1217
+ * resends everything that allows it.
1217
1218
  *
1218
1219
  * Wire format of the `completedStages` argument is chosen per wire format:
1219
1220
  * - JSON (json5np): plain object `{"<stage>": "<base64-of-bytes>"}` —
package/dist/index.js CHANGED
@@ -900,6 +900,14 @@ var RpcError = class extends Error {
900
900
  this.typeName = typeName;
901
901
  }
902
902
  };
903
+ var CANCELLATION_TYPE_NAMES = /* @__PURE__ */ new Set([
904
+ "System.OperationCanceledException",
905
+ "System.Threading.Tasks.TaskCanceledException",
906
+ "ActualLab.Rpc.RpcRerouteException"
907
+ ]);
908
+ function isCancellationTypeName(typeName) {
909
+ return typeName !== void 0 && CANCELLATION_TYPE_NAMES.has(typeName);
910
+ }
903
911
  var REMOTE_EXCEPTION_TYPE_REF = "ActualLab.Serialization.RemoteException, ActualLab.Core";
904
912
  var genericErrorFilter = () => ({
905
913
  TypeRef: REMOTE_EXCEPTION_TYPE_REF,
@@ -1059,15 +1067,21 @@ var RpcSystemCallHandler = class {
1059
1067
  const call = peer.outboundCalls.get(relatedId);
1060
1068
  if (call !== void 0) {
1061
1069
  call.completedStage |= RpcCallStage.ResultReady;
1062
- if (call.removeOnOk) {
1070
+ const errorInfo = args[0];
1071
+ const typeName = extractTypeName(errorInfo?.TypeRef ?? errorInfo?.typeRef);
1072
+ const isCancelled = !call.removeOnOk && isCancellationTypeName(typeName);
1073
+ if (call.removeOnOk || isCancelled) {
1063
1074
  peer.outboundCalls.remove(relatedId);
1064
1075
  }
1065
- const errorInfo = args[0];
1066
1076
  const msg = errorInfo?.Message ?? errorInfo?.message ?? "RPC error";
1067
- const typeName = extractTypeName(errorInfo?.TypeRef ?? errorInfo?.typeRef);
1068
1077
  if (typeName === "ActualLab.Rpc.RpcRerouteException")
1069
1078
  warnLog4?.log("Got RpcRerouteException from remote peer:", msg);
1070
- call.result.reject(new RpcError(msg, typeName));
1079
+ const error = new RpcError(msg, typeName);
1080
+ if (isCancelled)
1081
+ error.name = "AbortError";
1082
+ call.result.reject(error);
1083
+ if (isCancelled)
1084
+ call.onDisconnect();
1071
1085
  }
1072
1086
  break;
1073
1087
  }
@@ -2365,6 +2379,7 @@ function toRpcStream(value, peer) {
2365
2379
  }
2366
2380
 
2367
2381
  // src/rpc-remote-object-tracker.ts
2382
+ var { warnLog: warnLog7 } = getLogs("RpcRemoteObjectTracker");
2368
2383
  var RpcRemoteObjectTracker = class {
2369
2384
  _objects = /* @__PURE__ */ new Map();
2370
2385
  register(obj) {
@@ -2395,8 +2410,17 @@ var RpcRemoteObjectTracker = class {
2395
2410
  }
2396
2411
  reconnectAll() {
2397
2412
  for (const obj of this._objects.values()) {
2398
- if (obj.allowReconnect) obj.reconnect();
2399
- else obj.disconnect();
2413
+ try {
2414
+ if (obj.allowReconnect) obj.reconnect();
2415
+ else obj.disconnect();
2416
+ } catch (e) {
2417
+ warnLog7?.log(`Failed to reconnect ${obj.id.localId}, disconnecting it`, e);
2418
+ try {
2419
+ obj.disconnect();
2420
+ } catch (e2) {
2421
+ warnLog7?.log(`Failed to disconnect ${obj.id.localId}`, e2);
2422
+ }
2423
+ }
2400
2424
  }
2401
2425
  }
2402
2426
  };
@@ -2621,7 +2645,7 @@ function yieldToEventLoop() {
2621
2645
  return new Promise((resolve) => setTimeout(resolve, 0));
2622
2646
  }
2623
2647
  var RPC_CLOSE_CODE_UNSUPPORTED_FORMAT = 4001;
2624
- var { debugLog: debugLog2, infoLog, warnLog: warnLog7, errorLog: errorLog2 } = getLogs("RpcPeer");
2648
+ var { debugLog: debugLog2, infoLog, warnLog: warnLog8, errorLog: errorLog2 } = getLogs("RpcPeer");
2625
2649
  var defaultConnectionUrlResolver = async (peer) => {
2626
2650
  const formatKey = peer.serializationFormat.key;
2627
2651
  const proof = await peer.computeReconnectProof();
@@ -3093,9 +3117,13 @@ var RpcPeer = class {
3093
3117
  this._keepAliveTimer ??= setInterval(() => {
3094
3118
  const conn = this.wireConnection;
3095
3119
  if (conn !== void 0) {
3096
- this.hub.systemCallSender.keepAlive(conn, this.serializationFormat, [
3097
- ...this.remoteObjects.keys()
3098
- ]);
3120
+ try {
3121
+ this.hub.systemCallSender.keepAlive(conn, this.serializationFormat, [
3122
+ ...this.remoteObjects.keys()
3123
+ ]);
3124
+ } catch (e) {
3125
+ warnLog8?.log(`'${this.ref}': Failed to send keep-alive`, e);
3126
+ }
3099
3127
  }
3100
3128
  }, this.keepAlivePeriodMs);
3101
3129
  if (this._keepAliveWatchdog !== void 0)
@@ -3103,7 +3131,7 @@ var RpcPeer = class {
3103
3131
  this._keepAliveWatchdog = setTimeout(() => {
3104
3132
  this._keepAliveWatchdog = void 0;
3105
3133
  if (this._connection === void 0) return;
3106
- warnLog7?.log(
3134
+ warnLog8?.log(
3107
3135
  `'${this.ref}': no keep-alive for ${this.keepAliveTimeoutMs}ms \u2014 force-closing connection`
3108
3136
  );
3109
3137
  this._connection.close();
@@ -3304,7 +3332,7 @@ var RpcClientPeer = class extends RpcPeer {
3304
3332
  this._setConnectionState(1 /* Connecting */);
3305
3333
  const retryDelay = this.hub.reconnectDelayer.getDelay(this._tryIndex, this._stopController.signal);
3306
3334
  if (retryDelay.isLimitExceeded) {
3307
- warnLog7?.log(`'${this.ref}': Reconnect limit exceeded - shutting down`);
3335
+ warnLog8?.log(`'${this.ref}': Reconnect limit exceeded - shutting down`);
3308
3336
  this._disposed = true;
3309
3337
  break;
3310
3338
  }
@@ -3353,7 +3381,7 @@ var RpcClientPeer = class extends RpcPeer {
3353
3381
  );
3354
3382
  } catch (e) {
3355
3383
  if (e instanceof TimeoutError) {
3356
- warnLog7?.log(`'${this.ref}': Connect timed out after ${connectTimeoutMs}ms`);
3384
+ warnLog8?.log(`'${this.ref}': Connect timed out after ${connectTimeoutMs}ms`);
3357
3385
  conn.close();
3358
3386
  }
3359
3387
  throw e;
@@ -3368,7 +3396,7 @@ var RpcClientPeer = class extends RpcPeer {
3368
3396
  this.nextOwnHandshakeIndex()
3369
3397
  );
3370
3398
  } catch (e) {
3371
- warnLog7?.log(`'${this.ref}': Failed to send handshake`, e);
3399
+ warnLog8?.log(`'${this.ref}': Failed to send handshake`, e);
3372
3400
  throw e;
3373
3401
  }
3374
3402
  const handshakeTimeoutMs = this.handshakeTimeoutMs;
@@ -3381,7 +3409,7 @@ var RpcClientPeer = class extends RpcPeer {
3381
3409
  );
3382
3410
  } catch (e) {
3383
3411
  if (e instanceof TimeoutError) {
3384
- warnLog7?.log(`'${this.ref}': Handshake timed out after ${handshakeTimeoutMs}ms`);
3412
+ warnLog8?.log(`'${this.ref}': Handshake timed out after ${handshakeTimeoutMs}ms`);
3385
3413
  conn.close();
3386
3414
  }
3387
3415
  throw e;
@@ -3411,7 +3439,7 @@ var RpcClientPeer = class extends RpcPeer {
3411
3439
  this._setConnectionState(0 /* Disconnected */);
3412
3440
  if (this._disposed) break;
3413
3441
  if (lastCloseCode === RPC_CLOSE_CODE_UNSUPPORTED_FORMAT) {
3414
- warnLog7?.log(`'${this.ref}': Unsupported serialization format - shutting down`);
3442
+ warnLog8?.log(`'${this.ref}': Unsupported serialization format - shutting down`);
3415
3443
  this.unsupportedFormat.trigger({ reason: lastCloseReason });
3416
3444
  this._disposed = true;
3417
3445
  break;
@@ -3479,8 +3507,10 @@ var RpcClientPeer = class extends RpcPeer {
3479
3507
  call.sentAt = Date.now();
3480
3508
  }
3481
3509
  let unknownIds = null;
3510
+ let isReconciled = false;
3482
3511
  if (!isPeerChanged && eligible.length > 0) {
3483
3512
  unknownIds = await this._reconcileReconnect(eligible, closedSignal);
3513
+ isReconciled = unknownIds !== null;
3484
3514
  }
3485
3515
  const YIELD_BATCH = 20;
3486
3516
  let resentSinceYield = 0;
@@ -3493,6 +3523,17 @@ var RpcClientPeer = class extends RpcPeer {
3493
3523
  if (unknownIds !== null && !unknownIds.has(call.callId)) {
3494
3524
  continue;
3495
3525
  }
3526
+ if (!(call.remoteExecutionMode & 4)) {
3527
+ if (!isReconciled)
3528
+ continue;
3529
+ if (!call.result.isCompleted)
3530
+ call.result.reject(new Error(
3531
+ "Outbound call failed: the peer does not know the call after reconnect and AllowResend is not set."
3532
+ ));
3533
+ call.onDisconnect();
3534
+ this.outboundCalls.remove(call.callId);
3535
+ continue;
3536
+ }
3496
3537
  call.sentAt = Date.now();
3497
3538
  this._sendWireData(call.serializedWireData);
3498
3539
  resentSinceYield++;
@@ -3505,7 +3546,8 @@ var RpcClientPeer = class extends RpcPeer {
3505
3546
  /**
3506
3547
  * Ask the server which call IDs in `eligible` it no longer recognizes
3507
3548
  * via `$sys.Reconnect:3`. Returns the set of call IDs that need to be
3508
- * resent. On any failure, falls back to "resend everything".
3549
+ * resent, or `null` if the reconciliation failed: the caller then
3550
+ * resends everything that allows it.
3509
3551
  *
3510
3552
  * Wire format of the `completedStages` argument is chosen per wire format:
3511
3553
  * - JSON (json5np): plain object `{"<stage>": "<base64-of-bytes>"}` —
@@ -3529,7 +3571,7 @@ var RpcClientPeer = class extends RpcPeer {
3529
3571
  arr.push(call.callId);
3530
3572
  }
3531
3573
  if (byStage.size === 0) {
3532
- return new Set(eligible.map((c) => c.callId));
3574
+ return null;
3533
3575
  }
3534
3576
  const isBinary = this.serializationFormat.isBinary;
3535
3577
  const stagesArg = isBinary ? /* @__PURE__ */ new Map() : {};
@@ -3553,10 +3595,10 @@ var RpcClientPeer = class extends RpcPeer {
3553
3595
  );
3554
3596
  const result = await reconnectCall.result;
3555
3597
  const bytes = _toBytes2(result);
3556
- if (bytes === null) return new Set(eligible.map((c) => c.callId));
3598
+ if (bytes === null) return null;
3557
3599
  return new Set(IncreasingSeqCompressor.deserialize(bytes));
3558
3600
  } catch {
3559
- return new Set(eligible.map((c) => c.callId));
3601
+ return null;
3560
3602
  }
3561
3603
  }
3562
3604
  /** Detects a server-peer identity change by comparing the incoming
@@ -3666,7 +3708,7 @@ function warnReconnectProofUnavailable(ref, reason) {
3666
3708
  if (isReconnectProofUnavailableWarned)
3667
3709
  return;
3668
3710
  isReconnectProofUnavailableWarned = true;
3669
- warnLog7?.log(
3711
+ warnLog8?.log(
3670
3712
  `'${ref}': ${reason} - connecting without a reconnect proof. This fails if the server requires one (RequireReconnectProof).`
3671
3713
  );
3672
3714
  }
@@ -3747,7 +3789,7 @@ var RpcClientPeerReconnectDelayer = class extends RetryDelayer {
3747
3789
  };
3748
3790
 
3749
3791
  // src/rpc-service-host.ts
3750
- var { warnLog: warnLog8 } = getLogs("RpcServiceHost");
3792
+ var { warnLog: warnLog9 } = getLogs("RpcServiceHost");
3751
3793
  var RpcServiceHost = class {
3752
3794
  _methods = /* @__PURE__ */ new Map();
3753
3795
  register(def, impl, receiver = impl) {
@@ -3764,7 +3806,7 @@ var RpcServiceHost = class {
3764
3806
  async dispatch(wireMethod, args, context) {
3765
3807
  const entry = this._methods.get(wireMethod);
3766
3808
  if (entry === void 0) {
3767
- warnLog8?.log(`Method not found: ${wireMethod}`);
3809
+ warnLog9?.log(`Method not found: ${wireMethod}`);
3768
3810
  throw new Error(`Method not found: ${wireMethod}`);
3769
3811
  }
3770
3812
  if (context !== void 0 && entry.def.callTypeId !== 0) {
@@ -4297,7 +4339,7 @@ function computeMethodHash(methodName) {
4297
4339
  }
4298
4340
 
4299
4341
  // src/rpc-method-registry.ts
4300
- var { warnLog: warnLog9 } = getLogs("RpcMethodRegistry");
4342
+ var { warnLog: warnLog10 } = getLogs("RpcMethodRegistry");
4301
4343
  var RpcMethodRegistry = class {
4302
4344
  _nameToHash = /* @__PURE__ */ new Map();
4303
4345
  _hashToName = /* @__PURE__ */ new Map();
@@ -4307,7 +4349,7 @@ var RpcMethodRegistry = class {
4307
4349
  const hash = computeMethodHash(methodName);
4308
4350
  const existing = this._hashToName.get(hash);
4309
4351
  if (existing !== void 0 && existing !== methodName) {
4310
- warnLog9?.log(`hash collision: "${methodName}" and "${existing}" both hash to ${hash}`);
4352
+ warnLog10?.log(`hash collision: "${methodName}" and "${existing}" both hash to ${hash}`);
4311
4353
  return;
4312
4354
  }
4313
4355
  this._nameToHash.set(methodName, hash);
@@ -4340,7 +4382,7 @@ var RpcMethodRegistry = class {
4340
4382
  };
4341
4383
 
4342
4384
  // src/rpc-hub.ts
4343
- var { warnLog: warnLog10 } = getLogs("RpcHub");
4385
+ var { warnLog: warnLog11 } = getLogs("RpcHub");
4344
4386
  var RpcHub = class {
4345
4387
  hubId;
4346
4388
  peers = /* @__PURE__ */ new Map();
@@ -4405,7 +4447,7 @@ var RpcHub = class {
4405
4447
  if (peer !== void 0) {
4406
4448
  peer.close();
4407
4449
  this.peers.delete(ref);
4408
- warnLog10?.log(`'${ref}': peer is removed from RpcHub`);
4450
+ warnLog11?.log(`'${ref}': peer is removed from RpcHub`);
4409
4451
  }
4410
4452
  }
4411
4453
  /** Get or create a peer by ref. If the peer does not exist: