@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.js CHANGED
@@ -42,7 +42,8 @@ var RpcSystemCalls = {
42
42
  ackEnd: "$sys.AckEnd:1",
43
43
  item: "$sys.I:2",
44
44
  batch: "$sys.B:2",
45
- end: "$sys.End:2"
45
+ end: "$sys.End:2",
46
+ disconnect: "$sys.Disconnect:1"
46
47
  };
47
48
  var ENVELOPE_DELIMITER = "\n";
48
49
  var ARG_DELIMITER = "";
@@ -870,7 +871,7 @@ var RpcObjectKind = { Local: 0, Remote: 1 };
870
871
  import { PromiseSource as PromiseSource3, RingBuffer } from "@actuallab/core";
871
872
  var { warnLog: warnLog3 } = getLogs("RpcSharedStream");
872
873
  var DEFAULT_ACK_PERIOD = 256;
873
- var DEFAULT_BUFFER_SIZE = 128;
874
+ var DEFAULT_ACK_ADVANCE = 128;
874
875
  var RpcStreamSender = class _RpcStreamSender {
875
876
  static _emptyGuid = "00000000-0000-0000-0000-000000000000";
876
877
  id;
@@ -880,6 +881,18 @@ var RpcStreamSender = class _RpcStreamSender {
880
881
  canSkipTo;
881
882
  peer;
882
883
  ackPeriod;
884
+ /**
885
+ * Wire-level flow-control window: the maximum index the sender may
886
+ * advance past the most recently acknowledged index. Mirrors .NET
887
+ * `RpcStream.AckAdvance`.
888
+ */
889
+ ackAdvance;
890
+ /**
891
+ * Effective local ring buffer capacity used by the pump. Set larger
892
+ * than `ackAdvance` to pre-buffer items beyond the in-flight window;
893
+ * values below `ackAdvance` are clamped up (with a warning) at
894
+ * construction time. Not transmitted on the wire.
895
+ */
883
896
  bufferSize;
884
897
  /**
885
898
  * If true, the source was produced by a factory `(abortSignal) => AsyncIterable<T>`
@@ -908,6 +921,13 @@ var RpcStreamSender = class _RpcStreamSender {
908
921
  * watchdog so it can distinguish "stuck" (no ACK > N s) from
909
922
  * "throttled" (ACKs flowing). */
910
923
  onAckProcessed;
924
+ /** Fires after each item is pushed onto the local ring buffer. The
925
+ * argument is the buffer count *after* the push. Together with
926
+ * `onAckProcessed` (which is the only place items leave the buffer),
927
+ * this gives controllers a complete picture of buffer utilization.
928
+ * When `bufferedCount === bufferSize`, the source pull is paused
929
+ * until the next ACK frees space. Listener errors are swallowed. */
930
+ onBuffered;
911
931
  get nextIndex() {
912
932
  return this._nextIndex;
913
933
  }
@@ -920,7 +940,7 @@ var RpcStreamSender = class _RpcStreamSender {
920
940
  // -- ACK queue (analog of .NET's Channel<(long, bool)>) --
921
941
  _acks = [];
922
942
  _whenAckReady = null;
923
- constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, bufferSize = DEFAULT_BUFFER_SIZE, allowReconnect = true, isRealTime = false, canSkipTo = () => true, sourceUsesAbortSignal = false) {
943
+ constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, ackAdvance = DEFAULT_ACK_ADVANCE, allowReconnect = true, isRealTime = false, canSkipTo = () => true, sourceUsesAbortSignal = false, bufferSize) {
924
944
  const localId = peer.sharedObjects.nextId();
925
945
  this.id = { hostId: peer.hub.hubId, localId };
926
946
  this.allowReconnect = allowReconnect;
@@ -928,8 +948,13 @@ var RpcStreamSender = class _RpcStreamSender {
928
948
  this.canSkipTo = canSkipTo;
929
949
  this.peer = peer;
930
950
  this.ackPeriod = ackPeriod;
931
- this.bufferSize = bufferSize;
951
+ this.ackAdvance = ackAdvance;
932
952
  this.sourceUsesAbortSignal = sourceUsesAbortSignal;
953
+ if (bufferSize !== void 0 && bufferSize !== 0 && bufferSize < ackAdvance)
954
+ warnLog3?.log(
955
+ `RpcStream bufferSize (${bufferSize}) is below ackAdvance (${ackAdvance}); using ackAdvance as buffer size.`
956
+ );
957
+ this.bufferSize = bufferSize !== void 0 && bufferSize >= ackAdvance ? bufferSize : ackAdvance;
933
958
  }
934
959
  /** AbortSignal that is aborted when the sender is disconnected. */
935
960
  get abortSignal() {
@@ -937,7 +962,7 @@ var RpcStreamSender = class _RpcStreamSender {
937
962
  }
938
963
  /** Returns the stream reference string for the $sys.Ok response. */
939
964
  toRef() {
940
- return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.bufferSize},${this.allowReconnect ? "1" : "0"},${this.isRealTime ? "1" : "0"}`;
965
+ return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance},${this.allowReconnect ? "1" : "0"},${this.isRealTime ? "1" : "0"}`;
941
966
  }
942
967
  /**
943
968
  * Called by system call handler when $sys.Ack is received from the client.
@@ -1033,8 +1058,10 @@ var RpcStreamSender = class _RpcStreamSender {
1033
1058
  }
1034
1059
  /**
1035
1060
  * ACK-driven pump. Non-real-time streams apply normal backpressure at
1036
- * `bufferSize`; real-time streams may compact the already-buffered unsent
1061
+ * `ackAdvance`; real-time streams may compact the already-buffered unsent
1037
1062
  * suffix to the latest buffered `canSkipTo` item after the consumer ACKs.
1063
+ * With `bufferSize > ackAdvance` the local ring buffer holds more than the
1064
+ * in-flight window — real-time mode uses the extra space to pre-buffer.
1038
1065
  */
1039
1066
  /* eslint-disable @typescript-eslint/no-unnecessary-condition -- _ended changes across awaits */
1040
1067
  async _run(iterator, state) {
@@ -1056,6 +1083,12 @@ var RpcStreamSender = class _RpcStreamSender {
1056
1083
  Promise.resolve(pending)
1057
1084
  ]);
1058
1085
  };
1086
+ const fireBuffered = () => {
1087
+ try {
1088
+ this.onBuffered?.(buffer.count);
1089
+ } catch {
1090
+ }
1091
+ };
1059
1092
  const bufferNext = async (onlyIfReady, prefetchNext) => {
1060
1093
  let r;
1061
1094
  try {
@@ -1073,6 +1106,7 @@ var RpcStreamSender = class _RpcStreamSender {
1073
1106
  if (prefetchNext)
1074
1107
  whenMovedNext = iterator.next();
1075
1108
  }
1109
+ fireBuffered();
1076
1110
  return true;
1077
1111
  } catch (e) {
1078
1112
  whenMovedNext = null;
@@ -1082,6 +1116,7 @@ var RpcStreamSender = class _RpcStreamSender {
1082
1116
  kind: "error",
1083
1117
  error: e instanceof Error ? e : new Error(String(e))
1084
1118
  });
1119
+ fireBuffered();
1085
1120
  return true;
1086
1121
  }
1087
1122
  };
@@ -1126,6 +1161,7 @@ var RpcStreamSender = class _RpcStreamSender {
1126
1161
  };
1127
1162
  }
