@actuallab/rpc 12.4.7 → 12.5.2

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
@@ -151,7 +151,8 @@ var RpcSystemCalls = {
151
151
  ackEnd: "$sys.AckEnd:1",
152
152
  item: "$sys.I:2",
153
153
  batch: "$sys.B:2",
154
- end: "$sys.End:2"
154
+ end: "$sys.End:2",
155
+ disconnect: "$sys.Disconnect:1"
155
156
  };
156
157
  var ENVELOPE_DELIMITER = "\n";
157
158
  var ARG_DELIMITER = "";
@@ -976,7 +977,7 @@ var RpcObjectKind = { Local: 0, Remote: 1 };
976
977
  var import_core5 = require("@actuallab/core");
977
978
  var { warnLog: warnLog3 } = getLogs("RpcSharedStream");
978
979
  var DEFAULT_ACK_PERIOD = 256;
979
- var DEFAULT_BUFFER_SIZE = 128;
980
+ var DEFAULT_ACK_ADVANCE = 128;
980
981
  var RpcStreamSender = class _RpcStreamSender {
981
982
  static _emptyGuid = "00000000-0000-0000-0000-000000000000";
982
983
  id;
@@ -986,6 +987,18 @@ var RpcStreamSender = class _RpcStreamSender {
986
987
  canSkipTo;
987
988
  peer;
988
989
  ackPeriod;
990
+ /**
991
+ * Wire-level flow-control window: the maximum index the sender may
992
+ * advance past the most recently acknowledged index. Mirrors .NET
993
+ * `RpcStream.AckAdvance`.
994
+ */
995
+ ackAdvance;
996
+ /**
997
+ * Effective local ring buffer capacity used by the pump. Set larger
998
+ * than `ackAdvance` to pre-buffer items beyond the in-flight window;
999
+ * values below `ackAdvance` are clamped up (with a warning) at
1000
+ * construction time. Not transmitted on the wire.
1001
+ */
989
1002
  bufferSize;
990
1003
  /**
991
1004
  * If true, the source was produced by a factory `(abortSignal) => AsyncIterable<T>`
@@ -1014,6 +1027,13 @@ var RpcStreamSender = class _RpcStreamSender {
1014
1027
  * watchdog so it can distinguish "stuck" (no ACK > N s) from
1015
1028
  * "throttled" (ACKs flowing). */
1016
1029
  onAckProcessed;
1030
+ /** Fires after each item is pushed onto the local ring buffer. The
1031
+ * argument is the buffer count *after* the push. Together with
1032
+ * `onAckProcessed` (which is the only place items leave the buffer),
1033
+ * this gives controllers a complete picture of buffer utilization.
1034
+ * When `bufferedCount === bufferSize`, the source pull is paused
1035
+ * until the next ACK frees space. Listener errors are swallowed. */
1036
+ onBuffered;
1017
1037
  get nextIndex() {
1018
1038
  return this._nextIndex;
1019
1039
  }
@@ -1026,7 +1046,7 @@ var RpcStreamSender = class _RpcStreamSender {
1026
1046
  // -- ACK queue (analog of .NET's Channel<(long, bool)>) --
1027
1047
  _acks = [];
1028
1048
  _whenAckReady = null;
1029
- constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, bufferSize = DEFAULT_BUFFER_SIZE, allowReconnect = true, isRealTime = false, canSkipTo = () => true, sourceUsesAbortSignal = false) {
1049
+ constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, ackAdvance = DEFAULT_ACK_ADVANCE, allowReconnect = true, isRealTime = false, canSkipTo = () => true, sourceUsesAbortSignal = false, bufferSize) {
1030
1050
  const localId = peer.sharedObjects.nextId();
1031
1051
  this.id = { hostId: peer.hub.hubId, localId };
1032
1052
  this.allowReconnect = allowReconnect;
@@ -1034,8 +1054,13 @@ var RpcStreamSender = class _RpcStreamSender {
1034
1054
  this.canSkipTo = canSkipTo;
1035
1055
  this.peer = peer;
1036
1056
  this.ackPeriod = ackPeriod;
1037
- this.bufferSize = bufferSize;
1057
+ this.ackAdvance = ackAdvance;
1038
1058
  this.sourceUsesAbortSignal = sourceUsesAbortSignal;
1059
+ if (bufferSize !== void 0 && bufferSize !== 0 && bufferSize < ackAdvance)
1060
+ warnLog3?.log(
1061
+ `RpcStream bufferSize (${bufferSize}) is below ackAdvance (${ackAdvance}); using ackAdvance as buffer size.`
1062
+ );
1063
+ this.bufferSize = bufferSize !== void 0 && bufferSize >= ackAdvance ? bufferSize : ackAdvance;
1039
1064
  }
1040
1065
  /** AbortSignal that is aborted when the sender is disconnected. */
1041
1066
  get abortSignal() {
@@ -1043,7 +1068,7 @@ var RpcStreamSender = class _RpcStreamSender {
1043
1068
  }
1044
1069
  /** Returns the stream reference string for the $sys.Ok response. */
1045
1070
  toRef() {
1046
- return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.bufferSize},${this.allowReconnect ? "1" : "0"},${this.isRealTime ? "1" : "0"}`;
1071
+ return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance},${this.allowReconnect ? "1" : "0"},${this.isRealTime ? "1" : "0"}`;
1047
1072
  }
1048
1073
  /**
1049
1074
  * Called by system call handler when $sys.Ack is received from the client.
@@ -1139,8 +1164,10 @@ var RpcStreamSender = class _RpcStreamSender {
1139
1164
  }
1140
1165
  /**
1141
1166
  * ACK-driven pump. Non-real-time streams apply normal backpressure at
1142
- * `bufferSize`; real-time streams may compact the already-buffered unsent
1167
+ * `ackAdvance`; real-time streams may compact the already-buffered unsent
1143
1168
  * suffix to the latest buffered `canSkipTo` item after the consumer ACKs.
1169
+ * With `bufferSize > ackAdvance` the local ring buffer holds more than the
1170
+ * in-flight window — real-time mode uses the extra space to pre-buffer.
1144
1171
  */
1145
1172
  /* eslint-disable @typescript-eslint/no-unnecessary-condition -- _ended changes across awaits */
1146
1173
  async _run(iterator, state) {
@@ -1162,6 +1189,12 @@ var RpcStreamSender = class _RpcStreamSender {
1162
1189
  Promise.resolve(pending)
1163
1190
  ]);
1164
1191
  };
1192
+ const fireBuffered = () => {
1193
+ try {
1194
+ this.onBuffered?.(buffer.count);
1195
+ } catch {
1196
+ }
1197
+ };
1165
1198
  const bufferNext = async (onlyIfReady, prefetchNext) => {
1166
1199
  let r;
1167
1200
  try {
@@ -1179,6 +1212,7 @@ var RpcStreamSender = class _RpcStreamSender {
1179
1212
  if (prefetchNext)
1180
1213
  whenMovedNext = iterator.next();
1181
1214
  }
1215
+ fireBuffered();
1182
1216
  return true;
1183
1217
  } catch (e) {
1184
1218
  whenMovedNext = null;
@@ -1188,6 +1222,7 @@ var RpcStreamSender = class _RpcStreamSender {
1188
1222
  kind: "error",
1189
1223
  error: e instanceof Error ? e : new Error(String(e))
1190
1224
  });
1225
+ fireBuffered();
1191
1226
  return true;
1192
1227
  }
1193
1228
  };
@@ -1232,6 +1267,7 @@ var RpcStreamSender = class _RpcStreamSender {
1232
1267
  };
1233
1268
  }