1128
1163
  buffer.pushTail(item);
1164
+ fireBuffered();
1129
1165
  if (accepted || isFullyBuffered) break;
1130
1166
  }
1131
1167
  }
@@ -1140,7 +1176,7 @@ var RpcStreamSender = class _RpcStreamSender {
1140
1176
  return;
1141
1177
  }
1142
1178
  let bufferIndex = this._nextIndex - bufferStart;
1143
- const maxIndex = ack.nextIndex + this.bufferSize;
1179
+ const maxIndex = ack.nextIndex + this.ackAdvance;
1144
1180
  while (this._nextIndex < maxIndex) {
1145
1181
  if (this._ended) return;
1146
1182
  if (isRealTime) {
@@ -1274,7 +1310,7 @@ function _compactBufferedUnsentSuffix(buffer, firstUnsentIndex, canSkipTo) {
1274
1310
  // src/rpc-stream.ts
1275
1311
  var { warnLog: warnLog4 } = getLogs("RpcStream");
1276
1312
  var DEFAULT_ACK_PERIOD2 = 30;
1277
- var DEFAULT_BUFFER_SIZE2 = 61;
1313
+ var DEFAULT_ACK_ADVANCE2 = 61;
1278
1314
  function parseStreamRef(value) {
1279
1315
  if (typeof value === "string") {
1280
1316
  const parts = value.split(",");
@@ -1282,12 +1318,12 @@ function parseStreamRef(value) {
1282
1318
  const hostId = parts[0];
1283
1319
  const localId = parseInt(parts[1], 10);
1284
1320
  const ackPeriod = parseInt(parts[2], 10);
1285
- const bufferSize = parseInt(parts[3], 10);
1286
- if (isNaN(localId) || isNaN(ackPeriod) || isNaN(bufferSize))
1321
+ const ackAdvance = parseInt(parts[3], 10);
1322
+ if (isNaN(localId) || isNaN(ackPeriod) || isNaN(ackAdvance))
1287
1323
  return null;
1288
1324
  const allowReconnect = parts.length < 5 || parts[4] !== "0";
1289
1325
  const isRealTime = parts.length >= 6 && parts[5] === "1";
1290
- return { hostId, localId, ackPeriod, bufferSize, allowReconnect, isRealTime };
1326
+ return { hostId, localId, ackPeriod, ackAdvance, allowReconnect, isRealTime };
1291
1327
  }
1292
1328
  if (typeof value === "object" && value !== null) {
1293
1329
  const obj = value;
@@ -1298,10 +1334,10 @@ function parseStreamRef(value) {
1298
1334
  const hostId = String(rawHostId);
1299
1335
  const localId = Number(serializedId[1]);
1300
1336
  const ackPeriod = Number(obj.AckPeriod ?? 256);
1301
- const bufferSize = Number(obj.BufferSize ?? 128);
1337
+ const ackAdvance = Number(obj.AckAdvance ?? 128);
1302
1338
  const allowReconnect = obj.AllowReconnect !== false;
1303
1339
  const isRealTime = obj.IsRealTime === true;
1304
- return { hostId, localId, ackPeriod, bufferSize, allowReconnect, isRealTime };
1340
+ return { hostId, localId, ackPeriod, ackAdvance, allowReconnect, isRealTime };
1305
1341
  }
1306
1342
  return null;
1307
1343
  }
@@ -1321,6 +1357,11 @@ var RpcStream = class _RpcStream {
1321
1357
  isRealTime;
1322
1358
  canSkipTo;
1323
1359
  ackPeriod;
1360
+ ackAdvance;
1361
+ /**
1362
+ * Local-only ring buffer capacity hint for the sender. Undefined means
1363
+ * "use ackAdvance as the buffer size". Not part of the wire ref.
1364
+ */
1324
1365
  bufferSize;
1325
1366
  // Local (origin) mode
1326
1367
  localSource;
@@ -1351,7 +1392,8 @@ var RpcStream = class _RpcStream {
1351
1392
  this.isRealTime = options.isRealTime ?? false;
1352
1393
  this.canSkipTo = options.canSkipTo ?? (() => true);
1353
1394
  this.ackPeriod = options.ackPeriod ?? DEFAULT_ACK_PERIOD2;
1354
- this.bufferSize = options.bufferSize ?? DEFAULT_BUFFER_SIZE2;
1395
+ this.ackAdvance = options.ackAdvance ?? DEFAULT_ACK_ADVANCE2;
1396
+ this.bufferSize = options.bufferSize;
1355
1397
  } else {
1356
1398
  const ref = sourceOrRef;
1357
1399
  const peer = optionsOrPeer;
@@ -1362,7 +1404,7 @@ var RpcStream = class _RpcStream {
1362
1404
  this.isRealTime = ref.isRealTime;
1363
1405
  this.canSkipTo = () => true;
1364
1406
  this.ackPeriod = ref.ackPeriod;
1365
- this.bufferSize = ref.bufferSize;
1407
+ this.ackAdvance = ref.ackAdvance;
1366
1408
  }
1367
1409
  }
1368
1410
  /**
@@ -1385,7 +1427,7 @@ var RpcStream = class _RpcStream {
1385
1427
  * stream reference.
1386
1428
  *
1387
1429
  * For binary peers the ref is a MessagePack-compatible object
1388
- * (`{ SerializedId, AckPeriod, BufferSize, AllowReconnect }`);
1430
+ * (`{ SerializedId, AckPeriod, AckAdvance, AllowReconnect }`);
1389
1431
  * for text peers it is a comma-separated string.
1390
1432
  *
1391
1433
  * Can only be called once per stream.
@@ -1402,11 +1444,12 @@ var RpcStream = class _RpcStream {
1402
1444
  const sender = new RpcStreamSender(
1403
1445
  peer,
1404
1446
  this.ackPeriod,
1405
- this.bufferSize,
1447
+ this.ackAdvance,
1406
1448
  this.allowReconnect,
1407
1449
  this.isRealTime,
1408
1450
  this.canSkipTo,
1409
- isFactory
1451
+ isFactory,
1452
+ this.bufferSize
1410
1453
  );
1411
1454
  peer.sharedObjects.register(sender);
1412
1455
  this._sender = sender;
@@ -1415,7 +1458,7 @@ var RpcStream = class _RpcStream {
1415
1458
  this._toRefValue = peer.serializationFormat.isBinary ? {
1416
1459
  SerializedId: [sender.id.hostId, sender.id.localId],
1417
1460
  AckPeriod: sender.ackPeriod,
1418
- BufferSize: sender.bufferSize,
1461
+ AckAdvance: sender.ackAdvance,
1419
1462
  AllowReconnect: sender.allowReconnect
1420
1463
  } : sender.toRef();
1421
1464
  return this._toRefValue;
@@ -1730,19 +1773,17 @@ var RpcSystemCallHandler = class {
1730
1773
  sender?.onAckEnd(args[0]);
1731
1774
  break;
1732
1775
  }
1733
- default: {
1734
- if (method === "$sys.Disconnect" || method?.startsWith("$sys.Disconnect")) {
1735
- const ids = args[0];
1736
- if (Array.isArray(ids)) {
1737
- for (const id of ids) {
1738
- const remoteObj = peer.remoteObjects.get(id);
1739
- if (remoteObj && typeof remoteObj.disconnect === "function") {
1740
- remoteObj.disconnect();
1741
- }
1742
- const sharedObj = peer.sharedObjects.get(id);
1743
- if (sharedObj && typeof sharedObj.disconnect === "function") {
1744
- sharedObj.disconnect();
1745
- }
1776
+ case RpcSystemCalls.disconnect: {
1777
+ const ids = args[0];
1778
+ if (Array.isArray(ids)) {
1779
+ for (const id of ids) {
1780
+ const remoteObj = peer.remoteObjects.get(id);
1781
+ if (remoteObj && typeof remoteObj.disconnect === "function") {
1782
+ remoteObj.disconnect();
1783
+ }
1784
+ const sharedObj = peer.sharedObjects.get(id);
1785
+ if (sharedObj && typeof sharedObj.disconnect === "function") {
1786
+ sharedObj.disconnect();
1746
1787
  }
1747
1788
  }
1748
1789
  }
@@ -2139,6 +2180,9 @@ var RpcSerializationFormatResolver = class _RpcSerializationFormatResolver {
2139
2180
  };
2140
2181
 
2141
2182
  // src/rpc-peer.ts
2183
+ function yieldToEventLoop() {
2184
+ return new Promise((resolve) => setTimeout(resolve, 0));
2185
+ }
2142
2186
  var RPC_CLOSE_CODE_UNSUPPORTED_FORMAT = 4001;
2143
2187
  var HANDSHAKE_TIMEOUT_MS = 1e4;
2144
2188
  var KEEP_ALIVE_PERIOD_MS = 15e3;
@@ -2191,16 +2235,14 @@ var RpcPeer = class {
2191
2235
  * entered only after the handshake has succeeded. */
2192
2236
  connectionStateChanged = new EventHandlerSet3();
2193
2237
  _connection;
2194
- /** Mirror of `_connectionState === Connected` — true only after the
2195
- * handshake round-trip completes. Kept as its own field (rather than
2196
- * derived from `_connection`) so outbound calls can be gated on
2197
- * handshake completion, not merely WS-open: previously the run loop
2198
- * nulled `_connection` during the handshake window to suppress calls,
2199
- * which made `close()` a silent no-op and left a zombie peer when the
2200
- * hub removed it mid-handshake. */
2238
+ /** Mirror of `_connectionState === Connected`. True only after the
2239
+ * handshake round-trip completes; outbound `call()` checks this to
2240
+ * decide between sending now and deferring until the next `Connected`
2241
+ * transition. Kept as its own field so it can stay false through the
2242
+ * Handshaking phase without exposing the run loop's `_connection`
2243
+ * bookkeeping. */
2201
2244
  _isConnected = false;
2202
2245
  _connectionState = 0 /* Disconnected */;
2203
- _pendingSends = [];
2204
2246
  _keepAliveTimer;
2205
2247
  _keepAliveWatchdog;
2206
2248
  /** Wall-clock time of the last inbound `$sys.KeepAlive`, or 0 if none yet
@@ -2227,11 +2269,11 @@ var RpcPeer = class {
2227
2269
  if (this._connectionState === value) return;
2228
2270
  const previousState = this._connectionState;
2229
2271
  this._connectionState = value;
2230
- if (value === 2 /* Connected */) {
2272
+ if (value === 3 /* Connected */) {
2231
2273
  this._isConnected = true;
2232
2274
  this._armKeepAliveWatchdog();
2233
2275
  } else if (value === 0 /* Disconnected */) {
2234
- if (previousState === 2 /* Connected */)
2276
+ if (previousState === 3 /* Connected */)
2235
2277
  this._isConnected = false;
2236
2278
  this._disarmKeepAliveWatchdog();
2237
2279
  }
@@ -2240,11 +2282,11 @@ var RpcPeer = class {
2240
2282
  /** Resolves on the next transition into `Connected` — or immediately if
2241
2283
  * already in that state. */
2242
2284
  whenConnected() {
2243
- if (this._connectionState === 2 /* Connected */)
2285
+ if (this._connectionState === 3 /* Connected */)
2244
2286
  return Promise.resolve();
2245
2287
  return new Promise((resolve) => {
2246
2288
  const handler = (state) => {
2247
- if (state !== 2 /* Connected */) return;
2289
+ if (state !== 3 /* Connected */) return;
2248
2290
  this.connectionStateChanged.remove(handler);
2249
2291
  resolve();
2250
2292
  };
@@ -2294,11 +2336,18 @@ var RpcPeer = class {
2294
2336
  this._connection?.encoder,
2295
2337
  this.hub.registry
2296
2338
  );
2339
+ this.outboundCalls.register(outboundCall);
2297
2340
  if (this._isConnected) {
2298
- this.outboundCalls.register(outboundCall);
2299
2341
  this._sendWireData(outboundCall.serializedWireData);
2300
2342
  } else {
2301
- this._pendingSends.push(outboundCall);
2343
+ const stateHandler = (state) => {
2344
+ if (state !== 3 /* Connected */) return;
2345
+ this.connectionStateChanged.remove(stateHandler);
2346
+ if (outboundCall.result.isCompleted) return;
2347
+ this._sendWireData(outboundCall.serializedWireData);
2348
+ };
2349
+ this.connectionStateChanged.add(stateHandler);
2350
+ outboundCall.result.promise.then(() => this.connectionStateChanged.remove(stateHandler)).catch(() => this.connectionStateChanged.remove(stateHandler));
2302
2351
  }
2303
2352
  const signal = options?.signal;
2304
2353
  if (signal !== void 0) {
@@ -2308,17 +2357,6 @@ var RpcPeer = class {
2308
2357
  outboundCall.onDisconnect();
2309
2358
  if (this._connection !== void 0)
2310
2359
  this.hub.systemCallSender.cancel(this._connection, this.serializationFormat, callId);
2311
- } else {
2312
- const idx = this._pendingSends.findIndex(
2313
- (c) => c.callId === callId
2314
- );
2315
- if (idx !== -1) {
2316
- this._pendingSends.splice(idx, 1);
2317
- outboundCall.result.reject(
2318
- new Error("Call cancelled.")
2319
- );
2320
- outboundCall.onDisconnect();
2321
- }
2322
2360
  }
2323
2361
  };
2324
2362
  signal.addEventListener("abort", onAbort, { once: true });
@@ -2349,12 +2387,6 @@ var RpcPeer = class {
2349
2387
  infoLog?.log(`'${this.ref}': Stopping`);
2350
2388
  this.remoteObjects.disconnectAll();
2351
2389
  this.sharedObjects.disconnectAll();
2352
- for (const call of this._pendingSends) {
2353
- if (!call.result.isCompleted)
2354
- call.result.reject(new Error("Peer closed."));
2355
- call.onDisconnect();
2356
- }
2357
- this._pendingSends.length = 0;
2358
2390
  this.outboundCalls.rejectAll(new Error("Peer closed."));
2359
2391
  this.outboundCalls.invalidateAll();
2360
2392
  this._connection?.close();
@@ -2362,16 +2394,6 @@ var RpcPeer = class {
2362
2394
  this._setConnectionState(0 /* Disconnected */);
2363
2395
  infoLog?.log(`'${this.ref}': Stopped`);
2364
2396
  }
2365
- /** Send any messages buffered while disconnected. Call after connection + handshake are ready. */
2366
- _flushPendingSends() {
2367
- if (this._pendingSends.length === 0 || this._connection === void 0)
2368
- return;
2369
- for (const call of this._pendingSends) {
2370
- this.outboundCalls.register(call);
2371
- this._sendWireData(call.serializedWireData);
2372
- }
2373
- this._pendingSends.length = 0;
2374
- }
2375
2397
  /** Send pre-serialized wire data through the current connection. */
2376
2398
  _sendWireData(data) {
2377
2399
  if (this._connection === void 0) return;
@@ -2524,7 +2546,8 @@ var RpcPeer = class {
2524
2546
  var RpcConnectionState = /* @__PURE__ */ ((RpcConnectionState2) => {
2525
2547
  RpcConnectionState2[RpcConnectionState2["Disconnected"] = 0] = "Disconnected";
2526
2548
  RpcConnectionState2[RpcConnectionState2["Connecting"] = 1] = "Connecting";
2527
- RpcConnectionState2[RpcConnectionState2["Connected"] = 2] = "Connected";
2549
+ RpcConnectionState2[RpcConnectionState2["Handshaking"] = 2] = "Handshaking";
2550
+ RpcConnectionState2[RpcConnectionState2["Connected"] = 3] = "Connected";
2528
2551
  return RpcConnectionState2;
2529
2552
  })(RpcConnectionState || {});
2530
2553
  var RpcClientPeer = class extends RpcPeer {
@@ -2598,6 +2621,7 @@ var RpcClientPeer = class extends RpcPeer {
2598
2621
  connectWith(conn, isPeerChanged = true) {
2599
2622
  this.setupConnection(conn);
2600
2623
  this._isConnected = true;
2624
+ this.connectionStateChanged.trigger(3 /* Connected */);
2601
2625
  void this._reconnect(isPeerChanged);
2602
2626
  }
2603
2627
  /** Kick off the reconnect loop. Idempotent — subsequent calls are
@@ -2659,6 +2683,7 @@ var RpcClientPeer = class extends RpcPeer {
2659
2683
  this._pendingHandshake = new PromiseSource5();
2660
2684
  this.setupConnection(conn);
2661
2685
  await Promise.race([conn.whenConnected, closedRejection]);
2686
+ this._setConnectionState(2 /* Handshaking */);
2662
2687
  try {
2663
2688
  this.hub.systemCallSender.handshake(
2664
2689
  conn,
@@ -2710,7 +2735,7 @@ var RpcClientPeer = class extends RpcPeer {
2710
2735
  this._lastRemoteHubId = remoteHubId;
2711
2736
  }
2712
2737
  infoLog?.log(`'${this.ref}': Handshake succeeded, peerChanged=${isPeerChanged}`);
2713
- this._setConnectionState(2 /* Connected */);
2738
+ this._setConnectionState(3 /* Connected */);
2714
2739
  this._tryIndex = 0;
2715
2740
  await this._reconnect(isPeerChanged, closedController.signal);
2716
2741
  await whenClosed.promise;
@@ -2785,6 +2810,8 @@ var RpcClientPeer = class extends RpcPeer {
2785
2810
  if (!isPeerChanged && eligible.length > 0) {
2786
2811
  unknownIds = await this._reconcileReconnect(eligible, closedSignal);
2787
2812
  }
2813
+ const YIELD_BATCH = 20;
2814
+ let resentSinceYield = 0;
2788
2815
  for (const call of eligible) {
2789
2816
  if (!call.removeOnOk && call.result.isCompleted) {
2790
2817
  call.onDisconnect();
@@ -2795,8 +2822,12 @@ var RpcClientPeer = class extends RpcPeer {
2795
2822
  continue;
2796
2823
  }
2797
2824
  this._sendWireData(call.serializedWireData);
2825
+ resentSinceYield++;
2826
+ if (resentSinceYield >= YIELD_BATCH) {
2827
+ resentSinceYield = 0;
2828
+ await yieldToEventLoop();
2829
+ }
2798
2830
  }
2799
- this._flushPendingSends();
2800
2831
  }
2801
2832
  /**
2802
2833
  * Ask the server which call IDs in `eligible` it no longer recognizes
@@ -2873,6 +2904,7 @@ var RpcServerPeer = class extends RpcPeer {
2873
2904
  accept(conn) {
2874
2905
  this._setConnectionState(1 /* Connecting */);
2875
2906
  this.setupConnection(conn);
2907
+ this._setConnectionState(2 /* Handshaking */);
2876
2908
  conn.closed.add(() => {
2877
2909
  if (this._connection !== conn && this._connection !== void 0) return;
2878
2910
  this.sharedObjects.disconnectAll();
@@ -2887,7 +2919,7 @@ var RpcServerPeer = class extends RpcPeer {
2887
2919
  this.hub.hubId,
2888
2920
  0
2889
2921
  );
2890
- this._setConnectionState(2 /* Connected */);
2922
+ this._setConnectionState(3 /* Connected */);
2891
2923
  }
2892
2924
  }
2893
2925
  };
@@ -3842,7 +3874,7 @@ var RpcPeerStateMonitor = class {
3842
3874
  _timer;
3843
3875
  // Arrow functions to preserve `this` when used as event handlers
3844
3876
  _onConnectionStateChanged = (state) => {
3845
- if (state === 2 /* Connected */) {
3877
+ if (state === 3 /* Connected */) {
3846
3878
  this._rawConnected = true;
3847
3879
  this._connectedAt = Date.now();
3848
3880
  this._lastError = void 0;