1234
1269
  buffer.pushTail(item);
1270
+ fireBuffered();
1235
1271
  if (accepted || isFullyBuffered) break;
1236
1272
  }
1237
1273
  }
@@ -1246,7 +1282,7 @@ var RpcStreamSender = class _RpcStreamSender {
1246
1282
  return;
1247
1283
  }
1248
1284
  let bufferIndex = this._nextIndex - bufferStart;
1249
- const maxIndex = ack.nextIndex + this.bufferSize;
1285
+ const maxIndex = ack.nextIndex + this.ackAdvance;
1250
1286
  while (this._nextIndex < maxIndex) {
1251
1287
  if (this._ended) return;
1252
1288
  if (isRealTime) {
@@ -1380,7 +1416,7 @@ function _compactBufferedUnsentSuffix(buffer, firstUnsentIndex, canSkipTo) {
1380
1416
  // src/rpc-stream.ts
1381
1417
  var { warnLog: warnLog4 } = getLogs("RpcStream");
1382
1418
  var DEFAULT_ACK_PERIOD2 = 30;
1383
- var DEFAULT_BUFFER_SIZE2 = 61;
1419
+ var DEFAULT_ACK_ADVANCE2 = 61;
1384
1420
  function parseStreamRef(value) {
1385
1421
  if (typeof value === "string") {
1386
1422
  const parts = value.split(",");
@@ -1388,12 +1424,12 @@ function parseStreamRef(value) {
1388
1424
  const hostId = parts[0];
1389
1425
  const localId = parseInt(parts[1], 10);
1390
1426
  const ackPeriod = parseInt(parts[2], 10);
1391
- const bufferSize = parseInt(parts[3], 10);
1392
- if (isNaN(localId) || isNaN(ackPeriod) || isNaN(bufferSize))
1427
+ const ackAdvance = parseInt(parts[3], 10);
1428
+ if (isNaN(localId) || isNaN(ackPeriod) || isNaN(ackAdvance))
1393
1429
  return null;
1394
1430
  const allowReconnect = parts.length < 5 || parts[4] !== "0";
1395
1431
  const isRealTime = parts.length >= 6 && parts[5] === "1";
1396
- return { hostId, localId, ackPeriod, bufferSize, allowReconnect, isRealTime };
1432
+ return { hostId, localId, ackPeriod, ackAdvance, allowReconnect, isRealTime };
1397
1433
  }
1398
1434
  if (typeof value === "object" && value !== null) {
1399
1435
  const obj = value;
@@ -1404,10 +1440,10 @@ function parseStreamRef(value) {
1404
1440
  const hostId = String(rawHostId);
1405
1441
  const localId = Number(serializedId[1]);
1406
1442
  const ackPeriod = Number(obj.AckPeriod ?? 256);
1407
- const bufferSize = Number(obj.BufferSize ?? 128);
1443
+ const ackAdvance = Number(obj.AckAdvance ?? 128);
1408
1444
  const allowReconnect = obj.AllowReconnect !== false;
1409
1445
  const isRealTime = obj.IsRealTime === true;
1410
- return { hostId, localId, ackPeriod, bufferSize, allowReconnect, isRealTime };
1446
+ return { hostId, localId, ackPeriod, ackAdvance, allowReconnect, isRealTime };
1411
1447
  }
1412
1448
  return null;
1413
1449
  }
@@ -1427,6 +1463,11 @@ var RpcStream = class _RpcStream {
1427
1463
  isRealTime;
1428
1464
  canSkipTo;
1429
1465
  ackPeriod;
1466
+ ackAdvance;
1467
+ /**
1468
+ * Local-only ring buffer capacity hint for the sender. Undefined means
1469
+ * "use ackAdvance as the buffer size". Not part of the wire ref.
1470
+ */
1430
1471
  bufferSize;
1431
1472
  // Local (origin) mode
1432
1473
  localSource;
@@ -1457,7 +1498,8 @@ var RpcStream = class _RpcStream {
1457
1498
  this.isRealTime = options.isRealTime ?? false;
1458
1499
  this.canSkipTo = options.canSkipTo ?? (() => true);
1459
1500
  this.ackPeriod = options.ackPeriod ?? DEFAULT_ACK_PERIOD2;
1460
- this.bufferSize = options.bufferSize ?? DEFAULT_BUFFER_SIZE2;
1501
+ this.ackAdvance = options.ackAdvance ?? DEFAULT_ACK_ADVANCE2;
1502
+ this.bufferSize = options.bufferSize;
1461
1503
  } else {
1462
1504
  const ref = sourceOrRef;
1463
1505
  const peer = optionsOrPeer;
@@ -1468,7 +1510,7 @@ var RpcStream = class _RpcStream {
1468
1510
  this.isRealTime = ref.isRealTime;
1469
1511
  this.canSkipTo = () => true;
1470
1512
  this.ackPeriod = ref.ackPeriod;
1471
- this.bufferSize = ref.bufferSize;
1513
+ this.ackAdvance = ref.ackAdvance;
1472
1514
  }
1473
1515
  }
1474
1516
  /**
@@ -1491,7 +1533,7 @@ var RpcStream = class _RpcStream {
1491
1533
  * stream reference.
1492
1534
  *
1493
1535
  * For binary peers the ref is a MessagePack-compatible object
1494
- * (`{ SerializedId, AckPeriod, BufferSize, AllowReconnect }`);
1536
+ * (`{ SerializedId, AckPeriod, AckAdvance, AllowReconnect }`);
1495
1537
  * for text peers it is a comma-separated string.
1496
1538
  *
1497
1539
  * Can only be called once per stream.
@@ -1508,11 +1550,12 @@ var RpcStream = class _RpcStream {
1508
1550
  const sender = new RpcStreamSender(
1509
1551
  peer,
1510
1552
  this.ackPeriod,
1511
- this.bufferSize,
1553
+ this.ackAdvance,
1512
1554
  this.allowReconnect,
1513
1555
  this.isRealTime,
1514
1556
  this.canSkipTo,
1515
- isFactory
1557
+ isFactory,
1558
+ this.bufferSize
1516
1559
  );
1517
1560
  peer.sharedObjects.register(sender);
1518
1561
  this._sender = sender;
@@ -1521,7 +1564,7 @@ var RpcStream = class _RpcStream {
1521
1564
  this._toRefValue = peer.serializationFormat.isBinary ? {
1522
1565
  SerializedId: [sender.id.hostId, sender.id.localId],
1523
1566
  AckPeriod: sender.ackPeriod,
1524
- BufferSize: sender.bufferSize,
1567
+ AckAdvance: sender.ackAdvance,
1525
1568
  AllowReconnect: sender.allowReconnect
1526
1569
  } : sender.toRef();
1527
1570
  return this._toRefValue;
@@ -1836,19 +1879,17 @@ var RpcSystemCallHandler = class {
1836
1879
  sender?.onAckEnd(args[0]);
1837
1880
  break;
1838
1881
  }
1839
- default: {
1840
- if (method === "$sys.Disconnect" || method?.startsWith("$sys.Disconnect")) {
1841
- const ids = args[0];
1842
- if (Array.isArray(ids)) {
1843
- for (const id of ids) {
1844
- const remoteObj = peer.remoteObjects.get(id);
1845
- if (remoteObj && typeof remoteObj.disconnect === "function") {
1846
- remoteObj.disconnect();
1847
- }
1848
- const sharedObj = peer.sharedObjects.get(id);
1849
- if (sharedObj && typeof sharedObj.disconnect === "function") {
1850
- sharedObj.disconnect();
1851
- }
1882
+ case RpcSystemCalls.disconnect: {
1883
+ const ids = args[0];
1884
+ if (Array.isArray(ids)) {
1885
+ for (const id of ids) {
1886
+ const remoteObj = peer.remoteObjects.get(id);
1887
+ if (remoteObj && typeof remoteObj.disconnect === "function") {
1888
+ remoteObj.disconnect();
1889
+ }
1890
+ const sharedObj = peer.sharedObjects.get(id);
1891
+ if (sharedObj && typeof sharedObj.disconnect === "function") {
1892
+ sharedObj.disconnect();
1852
1893
  }
1853
1894
  }
1854
1895
  }
@@ -2245,6 +2286,9 @@ var RpcSerializationFormatResolver = class _RpcSerializationFormatResolver {
2245
2286
  };
2246
2287
 
2247
2288
  // src/rpc-peer.ts
2289
+ function yieldToEventLoop() {
2290
+ return new Promise((resolve) => setTimeout(resolve, 0));
2291
+ }
2248
2292
  var RPC_CLOSE_CODE_UNSUPPORTED_FORMAT = 4001;
2249
2293
  var HANDSHAKE_TIMEOUT_MS = 1e4;
2250
2294
  var KEEP_ALIVE_PERIOD_MS = 15e3;
@@ -2297,16 +2341,14 @@ var RpcPeer = class {
2297
2341
  * entered only after the handshake has succeeded. */
2298
2342
  connectionStateChanged = new import_core7.EventHandlerSet();
2299
2343
  _connection;
2300
- /** Mirror of `_connectionState === Connected` — true only after the
2301
- * handshake round-trip completes. Kept as its own field (rather than
2302
- * derived from `_connection`) so outbound calls can be gated on
2303
- * handshake completion, not merely WS-open: previously the run loop
2304
- * nulled `_connection` during the handshake window to suppress calls,
2305
- * which made `close()` a silent no-op and left a zombie peer when the
2306
- * hub removed it mid-handshake. */
2344
+ /** Mirror of `_connectionState === Connected`. True only after the
2345
+ * handshake round-trip completes; outbound `call()` checks this to
2346
+ * decide between sending now and deferring until the next `Connected`
2347
+ * transition. Kept as its own field so it can stay false through the
2348
+ * Handshaking phase without exposing the run loop's `_connection`
2349
+ * bookkeeping. */
2307
2350
  _isConnected = false;
2308
2351
  _connectionState = 0 /* Disconnected */;
2309
- _pendingSends = [];
2310
2352
  _keepAliveTimer;
2311
2353
  _keepAliveWatchdog;
2312
2354
  /** Wall-clock time of the last inbound `$sys.KeepAlive`, or 0 if none yet
@@ -2333,11 +2375,11 @@ var RpcPeer = class {
2333
2375
  if (this._connectionState === value) return;
2334
2376
  const previousState = this._connectionState;
2335
2377
  this._connectionState = value;
2336
- if (value === 2 /* Connected */) {
2378
+ if (value === 3 /* Connected */) {
2337
2379
  this._isConnected = true;
2338
2380
  this._armKeepAliveWatchdog();
2339
2381
  } else if (value === 0 /* Disconnected */) {
2340
- if (previousState === 2 /* Connected */)
2382
+ if (previousState === 3 /* Connected */)
2341
2383
  this._isConnected = false;
2342
2384
  this._disarmKeepAliveWatchdog();
2343
2385
  }
@@ -2346,11 +2388,11 @@ var RpcPeer = class {
2346
2388
  /** Resolves on the next transition into `Connected` — or immediately if
2347
2389
  * already in that state. */
2348
2390
  whenConnected() {
2349
- if (this._connectionState === 2 /* Connected */)
2391
+ if (this._connectionState === 3 /* Connected */)
2350
2392
  return Promise.resolve();
2351
2393
  return new Promise((resolve) => {
2352
2394
  const handler = (state) => {
2353
- if (state !== 2 /* Connected */) return;
2395
+ if (state !== 3 /* Connected */) return;
2354
2396
  this.connectionStateChanged.remove(handler);
2355
2397
  resolve();
2356
2398
  };
@@ -2400,11 +2442,18 @@ var RpcPeer = class {
2400
2442
  this._connection?.encoder,
2401
2443
  this.hub.registry
2402
2444
  );
2445
+ this.outboundCalls.register(outboundCall);
2403
2446
  if (this._isConnected) {
2404
- this.outboundCalls.register(outboundCall);
2405
2447
  this._sendWireData(outboundCall.serializedWireData);
2406
2448
  } else {
2407
- this._pendingSends.push(outboundCall);
2449
+ const stateHandler = (state) => {
2450
+ if (state !== 3 /* Connected */) return;
2451
+ this.connectionStateChanged.remove(stateHandler);
2452
+ if (outboundCall.result.isCompleted) return;
2453
+ this._sendWireData(outboundCall.serializedWireData);
2454
+ };
2455
+ this.connectionStateChanged.add(stateHandler);
2456
+ outboundCall.result.promise.then(() => this.connectionStateChanged.remove(stateHandler)).catch(() => this.connectionStateChanged.remove(stateHandler));
2408
2457
  }
2409
2458
  const signal = options?.signal;
2410
2459
  if (signal !== void 0) {
@@ -2414,17 +2463,6 @@ var RpcPeer = class {
2414
2463
  outboundCall.onDisconnect();
2415
2464
  if (this._connection !== void 0)
2416
2465
  this.hub.systemCallSender.cancel(this._connection, this.serializationFormat, callId);
2417
- } else {
2418
- const idx = this._pendingSends.findIndex(
2419
- (c) => c.callId === callId
2420
- );
2421
- if (idx !== -1) {
2422
- this._pendingSends.splice(idx, 1);
2423
- outboundCall.result.reject(
2424
- new Error("Call cancelled.")
2425
- );
2426
- outboundCall.onDisconnect();
2427
- }
2428
2466
  }
2429
2467
  };
2430
2468
  signal.addEventListener("abort", onAbort, { once: true });
@@ -2455,12 +2493,6 @@ var RpcPeer = class {
2455
2493
  infoLog?.log(`'${this.ref}': Stopping`);
2456
2494
  this.remoteObjects.disconnectAll();
2457
2495
  this.sharedObjects.disconnectAll();
2458
- for (const call of this._pendingSends) {
2459
- if (!call.result.isCompleted)
2460
- call.result.reject(new Error("Peer closed."));
2461
- call.onDisconnect();
2462
- }
2463
- this._pendingSends.length = 0;
2464
2496
  this.outboundCalls.rejectAll(new Error("Peer closed."));
2465
2497
  this.outboundCalls.invalidateAll();
2466
2498
  this._connection?.close();
@@ -2468,16 +2500,6 @@ var RpcPeer = class {
2468
2500
  this._setConnectionState(0 /* Disconnected */);
2469
2501
  infoLog?.log(`'${this.ref}': Stopped`);
2470
2502
  }
2471
- /** Send any messages buffered while disconnected. Call after connection + handshake are ready. */
2472
- _flushPendingSends() {
2473
- if (this._pendingSends.length === 0 || this._connection === void 0)
2474
- return;
2475
- for (const call of this._pendingSends) {
2476
- this.outboundCalls.register(call);
2477
- this._sendWireData(call.serializedWireData);
2478
- }
2479
- this._pendingSends.length = 0;
2480
- }
2481
2503
  /** Send pre-serialized wire data through the current connection. */
2482
2504
  _sendWireData(data) {
2483
2505
  if (this._connection === void 0) return;
@@ -2630,7 +2652,8 @@ var RpcPeer = class {
2630
2652
  var RpcConnectionState = /* @__PURE__ */ ((RpcConnectionState2) => {
2631
2653
  RpcConnectionState2[RpcConnectionState2["Disconnected"] = 0] = "Disconnected";
2632
2654
  RpcConnectionState2[RpcConnectionState2["Connecting"] = 1] = "Connecting";
2633
- RpcConnectionState2[RpcConnectionState2["Connected"] = 2] = "Connected";
2655
+ RpcConnectionState2[RpcConnectionState2["Handshaking"] = 2] = "Handshaking";
2656
+ RpcConnectionState2[RpcConnectionState2["Connected"] = 3] = "Connected";
2634
2657
  return RpcConnectionState2;
2635
2658
  })(RpcConnectionState || {});
2636
2659
  var RpcClientPeer = class extends RpcPeer {
@@ -2704,6 +2727,7 @@ var RpcClientPeer = class extends RpcPeer {
2704
2727
  connectWith(conn, isPeerChanged = true) {
2705
2728
  this.setupConnection(conn);
2706
2729
  this._isConnected = true;
2730
+ this.connectionStateChanged.trigger(3 /* Connected */);
2707
2731
  void this._reconnect(isPeerChanged);
2708
2732
  }
2709
2733
  /** Kick off the reconnect loop. Idempotent — subsequent calls are
@@ -2765,6 +2789,7 @@ var RpcClientPeer = class extends RpcPeer {
2765
2789
  this._pendingHandshake = new import_core7.PromiseSource();
2766
2790
  this.setupConnection(conn);
2767
2791
  await Promise.race([conn.whenConnected, closedRejection]);
2792
+ this._setConnectionState(2 /* Handshaking */);
2768
2793
  try {
2769
2794
  this.hub.systemCallSender.handshake(
2770
2795
  conn,
@@ -2816,7 +2841,7 @@ var RpcClientPeer = class extends RpcPeer {
2816
2841
  this._lastRemoteHubId = remoteHubId;
2817
2842
  }
2818
2843
  infoLog?.log(`'${this.ref}': Handshake succeeded, peerChanged=${isPeerChanged}`);
2819
- this._setConnectionState(2 /* Connected */);
2844
+ this._setConnectionState(3 /* Connected */);
2820
2845
  this._tryIndex = 0;
2821
2846
  await this._reconnect(isPeerChanged, closedController.signal);
2822
2847
  await whenClosed.promise;
@@ -2891,6 +2916,8 @@ var RpcClientPeer = class extends RpcPeer {
2891
2916
  if (!isPeerChanged && eligible.length > 0) {
2892
2917
  unknownIds = await this._reconcileReconnect(eligible, closedSignal);
2893
2918
  }
2919
+ const YIELD_BATCH = 20;
2920
+ let resentSinceYield = 0;
2894
2921
  for (const call of eligible) {
2895
2922
  if (!call.removeOnOk && call.result.isCompleted) {
2896
2923
  call.onDisconnect();
@@ -2901,8 +2928,12 @@ var RpcClientPeer = class extends RpcPeer {
2901
2928
  continue;
2902
2929
  }
2903
2930
  this._sendWireData(call.serializedWireData);
2931
+ resentSinceYield++;
2932
+ if (resentSinceYield >= YIELD_BATCH) {
2933
+ resentSinceYield = 0;
2934
+ await yieldToEventLoop();
2935
+ }
2904
2936
  }
2905
- this._flushPendingSends();
2906
2937
  }
2907
2938
  /**
2908
2939
  * Ask the server which call IDs in `eligible` it no longer recognizes
@@ -2979,6 +3010,7 @@ var RpcServerPeer = class extends RpcPeer {
2979
3010
  accept(conn) {
2980
3011
  this._setConnectionState(1 /* Connecting */);
2981
3012
  this.setupConnection(conn);
3013
+ this._setConnectionState(2 /* Handshaking */);
2982
3014
  conn.closed.add(() => {
2983
3015
  if (this._connection !== conn && this._connection !== void 0) return;
2984
3016
  this.sharedObjects.disconnectAll();
@@ -2993,7 +3025,7 @@ var RpcServerPeer = class extends RpcPeer {
2993
3025
  this.hub.hubId,
2994
3026
  0
2995
3027
  );
2996
- this._setConnectionState(2 /* Connected */);
3028
+ this._setConnectionState(3 /* Connected */);
2997
3029
  }
2998
3030
  }
2999
3031
  };
@@ -3948,7 +3980,7 @@ var RpcPeerStateMonitor = class {
3948
3980
  _timer;
3949
3981
  // Arrow functions to preserve `this` when used as event handlers
3950
3982
  _onConnectionStateChanged = (state) => {
3951
- if (state === 2 /* Connected */) {
3983
+ if (state === 3 /* Connected */) {
3952
3984
  this._rawConnected = true;
3953
3985
  this._connectedAt = Date.now();
3954
3986
  this._lastError = void 0;