@zlink-systems/stream-connector 0.24.0 → 0.26.0

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.
@@ -53,11 +53,7 @@ var ZlinkStreamConnectorBundle = (() => {
53
53
  pickled.set(payload, 1);
54
54
  return pickled;
55
55
  }
56
- function lz4UnpicklePayload(payload, maxSize) {
57
- const maxDecompressedSize = maxSize != null ? maxSize : defaultMaxDecompressedPayloadSize;
58
- if (payload.length === 0) {
59
- return new Uint8Array();
60
- }
56
+ function readLz4Pickle(payload) {
61
57
  const header = payload[0];
62
58
  if ((header & 7) !== 0) {
63
59
  throw new Error("Unexpected LZ4 pickle version.");
@@ -69,7 +65,17 @@ var ZlinkStreamConnectorBundle = (() => {
69
65
  }
70
66
  const data = payload.subarray(dataOffset);
71
67
  const resultDiff = sizeOfDiff === 0 ? 0 : readLittleEndian(payload, 1, sizeOfDiff);
72
- const resultLength = data.length + resultDiff;
68
+ return { data, resultDiff, resultLength: data.length + resultDiff };
69
+ }
70
+ function lz4PickledLength(payload) {
71
+ return payload.length === 0 ? 0 : readLz4Pickle(payload).resultLength;
72
+ }
73
+ function lz4UnpicklePayload(payload, maxSize) {
74
+ const maxDecompressedSize = maxSize != null ? maxSize : defaultMaxDecompressedPayloadSize;
75
+ if (payload.length === 0) {
76
+ return new Uint8Array();
77
+ }
78
+ const { data, resultDiff, resultLength } = readLz4Pickle(payload);
73
79
  if (resultLength > maxDecompressedSize) {
74
80
  throw new Error("LZ4 decoded payload exceeds maximum stream payload size.");
75
81
  }
@@ -420,6 +426,9 @@ var ZlinkStreamConnectorBundle = (() => {
420
426
  function lz4PickleUncompressed2(payload) {
421
427
  return lz4PickleUncompressed(payload);
422
428
  }
429
+ function lz4PickledLength2(payload) {
430
+ return lz4PickledLength(payload);
431
+ }
423
432
  function lz4UnpicklePayload2(payload, maxDecompressedSize) {
424
433
  return lz4UnpicklePayload(payload, maxDecompressedSize != null ? maxDecompressedSize : defaultMaxDecompressedPayloadSize);
425
434
  }
@@ -864,9 +873,10 @@ var ZlinkStreamConnectorBundle = (() => {
864
873
  }
865
874
  };
866
875
  var ZlinkStreamRequestBuilder = class {
867
- constructor(connector, name, payload, actorSlot, validateActor) {
876
+ constructor(connector, name, payload, enqueueCallback, actorSlot, validateActor) {
868
877
  this.connector = connector;
869
878
  this.payload = payload;
879
+ this.enqueueCallback = enqueueCallback;
870
880
  __publicField(this, "state");
871
881
  this.state = new ZlinkStreamCallBuilderState(name, actorSlot, validateActor);
872
882
  }
@@ -901,8 +911,10 @@ var ZlinkStreamConnectorBundle = (() => {
901
911
  );
902
912
  if (typeof signalOrCallback === "function") {
903
913
  operation.then(
904
- (value) => signalOrCallback({ isSuccess: true, value }),
905
- (error) => signalOrCallback({ isSuccess: false, error: unwrapStreamError(error) })
914
+ (value) => this.enqueueCallback(() => signalOrCallback({ isSuccess: true, value })),
915
+ (error) => this.enqueueCallback(
916
+ () => signalOrCallback({ isSuccess: false, error: unwrapStreamError(error) })
917
+ )
906
918
  );
907
919
  return;
908
920
  }
@@ -1113,6 +1125,12 @@ var ZlinkStreamConnectorBundle = (() => {
1113
1125
  return lz4PickleUncompressed2(payload);
1114
1126
  },
1115
1127
  decompress(payload, maxDecompressedSize) {
1128
+ if (lz4PickledLength2(payload) > maxDecompressedSize) {
1129
+ throw connectorError(
1130
+ "frameTooLarge" /* FrameTooLarge */,
1131
+ "LZ4 decoded payload exceeds MaxReceivePayloadSize."
1132
+ );
1133
+ }
1116
1134
  return lz4UnpicklePayload2(payload, maxDecompressedSize);
1117
1135
  }
1118
1136
  };
@@ -1124,7 +1142,11 @@ var ZlinkStreamConnectorBundle = (() => {
1124
1142
  "Compression codec is not configured."
1125
1143
  );
1126
1144
  }
1127
- return codec.compress(payload);
1145
+ try {
1146
+ return codec.compress(payload);
1147
+ } catch (cause) {
1148
+ throw connectorError("compressionFailed" /* CompressionFailed */, "Compression failed.", cause);
1149
+ }
1128
1150
  }
1129
1151
  function decompressIfNeeded(header, payload, compression, compressionCodec, maxDecompressedSize) {
1130
1152
  if ((header.flags & 4 /* PayloadCompressed */) === 0) {
@@ -1137,15 +1159,20 @@ var ZlinkStreamConnectorBundle = (() => {
1137
1159
  "Compression codec is not configured."
1138
1160
  );
1139
1161
  }
1162
+ let decompressed;
1140
1163
  try {
1141
- const decompressed = codec.decompress(payload, maxDecompressedSize);
1142
- if (decompressed.length > maxDecompressedSize) {
1143
- throw new Error("Decoded payload exceeds MaxReceivePayloadSize.");
1144
- }
1145
- return decompressed;
1164
+ decompressed = codec.decompress(payload, maxDecompressedSize);
1146
1165
  } catch (cause) {
1166
+ if (cause instanceof ZlinkStreamException) throw cause;
1147
1167
  throw connectorError("decompressionFailed" /* DecompressionFailed */, "Decompression failed.", cause);
1148
1168
  }
1169
+ if (decompressed.length > maxDecompressedSize) {
1170
+ throw connectorError(
1171
+ "frameTooLarge" /* FrameTooLarge */,
1172
+ "Decompressed payload exceeds MaxReceivePayloadSize."
1173
+ );
1174
+ }
1175
+ return decompressed;
1149
1176
  }
1150
1177
  function resolveCompressionCodec(compression, compressionCodec) {
1151
1178
  if (compression === "none" /* None */) {
@@ -1495,7 +1522,7 @@ var ZlinkStreamConnectorBundle = (() => {
1495
1522
 
1496
1523
  // packages/stream-connector/src/Runtime/ZlinkStreamConnectorOptions.ts
1497
1524
  function normalizeOptions(options, defaultTransportFactory) {
1498
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C;
1525
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G;
1499
1526
  const endpoint = options.endpoint;
1500
1527
  if (endpoint.trim().length === 0) {
1501
1528
  throw connectorError("configurationError" /* ConfigurationError */, "Endpoint must not be empty.");
@@ -1514,31 +1541,65 @@ var ZlinkStreamConnectorBundle = (() => {
1514
1541
  validatePositive((_e = options.maxReceivePayloadSize) != null ? _e : 64 * 1024, "MaxReceivePayloadSize");
1515
1542
  validateHeartbeat(options.heartbeat);
1516
1543
  validateReconnect(options.reconnect);
1544
+ if (((_f = options.heartbeat) == null ? void 0 : _f.enabled) !== void 0 && typeof options.heartbeat.enabled !== "boolean") {
1545
+ throw connectorError(
1546
+ "validationFailed" /* ValidationFailed */,
1547
+ "Heartbeat enabled must be boolean."
1548
+ );
1549
+ }
1550
+ if (((_g = options.reconnect) == null ? void 0 : _g.enabled) !== void 0 && typeof options.reconnect.enabled !== "boolean") {
1551
+ throw connectorError(
1552
+ "validationFailed" /* ValidationFailed */,
1553
+ "Reconnect enabled must be boolean."
1554
+ );
1555
+ }
1556
+ if (options.compressionCodec !== void 0 && !hasMethods(options.compressionCodec, "compress", "decompress")) {
1557
+ throw connectorError("validationFailed" /* ValidationFailed */, "Compression codec is invalid.");
1558
+ }
1559
+ if (options.codec !== void 0 && !hasMethods(options.codec, "encode", "decode")) {
1560
+ throw connectorError("validationFailed" /* ValidationFailed */, "Payload codec is invalid.");
1561
+ }
1562
+ if (options.nameResolver !== void 0 && !hasMethods(options.nameResolver, "resolve")) {
1563
+ throw connectorError("validationFailed" /* ValidationFailed */, "Packet name resolver is invalid.");
1564
+ }
1565
+ if (options.transportFactory !== void 0 && !hasMethods(options.transportFactory, "connect")) {
1566
+ throw connectorError("validationFailed" /* ValidationFailed */, "Transport factory is invalid.");
1567
+ }
1568
+ if (!Object.values(ZlinkStreamDispatchMode).includes(
1569
+ (_h = options.dispatchMode) != null ? _h : "manual" /* Manual */
1570
+ )) {
1571
+ throw connectorError("validationFailed" /* ValidationFailed */, "DispatchMode is invalid.");
1572
+ }
1573
+ if (!Object.values(ZlinkStreamCompression).includes(
1574
+ (_i = options.compression) != null ? _i : "lz4" /* Lz4 */
1575
+ )) {
1576
+ throw connectorError("validationFailed" /* ValidationFailed */, "Compression is invalid.");
1577
+ }
1517
1578
  return {
1518
1579
  endpoint,
1519
1580
  transport: inferredTransport,
1520
- connectTimeoutMs: (_f = options.connectTimeoutMs) != null ? _f : 5e3,
1521
- requestTimeoutMs: (_g = options.requestTimeoutMs) != null ? _g : 3e4,
1522
- waitTimeoutMs: (_h = options.waitTimeoutMs) != null ? _h : 5e3,
1581
+ connectTimeoutMs: (_j = options.connectTimeoutMs) != null ? _j : 5e3,
1582
+ requestTimeoutMs: (_k = options.requestTimeoutMs) != null ? _k : 3e4,
1583
+ waitTimeoutMs: (_l = options.waitTimeoutMs) != null ? _l : 5e3,
1523
1584
  heartbeat: {
1524
- enabled: (_j = (_i = options.heartbeat) == null ? void 0 : _i.enabled) != null ? _j : true,
1525
- intervalMs: (_l = (_k = options.heartbeat) == null ? void 0 : _k.intervalMs) != null ? _l : 1e3,
1526
- timeoutMs: (_n = (_m = options.heartbeat) == null ? void 0 : _m.timeoutMs) != null ? _n : 5e3
1585
+ enabled: (_n = (_m = options.heartbeat) == null ? void 0 : _m.enabled) != null ? _n : true,
1586
+ intervalMs: (_p = (_o = options.heartbeat) == null ? void 0 : _o.intervalMs) != null ? _p : 1e3,
1587
+ timeoutMs: (_r = (_q = options.heartbeat) == null ? void 0 : _q.timeoutMs) != null ? _r : 5e3
1527
1588
  },
1528
1589
  reconnect: {
1529
- enabled: (_p = (_o = options.reconnect) == null ? void 0 : _o.enabled) != null ? _p : true,
1530
- initialDelayMs: (_r = (_q = options.reconnect) == null ? void 0 : _q.initialDelayMs) != null ? _r : 250,
1531
- maxDelayMs: (_t = (_s = options.reconnect) == null ? void 0 : _s.maxDelayMs) != null ? _t : 5e3,
1532
- backoffFactor: (_v = (_u = options.reconnect) == null ? void 0 : _u.backoffFactor) != null ? _v : 2,
1533
- maxAttempts: ((_w = options.reconnect) == null ? void 0 : _w.maxAttempts) === void 0 ? 3 : options.reconnect.maxAttempts
1590
+ enabled: (_t = (_s = options.reconnect) == null ? void 0 : _s.enabled) != null ? _t : true,
1591
+ initialDelayMs: (_v = (_u = options.reconnect) == null ? void 0 : _u.initialDelayMs) != null ? _v : 250,
1592
+ maxDelayMs: (_x = (_w = options.reconnect) == null ? void 0 : _w.maxDelayMs) != null ? _x : 5e3,
1593
+ backoffFactor: (_z = (_y = options.reconnect) == null ? void 0 : _y.backoffFactor) != null ? _z : 2,
1594
+ maxAttempts: ((_A = options.reconnect) == null ? void 0 : _A.maxAttempts) === void 0 ? 3 : options.reconnect.maxAttempts
1534
1595
  },
1535
- maxSendPayloadSize: (_x = options.maxSendPayloadSize) != null ? _x : 64 * 1024,
1536
- maxReceivePayloadSize: (_y = options.maxReceivePayloadSize) != null ? _y : 64 * 1024,
1537
- dispatchMode: (_z = options.dispatchMode) != null ? _z : "manual" /* Manual */,
1538
- compression: (_A = options.compression) != null ? _A : "lz4" /* Lz4 */,
1596
+ maxSendPayloadSize: (_B = options.maxSendPayloadSize) != null ? _B : 64 * 1024,
1597
+ maxReceivePayloadSize: (_C = options.maxReceivePayloadSize) != null ? _C : 64 * 1024,
1598
+ dispatchMode: (_D = options.dispatchMode) != null ? _D : "manual" /* Manual */,
1599
+ compression: (_E = options.compression) != null ? _E : "lz4" /* Lz4 */,
1539
1600
  compressionCodec: resolveCompressionCodec2(options),
1540
- nameResolver: (_B = options.nameResolver) != null ? _B : defaultPacketNameResolver,
1541
- transportFactory: (_C = options.transportFactory) != null ? _C : defaultTransportFactory,
1601
+ nameResolver: (_F = options.nameResolver) != null ? _F : defaultPacketNameResolver,
1602
+ transportFactory: (_G = options.transportFactory) != null ? _G : defaultTransportFactory,
1542
1603
  codec: options.codec
1543
1604
  };
1544
1605
  }
@@ -1566,41 +1627,27 @@ var ZlinkStreamConnectorBundle = (() => {
1566
1627
  return options.compressionCodec;
1567
1628
  }
1568
1629
  function validatePositive(value, name) {
1569
- if (value <= 0) {
1630
+ if (!Number.isFinite(value) || value <= 0) {
1570
1631
  throw connectorError("validationFailed" /* ValidationFailed */, `${name} must be positive.`);
1571
1632
  }
1572
1633
  }
1634
+ function hasMethods(value, ...names) {
1635
+ if (value === null || typeof value !== "object") return false;
1636
+ const candidate = value;
1637
+ return names.every((name) => typeof candidate[name] === "function");
1638
+ }
1573
1639
  function validateHeartbeat(options) {
1574
- var _a, _b, _c;
1575
- const enabled = (_a = options == null ? void 0 : options.enabled) != null ? _a : true;
1576
- const intervalMs = (_b = options == null ? void 0 : options.intervalMs) != null ? _b : 1e3;
1577
- const timeoutMs = (_c = options == null ? void 0 : options.timeoutMs) != null ? _c : 5e3;
1578
- if (!enabled) {
1579
- return;
1580
- }
1640
+ var _a, _b;
1641
+ const intervalMs = (_a = options == null ? void 0 : options.intervalMs) != null ? _a : 1e3;
1642
+ const timeoutMs = (_b = options == null ? void 0 : options.timeoutMs) != null ? _b : 5e3;
1581
1643
  validatePositive(intervalMs, "Heartbeat interval");
1582
1644
  validatePositive(timeoutMs, "Heartbeat timeout");
1583
- if (timeoutMs <= intervalMs) {
1584
- throw connectorError(
1585
- "validationFailed" /* ValidationFailed */,
1586
- "Heartbeat timeout must be greater than the heartbeat interval."
1587
- );
1588
- }
1589
1645
  }
1590
1646
  function validateReconnect(options) {
1591
- var _a, _b, _c, _d;
1592
- const enabled = (_a = options == null ? void 0 : options.enabled) != null ? _a : true;
1593
- if (!enabled) {
1594
- return;
1595
- }
1596
- validatePositive((_b = options == null ? void 0 : options.initialDelayMs) != null ? _b : 250, "Reconnect InitialDelay");
1597
- validatePositive((_c = options == null ? void 0 : options.maxDelayMs) != null ? _c : 5e3, "Reconnect MaxDelay");
1598
- if (((_d = options == null ? void 0 : options.backoffFactor) != null ? _d : 2) < 1) {
1599
- throw connectorError(
1600
- "validationFailed" /* ValidationFailed */,
1601
- "Reconnect BackoffFactor must be at least 1.0."
1602
- );
1603
- }
1647
+ var _a, _b, _c;
1648
+ validatePositive((_a = options == null ? void 0 : options.initialDelayMs) != null ? _a : 250, "Reconnect InitialDelay");
1649
+ validatePositive((_b = options == null ? void 0 : options.maxDelayMs) != null ? _b : 5e3, "Reconnect MaxDelay");
1650
+ validatePositive((_c = options == null ? void 0 : options.backoffFactor) != null ? _c : 2, "Reconnect BackoffFactor");
1604
1651
  const maxAttempts = (options == null ? void 0 : options.maxAttempts) === void 0 ? 3 : options.maxAttempts;
1605
1652
  if (maxAttempts !== null && !(Number.isFinite(maxAttempts) && maxAttempts > 0)) {
1606
1653
  throw connectorError(
@@ -1616,32 +1663,33 @@ var ZlinkStreamConnectorBundle = (() => {
1616
1663
  __publicField(this, "nextRequestSeq", 1n);
1617
1664
  __publicField(this, "active", /* @__PURE__ */ new Map());
1618
1665
  }
1619
- get count() {
1620
- return this.active.size;
1621
- }
1622
1666
  create(packetName, timeoutMs) {
1667
+ if (this.nextRequestSeq > 0xffffffffffffffffn) {
1668
+ throw connectorError("sendFailed" /* SendFailed */, "Request sequence is exhausted.");
1669
+ }
1623
1670
  const requestSeq = this.nextRequestSeq++;
1624
1671
  let timeout;
1625
1672
  let resolvePending;
1626
1673
  let rejectPending;
1627
1674
  const promise = new Promise(
1628
1675
  (resolve, reject) => {
1629
- timeout = setTimeout(() => {
1630
- this.active.delete(requestSeq);
1631
- reject(
1632
- connectorError(
1633
- "requestTimeout" /* RequestTimeout */,
1634
- `Request '${packetName}' timed out.`
1635
- )
1636
- );
1637
- }, timeoutMs);
1638
1676
  resolvePending = resolve;
1639
1677
  rejectPending = (error) => reject(connectorError(error.code, error.message, error.cause));
1640
1678
  }
1641
1679
  );
1642
- this.active.set(requestSeq, {
1680
+ const tracked = {
1643
1681
  packetName,
1644
1682
  promise,
1683
+ startTimeout: () => {
1684
+ if (timeout !== void 0 || this.active.get(requestSeq) !== tracked) return;
1685
+ timeout = setTimeout(() => {
1686
+ this.active.delete(requestSeq);
1687
+ rejectPending({
1688
+ code: "requestTimeout" /* RequestTimeout */,
1689
+ message: `Request '${packetName}' timed out.`
1690
+ });
1691
+ }, timeoutMs);
1692
+ },
1645
1693
  resolve: (value) => {
1646
1694
  if (timeout !== void 0) {
1647
1695
  clearTimeout(timeout);
@@ -1659,8 +1707,9 @@ var ZlinkStreamConnectorBundle = (() => {
1659
1707
  clearTimeout(timeout);
1660
1708
  }
1661
1709
  }
1662
- });
1663
- return { requestSeq, promise };
1710
+ };
1711
+ this.active.set(requestSeq, tracked);
1712
+ return { requestSeq, promise, startTimeout: tracked.startTimeout };
1664
1713
  }
1665
1714
  /* stream connector spec §5.2: a pending request is matched by request_seq alone. */
1666
1715
  resolve(requestSeq, value, metadata) {
@@ -1668,8 +1717,9 @@ var ZlinkStreamConnectorBundle = (() => {
1668
1717
  if (pending === void 0) {
1669
1718
  return false;
1670
1719
  }
1720
+ const payload = value();
1671
1721
  this.active.delete(requestSeq);
1672
- pending.resolve({ name: pending.packetName, metadata, payload: value });
1722
+ pending.resolve({ name: pending.packetName, metadata, payload });
1673
1723
  return true;
1674
1724
  }
1675
1725
  reject(requestSeq, error) {
@@ -1697,7 +1747,164 @@ var ZlinkStreamConnectorBundle = (() => {
1697
1747
  }
1698
1748
  };
1699
1749
 
1750
+ // packages/stream-connector/src/Runtime/ZlinkStreamActors.ts
1751
+ var ACTOR_BOUND = "$zlink.actor.bound";
1752
+ var ACTOR_UNBOUND = "$zlink.actor.unbound";
1753
+ var zlinkStreamActorBinding = Symbol("zlink.stream.actorBinding");
1754
+ var DefaultZlinkStreamActor = class {
1755
+ constructor(connector, actorId, slot) {
1756
+ this.connector = connector;
1757
+ this.actorId = actorId;
1758
+ this.slot = slot;
1759
+ __publicField(this, "bound", true);
1760
+ }
1761
+ get isBound() {
1762
+ return this.bound;
1763
+ }
1764
+ send(payload, messageType) {
1765
+ this.ensureBound();
1766
+ return this.connector.sendForActor(this, payload, messageType);
1767
+ }
1768
+ request(payload, messageType) {
1769
+ this.ensureBound();
1770
+ return this.connector.requestForActor(this, payload, messageType);
1771
+ }
1772
+ on(nameOrType, handler, messageType) {
1773
+ return this.connector.onActorMessage(this, nameOrType, handler, messageType);
1774
+ }
1775
+ ensureBound() {
1776
+ if (!this.bound) {
1777
+ throw connectorError(
1778
+ "validationFailed" /* ValidationFailed */,
1779
+ `Actor '${this.actorId}' is no longer bound.`
1780
+ );
1781
+ }
1782
+ }
1783
+ close() {
1784
+ this.bound = false;
1785
+ }
1786
+ };
1787
+ var ZlinkStreamActors = class {
1788
+ constructor(connector, receivedMessages, events) {
1789
+ this.connector = connector;
1790
+ this.receivedMessages = receivedMessages;
1791
+ this.events = events;
1792
+ __publicField(this, "bySlot", /* @__PURE__ */ new Map());
1793
+ __publicField(this, "byId", /* @__PURE__ */ new Map());
1794
+ __publicField(this, "issued", []);
1795
+ __publicField(this, "boundHandlers", /* @__PURE__ */ new Set());
1796
+ __publicField(this, "unboundHandlers", /* @__PURE__ */ new Set());
1797
+ }
1798
+ get snapshot() {
1799
+ return Object.freeze(Array.from(this.bySlot.values()));
1800
+ }
1801
+ find(actorId) {
1802
+ return this.byId.get(actorId);
1803
+ }
1804
+ onBound(handler) {
1805
+ this.boundHandlers.add(handler);
1806
+ return subscription(() => this.boundHandlers.delete(handler));
1807
+ }
1808
+ onUnbound(handler) {
1809
+ this.unboundHandlers.add(handler);
1810
+ return subscription(() => this.unboundHandlers.delete(handler));
1811
+ }
1812
+ processControl(name, payload, signal) {
1813
+ if (name === ACTOR_BOUND) {
1814
+ this.bind(payload, signal);
1815
+ return true;
1816
+ }
1817
+ if (name === ACTOR_UNBOUND) {
1818
+ this.unbind(payload, signal);
1819
+ return true;
1820
+ }
1821
+ return false;
1822
+ }
1823
+ resolve(slot) {
1824
+ const actor = this.bySlot.get(slot);
1825
+ if (actor === void 0) {
1826
+ throw connectorError(
1827
+ "frameDecodeFailed" /* FrameDecodeFailed */,
1828
+ `Actor slot '${slot}' is not bound.`
1829
+ );
1830
+ }
1831
+ return actor;
1832
+ }
1833
+ closeAll(signal) {
1834
+ for (const actor of this.issued) {
1835
+ if (!actor.isBound) continue;
1836
+ this.bySlot.delete(actor.slot);
1837
+ this.byId.delete(actor.actorId);
1838
+ actor.close();
1839
+ this.queue(this.unboundHandlers, actor, signal);
1840
+ }
1841
+ this.issued.length = 0;
1842
+ }
1843
+ bind(payload, signal) {
1844
+ if (payload.length < 5 || payload[0] !== 1) {
1845
+ throw invalidControl("Actor bound payload is invalid.");
1846
+ }
1847
+ const slot = payload[1] << 8 | payload[2];
1848
+ const idLength = payload[3];
1849
+ if (slot === 0 || idLength === 0 || payload.length !== 4 + idLength) {
1850
+ throw invalidControl("Actor bound payload is invalid.");
1851
+ }
1852
+ let actorId;
1853
+ try {
1854
+ actorId = new TextDecoder("utf-8", { fatal: true }).decode(payload.subarray(4));
1855
+ } catch (cause) {
1856
+ throw connectorError(
1857
+ "frameDecodeFailed" /* FrameDecodeFailed */,
1858
+ "Actor id is not valid UTF-8.",
1859
+ cause
1860
+ );
1861
+ }
1862
+ if (actorId.length === 0 || this.bySlot.has(slot) || this.byId.has(actorId)) {
1863
+ throw invalidControl("Actor bound identity is already in use.");
1864
+ }
1865
+ const actor = new DefaultZlinkStreamActor(this.connector, actorId, slot);
1866
+ this.bySlot.set(slot, actor);
1867
+ this.byId.set(actorId, actor);
1868
+ this.issued.push(actor);
1869
+ this.queue(this.boundHandlers, actor, signal);
1870
+ }
1871
+ unbind(payload, signal) {
1872
+ if (payload.length !== 3 || payload[0] !== 1) {
1873
+ throw invalidControl("Actor unbound payload is invalid.");
1874
+ }
1875
+ const slot = payload[1] << 8 | payload[2];
1876
+ const actor = this.bySlot.get(slot);
1877
+ if (slot === 0 || actor === void 0) {
1878
+ throw invalidControl(`Actor slot '${slot}' is not bound.`);
1879
+ }
1880
+ this.bySlot.delete(slot);
1881
+ this.byId.delete(actor.actorId);
1882
+ actor.close();
1883
+ this.queue(this.unboundHandlers, actor, signal);
1884
+ }
1885
+ queue(handlers, actor, signal) {
1886
+ this.receivedMessages.enqueueCallback(
1887
+ () => {
1888
+ for (const handler of Array.from(handlers)) {
1889
+ this.events.runUserCallback(
1890
+ () => handler(actor, signal),
1891
+ "Actor lifecycle handler failed.",
1892
+ signal
1893
+ );
1894
+ }
1895
+ },
1896
+ () => handlers.size
1897
+ );
1898
+ }
1899
+ };
1900
+ function invalidControl(message) {
1901
+ return connectorError("frameDecodeFailed" /* FrameDecodeFailed */, message);
1902
+ }
1903
+
1700
1904
  // packages/stream-connector/src/Runtime/ZlinkStreamReceivedMessages.ts
1905
+ function receives(registration, message) {
1906
+ return registration.actor === void 0 || registration.actor === message[zlinkStreamActorBinding];
1907
+ }
1701
1908
  var ZlinkStreamReceivedMessages = class {
1702
1909
  /**
1703
1910
  * @param deliverOnArrival `Immediate` runs registered handlers on the receive
@@ -1714,10 +1921,10 @@ var ZlinkStreamConnectorBundle = (() => {
1714
1921
  // queue is not a simple FIFO. Tombstones let us remove a deliverable entry
1715
1922
  // without shifting every later message on the hot receive path.
1716
1923
  __publicField(this, "queue", []);
1924
+ __publicField(this, "deliverable", []);
1717
1925
  __publicField(this, "queueHead", 0);
1718
1926
  __publicField(this, "queuedCount", 0);
1719
- __publicField(this, "drainTask");
1720
- // True for as long as `drain` is on the stack, handler awaits included. It
1927
+ // True for as long as `pump` is on the stack. It
1721
1928
  // marks the execution context a registered handler runs in, so a `dispatch`
1722
1929
  // made from inside a handler is recognised as re-entry rather than a fresh
1723
1930
  // pump. It is not a lock: a single event loop admits no second thread, and
@@ -1728,19 +1935,32 @@ var ZlinkStreamConnectorBundle = (() => {
1728
1935
  // so consuming does not lower it and the dispatch mode does not change it.
1729
1936
  __publicField(this, "receivedCounts", /* @__PURE__ */ new Map());
1730
1937
  }
1731
- on(name, handler) {
1938
+ /**
1939
+ * @param actor The Actor handle that registers the handler, if any. Its
1940
+ * handler receives only that Actor's packets.
1941
+ */
1942
+ on(name, handler, actor) {
1732
1943
  validateName(name);
1733
1944
  let set = this.handlers.get(name);
1734
1945
  if (set === void 0) {
1735
1946
  set = /* @__PURE__ */ new Set();
1736
1947
  this.handlers.set(name, set);
1737
1948
  }
1738
- set.add(handler);
1739
- if (this.deliverOnArrival && this.hasQueuedMessage(name)) {
1740
- queueMicrotask(() => this.scheduleDrain());
1949
+ const registration = { handle: handler, actor };
1950
+ set.add(registration);
1951
+ let newlyDeliverable = false;
1952
+ for (let index = this.queueHead; index < this.queue.length; index += 1) {
1953
+ const queued = this.queue[index];
1954
+ if ((queued == null ? void 0 : queued.kind) === "message" && queued.indexed !== true && queued.message.name === name && receives(registration, queued.message)) {
1955
+ this.indexDeliverable(index);
1956
+ newlyDeliverable = true;
1957
+ }
1958
+ }
1959
+ if (this.deliverOnArrival && newlyDeliverable) {
1960
+ queueMicrotask(() => this.pump());
1741
1961
  }
1742
1962
  return subscription(() => {
1743
- set.delete(handler);
1963
+ set.delete(registration);
1744
1964
  if (set.size === 0 && this.handlers.get(name) === set) {
1745
1965
  this.handlers.delete(name);
1746
1966
  }
@@ -1812,6 +2032,11 @@ var ZlinkStreamConnectorBundle = (() => {
1812
2032
  this.queue.push(...submittedCallbacks);
1813
2033
  this.queueHead = 0;
1814
2034
  this.queuedCount = submittedCallbacks.length;
2035
+ this.deliverable.length = 0;
2036
+ for (let index = 0; index < this.queue.length; index += 1) {
2037
+ this.queue[index].indexed = false;
2038
+ this.indexDeliverable(index);
2039
+ }
1815
2040
  }
1816
2041
  /**
1817
2042
  * Releases every registered wait surface because the connection it was
@@ -1843,60 +2068,38 @@ var ZlinkStreamConnectorBundle = (() => {
1843
2068
  }
1844
2069
  this.queue.push({ kind: "message", message, signal });
1845
2070
  this.queuedCount += 1;
2071
+ this.indexDeliverable(this.queue.length - 1);
1846
2072
  if (this.deliverOnArrival) {
1847
- this.scheduleDrain();
2073
+ this.pump();
1848
2074
  }
1849
2075
  }
1850
- enqueueCallback(callback) {
1851
- this.queue.push({ kind: "callback", callback });
2076
+ /**
2077
+ * @param callbacks The number of handler calls `callback` makes when it runs
2078
+ * with the handlers registered at that moment; a callback that runs a set
2079
+ * of handlers reads the set's size. One callback by default.
2080
+ */
2081
+ enqueueCallback(callback, callbacks = () => 1) {
2082
+ this.queue.push({ kind: "callback", callback, callbacks });
1852
2083
  this.queuedCount += 1;
2084
+ this.indexDeliverable(this.queue.length - 1);
1853
2085
  if (this.deliverOnArrival) {
1854
- this.scheduleDrain();
2086
+ this.pump();
1855
2087
  }
1856
2088
  }
1857
2089
  /**
1858
2090
  * Runs the registered handlers the receive path left queued. `Manual` calls
1859
- * this from `dispatch`; `Immediate` has already drained on arrival.
2091
+ * this from `dispatch`; `Immediate` drains on arrival.
1860
2092
  *
1861
- * A handler that calls `dispatch` arrives back here from inside the drain it
1862
- * was started by. `scheduleDrain` would find `drainTask` already set and
1863
- * return, and the await below would then be the drain waiting on itself —
1864
- * a deadlock with neither timeout nor error. The drain loop already takes
1865
- * every message a handler exists for, so there is nothing a second drain
1866
- * would deliver and returning is the whole of the correct behaviour.
2093
+ * The drain is synchronous: the connector does not wait for a handler
2094
+ * (spec stream-connector 32 §7), so nothing inside it awaits. A handler that
2095
+ * calls `dispatch` arrives back here from inside the drain it was started by;
2096
+ * the running loop already takes every deliverable entry, so returning is the
2097
+ * whole of the correct behaviour.
1867
2098
  */
1868
- async pump() {
2099
+ pump() {
1869
2100
  if (this.draining) {
1870
2101
  return;
1871
2102
  }
1872
- this.scheduleDrain();
1873
- await this.drainTask;
1874
- }
1875
- offerQueued(name, registration) {
1876
- for (let index = this.queueHead; index < this.queue.length; index += 1) {
1877
- const queued = this.queue[index];
1878
- if (queued === void 0 || queued.kind !== "message" || queued.message.name !== name) {
1879
- continue;
1880
- }
1881
- if (!registration.consume(queued.message)) {
1882
- continue;
1883
- }
1884
- this.removeAt(index);
1885
- return;
1886
- }
1887
- }
1888
- scheduleDrain() {
1889
- if (this.drainTask !== void 0) {
1890
- return;
1891
- }
1892
- this.drainTask = this.drain().finally(() => {
1893
- this.drainTask = void 0;
1894
- if (this.deliverOnArrival && this.findDeliverableIndex() >= 0) {
1895
- this.scheduleDrain();
1896
- }
1897
- });
1898
- }
1899
- async drain() {
1900
2103
  this.draining = true;
1901
2104
  try {
1902
2105
  for (let index = this.findDeliverableIndex(); index >= 0; index = this.findDeliverableIndex()) {
@@ -1904,52 +2107,113 @@ var ZlinkStreamConnectorBundle = (() => {
1904
2107
  if (queued === void 0) continue;
1905
2108
  this.removeAt(index);
1906
2109
  if (queued.kind === "callback") {
1907
- await queued.callback();
2110
+ this.events.runUserCallback(queued.callback, "Connector callback failed.");
1908
2111
  continue;
1909
2112
  }
1910
2113
  const { message, signal } = queued;
1911
- const handlers = Array.from(this.handlers.get(message.name));
2114
+ const handlers = this.receiversOf(message);
1912
2115
  for (const handler of handlers) {
1913
- try {
1914
- await handler(message, signal);
1915
- } catch (cause) {
1916
- await this.events.publishError(
1917
- {
1918
- code: "userCallbackFailed" /* UserCallbackFailed */,
1919
- message: "Typed message handler failed.",
1920
- cause
1921
- },
1922
- signal
1923
- );
1924
- }
2116
+ this.events.runUserCallback(
2117
+ () => handler.handle(message, signal),
2118
+ "Typed message handler failed.",
2119
+ signal
2120
+ );
1925
2121
  }
1926
2122
  }
1927
2123
  } finally {
1928
2124
  this.draining = false;
1929
2125
  }
1930
2126
  }
1931
- removeAt(index) {
1932
- this.queue[index] = void 0;
1933
- this.queuedCount -= 1;
1934
- this.advanceHead();
1935
- this.compactQueue();
1936
- }
1937
- findDeliverableIndex() {
1938
- var _a, _b;
2127
+ /**
2128
+ * True while a registered callback runs from {@link pump}: the execution
2129
+ * context spec stream-connector 32 §7 calls "inside a handler".
2130
+ */
2131
+ get dispatching() {
2132
+ return this.draining;
2133
+ }
2134
+ get pendingCallbacks() {
2135
+ let count = 0;
2136
+ for (let index = this.queueHead; index < this.queue.length; index += 1) {
2137
+ const queued = this.queue[index];
2138
+ if ((queued == null ? void 0 : queued.kind) === "callback" && queued.callbacks() > 0) count += 1;
2139
+ if ((queued == null ? void 0 : queued.kind) === "message" && this.receiversOf(queued.message).length > 0) count += 1;
2140
+ }
2141
+ return count;
2142
+ }
2143
+ offerQueued(name, registration) {
1939
2144
  for (let index = this.queueHead; index < this.queue.length; index += 1) {
1940
2145
  const queued = this.queue[index];
1941
- if (queued !== void 0 && (queued.kind === "callback" || ((_b = (_a = this.handlers.get(queued.message.name)) == null ? void 0 : _a.size) != null ? _b : 0) > 0)) {
2146
+ if (queued === void 0 || queued.kind !== "message" || queued.message.name !== name) {
2147
+ continue;
2148
+ }
2149
+ if (!registration.consume(queued.message)) {
2150
+ continue;
2151
+ }
2152
+ this.removeAt(index);
2153
+ return;
2154
+ }
2155
+ }
2156
+ removeAt(index) {
2157
+ this.queue[index] = void 0;
2158
+ this.queuedCount -= 1;
2159
+ this.advanceHead();
2160
+ this.compactQueue();
2161
+ }
2162
+ findDeliverableIndex() {
2163
+ while (this.deliverable.length > 0) {
2164
+ const index = this.deliverable[0];
2165
+ const queued = this.queue[index];
2166
+ if (this.isDeliverable(queued)) {
1942
2167
  return index;
1943
2168
  }
2169
+ if (queued !== void 0) queued.indexed = false;
2170
+ this.removeDeliverable();
1944
2171
  }
1945
2172
  return -1;
1946
2173
  }
1947
- hasQueuedMessage(name) {
1948
- for (let index = this.queueHead; index < this.queue.length; index += 1) {
1949
- const queued = this.queue[index];
1950
- if ((queued == null ? void 0 : queued.kind) === "message" && queued.message.name === name) return true;
2174
+ indexDeliverable(index) {
2175
+ const queued = this.queue[index];
2176
+ if (queued === void 0 || !this.isDeliverable(queued) || queued.indexed === true) return;
2177
+ queued.indexed = true;
2178
+ let position = this.deliverable.length;
2179
+ this.deliverable.push(index);
2180
+ while (position > 0) {
2181
+ const parent = Math.floor((position - 1) / 2);
2182
+ if (this.deliverable[parent] <= index) break;
2183
+ this.deliverable[position] = this.deliverable[parent];
2184
+ position = parent;
1951
2185
  }
1952
- return false;
2186
+ this.deliverable[position] = index;
2187
+ }
2188
+ isDeliverable(queued) {
2189
+ return (queued == null ? void 0 : queued.kind) === "callback" || (queued == null ? void 0 : queued.kind) === "message" && this.receiversOf(queued.message).length > 0;
2190
+ }
2191
+ /**
2192
+ * Spec stream-connector 32 §7 and §10: the handlers that receive `message`
2193
+ * are the connector handlers for its name and the handle handlers of the
2194
+ * Actor it carries. A packet no handler receives stays in the queue for the
2195
+ * wait surfaces. This is the one place that decides it.
2196
+ */
2197
+ receiversOf(message) {
2198
+ const set = this.handlers.get(message.name);
2199
+ if (set === void 0) return [];
2200
+ return Array.from(set).filter((registration) => receives(registration, message));
2201
+ }
2202
+ removeDeliverable() {
2203
+ if (this.deliverable.length === 0) return;
2204
+ const last = this.deliverable.pop();
2205
+ if (this.deliverable.length === 0) return;
2206
+ let position = 0;
2207
+ for (; ; ) {
2208
+ const left = position * 2 + 1;
2209
+ if (left >= this.deliverable.length) break;
2210
+ const right = left + 1;
2211
+ const child = right < this.deliverable.length && this.deliverable[right] < this.deliverable[left] ? right : left;
2212
+ if (this.deliverable[child] >= last) break;
2213
+ this.deliverable[position] = this.deliverable[child];
2214
+ position = child;
2215
+ }
2216
+ this.deliverable[position] = last;
1953
2217
  }
1954
2218
  advanceHead() {
1955
2219
  while (this.queueHead < this.queue.length && this.queue[this.queueHead] === void 0) {
@@ -1960,22 +2224,99 @@ var ZlinkStreamConnectorBundle = (() => {
1960
2224
  if (this.queuedCount === 0) {
1961
2225
  this.queue.length = 0;
1962
2226
  this.queueHead = 0;
2227
+ this.deliverable.length = 0;
1963
2228
  return;
1964
2229
  }
1965
2230
  if (this.queueHead >= 1024 && this.queueHead * 2 >= this.queue.length) {
1966
2231
  this.queue.splice(0, this.queueHead);
1967
2232
  this.queueHead = 0;
2233
+ this.deliverable.length = 0;
2234
+ for (let index = 0; index < this.queue.length; index += 1) {
2235
+ const queued = this.queue[index];
2236
+ if (queued === void 0) continue;
2237
+ queued.indexed = false;
2238
+ this.indexDeliverable(index);
2239
+ }
1968
2240
  }
1969
2241
  }
1970
2242
  };
1971
2243
 
1972
2244
  // packages/stream-connector/src/Runtime/ZlinkStreamFrameSender.ts
2245
+ var ZlinkStreamConnectionWriteQueue = class {
2246
+ /**
2247
+ * @param writeFailed Ends the connection whose transport write failed (spec
2248
+ * stream-connector 32 §9: a transport write failure ends the connection as
2249
+ * `TransportError`).
2250
+ */
2251
+ constructor(connection, writeFailed) {
2252
+ this.connection = connection;
2253
+ this.writeFailed = writeFailed;
2254
+ __publicField(this, "queue", /* @__PURE__ */ new Set());
2255
+ __publicField(this, "active");
2256
+ }
2257
+ enqueue(operation) {
2258
+ this.queue.add(operation);
2259
+ this.advance();
2260
+ }
2261
+ cancel(operation, error) {
2262
+ if (this.queue.has(operation) || this.active === operation) operation.fail(error);
2263
+ }
2264
+ /**
2265
+ * Spec stream-connector 32 §7: when the connection ends, frames that have not
2266
+ * reached the transport are not written and the frame being written is not
2267
+ * waited for. The operations of both fail with `error`.
2268
+ */
2269
+ failAll(error) {
2270
+ const operations = this.active === void 0 ? [...this.queue] : [this.active, ...this.queue];
2271
+ this.queue.clear();
2272
+ this.active = void 0;
2273
+ for (const operation of operations) operation.fail(error);
2274
+ }
2275
+ advance() {
2276
+ if (this.active !== void 0) return;
2277
+ const next = this.queue.values().next().value;
2278
+ if (next === void 0) return;
2279
+ this.queue.delete(next);
2280
+ this.active = next;
2281
+ let write;
2282
+ try {
2283
+ write = Promise.resolve(this.connection.write(next.frame));
2284
+ } catch (error) {
2285
+ write = Promise.reject(error);
2286
+ }
2287
+ void write.then(
2288
+ () => next.complete(),
2289
+ (cause) => this.failWrite(next, cause)
2290
+ ).finally(() => {
2291
+ if (this.active !== next) return;
2292
+ this.active = void 0;
2293
+ this.advance();
2294
+ });
2295
+ }
2296
+ /**
2297
+ * Spec stream-connector 32 §9: a transport write failure is `SendFailed` for
2298
+ * the operation of that write, and it ends the connection. The ending fails
2299
+ * every other operation of the connection with `Disconnected`. A write that
2300
+ * fails after the connection already ended belongs to that ending, which has
2301
+ * already failed its operation with `Disconnected`.
2302
+ */
2303
+ failWrite(operation, cause) {
2304
+ if (this.active !== operation) return;
2305
+ const error = {
2306
+ code: "sendFailed" /* SendFailed */,
2307
+ message: `Frame write failed: ${cause instanceof Error ? cause.message : String(cause)}`,
2308
+ cause
2309
+ };
2310
+ operation.fail(new ZlinkStreamException(error));
2311
+ this.writeFailed(error);
2312
+ }
2313
+ };
1973
2314
  var ZlinkStreamFrameSender = class {
1974
2315
  constructor(protocol) {
1975
2316
  this.protocol = protocol;
1976
- __publicField(this, "pendingWrites", /* @__PURE__ */ new Set());
2317
+ __publicField(this, "queues", /* @__PURE__ */ new Map());
1977
2318
  }
1978
- async send(connection, kind, name, payload, metadata, compress, requestSeq, signal, correlationId, actorSlot) {
2319
+ async send(connection, kind, name, payload, metadata, compress, requestSeq, signal, correlationId, actorSlot, expiry, onAccepted) {
1979
2320
  throwIfAborted(signal);
1980
2321
  await this.write(
1981
2322
  connection,
@@ -1989,26 +2330,78 @@ var ZlinkStreamConnectorBundle = (() => {
1989
2330
  correlationId,
1990
2331
  actorSlot
1991
2332
  ),
1992
- signal
2333
+ signal,
2334
+ expiry,
2335
+ onAccepted
1993
2336
  );
1994
2337
  }
1995
- async sendControl(connection, name, signal) {
1996
- await this.write(connection, this.protocol.encodeControl(name), signal);
2338
+ /**
2339
+ * Writes a control frame (heartbeat ping or pong). A control frame belongs to
2340
+ * no operation: its write failure ends the connection through the write
2341
+ * queue like any other write (spec stream-connector 32 §9), and a connection
2342
+ * that has already ended does not write it. The promise settles when the
2343
+ * write has ended either way.
2344
+ */
2345
+ sendControl(connection, name) {
2346
+ const queue = this.queues.get(connection);
2347
+ if (queue === void 0) return Promise.resolve();
2348
+ const frame = this.protocol.encodeControl(name);
2349
+ return new Promise((resolve) => {
2350
+ queue.enqueue({ frame, complete: resolve, fail: () => resolve() });
2351
+ });
1997
2352
  }
1998
- async drain(signal) {
1999
- while (this.pendingWrites.size > 0) {
2000
- throwIfAborted(signal);
2001
- await Promise.allSettled(Array.from(this.pendingWrites));
2002
- }
2353
+ /**
2354
+ * Gives a connection that has just been established its own write queue.
2355
+ * `writeFailed` ends that connection when one of its transport writes fails.
2356
+ */
2357
+ open(connection, writeFailed) {
2358
+ this.queues.set(connection, new ZlinkStreamConnectionWriteQueue(connection, writeFailed));
2003
2359
  }
2004
- async write(connection, frame, signal) {
2005
- const write = connection.write(frame, signal);
2006
- this.pendingWrites.add(write);
2007
- try {
2008
- await write;
2009
- } finally {
2010
- this.pendingWrites.delete(write);
2360
+ /**
2361
+ * Ends the write queue of a connection that has ended, by close and by
2362
+ * transport loss alike. Every operation still in it fails with `error`.
2363
+ */
2364
+ failUnwritten(connection, error) {
2365
+ const queue = this.queues.get(connection);
2366
+ if (queue === void 0) return;
2367
+ this.queues.delete(connection);
2368
+ queue.failAll(error);
2369
+ }
2370
+ write(connection, frame, signal, expiry, onAccepted) {
2371
+ throwIfAborted(signal);
2372
+ const queue = this.queues.get(connection);
2373
+ if (queue === void 0) {
2374
+ throw connectorError("disconnected" /* Disconnected */, "Connector is not connected.");
2011
2375
  }
2376
+ let operation;
2377
+ let settled = false;
2378
+ const promise = new Promise((resolve, reject) => {
2379
+ const cleanup = () => signal == null ? void 0 : signal.removeEventListener("abort", onAbort);
2380
+ const onAbort = () => queue.cancel(
2381
+ operation,
2382
+ connectorError("disconnected" /* Disconnected */, "Operation canceled.")
2383
+ );
2384
+ operation = {
2385
+ frame,
2386
+ complete: () => {
2387
+ if (settled) return;
2388
+ settled = true;
2389
+ cleanup();
2390
+ resolve();
2391
+ },
2392
+ fail: (error) => {
2393
+ if (settled) return;
2394
+ settled = true;
2395
+ cleanup();
2396
+ reject(error);
2397
+ }
2398
+ };
2399
+ signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
2400
+ onAccepted == null ? void 0 : onAccepted();
2401
+ expiry == null ? void 0 : expiry.then(void 0, (error) => queue.cancel(operation, error));
2402
+ queue.enqueue(operation);
2403
+ });
2404
+ return promise;
2012
2405
  }
2013
2406
  };
2014
2407
 
@@ -2034,177 +2427,16 @@ var ZlinkStreamConnectorBundle = (() => {
2034
2427
  return { closeReason, diagnostic };
2035
2428
  }
2036
2429
 
2037
- // packages/stream-connector/src/Runtime/ZlinkStreamActors.ts
2038
- var ACTOR_BOUND = "$zlink.actor.bound";
2039
- var ACTOR_UNBOUND = "$zlink.actor.unbound";
2040
- var zlinkStreamActorBinding = Symbol("zlink.stream.actorBinding");
2041
- var DefaultZlinkStreamActor = class {
2042
- constructor(connector, actorId, slot) {
2043
- this.connector = connector;
2044
- this.actorId = actorId;
2045
- this.slot = slot;
2046
- __publicField(this, "bound", true);
2047
- }
2048
- get isBound() {
2049
- return this.bound;
2050
- }
2051
- send(payload, messageType) {
2052
- this.ensureBound();
2053
- return this.connector.sendForActor(this, payload, messageType);
2054
- }
2055
- request(payload, messageType) {
2056
- this.ensureBound();
2057
- return this.connector.requestForActor(this, payload, messageType);
2058
- }
2059
- on(nameOrType, handler, messageType) {
2060
- return this.connector.onActorMessage(this, nameOrType, handler, messageType);
2061
- }
2062
- ensureBound() {
2063
- if (!this.bound) {
2064
- throw connectorError(
2065
- "validationFailed" /* ValidationFailed */,
2066
- `Actor '${this.actorId}' is no longer bound.`
2067
- );
2068
- }
2069
- }
2070
- close() {
2071
- this.bound = false;
2072
- }
2073
- };
2074
- var ZlinkStreamActors = class {
2075
- constructor(connector, receivedMessages, events) {
2076
- this.connector = connector;
2077
- this.receivedMessages = receivedMessages;
2078
- this.events = events;
2079
- __publicField(this, "bySlot", /* @__PURE__ */ new Map());
2080
- __publicField(this, "byId", /* @__PURE__ */ new Map());
2081
- __publicField(this, "issued", []);
2082
- __publicField(this, "boundHandlers", /* @__PURE__ */ new Set());
2083
- __publicField(this, "unboundHandlers", /* @__PURE__ */ new Set());
2084
- }
2085
- get snapshot() {
2086
- return Object.freeze(Array.from(this.bySlot.values()));
2087
- }
2088
- find(actorId) {
2089
- return this.byId.get(actorId);
2090
- }
2091
- onBound(handler) {
2092
- this.boundHandlers.add(handler);
2093
- return subscription(() => this.boundHandlers.delete(handler));
2094
- }
2095
- onUnbound(handler) {
2096
- this.unboundHandlers.add(handler);
2097
- return subscription(() => this.unboundHandlers.delete(handler));
2098
- }
2099
- processControl(name, payload, signal) {
2100
- if (name === ACTOR_BOUND) {
2101
- this.bind(payload, signal);
2102
- return true;
2103
- }
2104
- if (name === ACTOR_UNBOUND) {
2105
- this.unbind(payload, signal);
2106
- return true;
2107
- }
2108
- return false;
2109
- }
2110
- resolve(slot) {
2111
- const actor = this.bySlot.get(slot);
2112
- if (actor === void 0) {
2113
- throw connectorError(
2114
- "frameDecodeFailed" /* FrameDecodeFailed */,
2115
- `Actor slot '${slot}' is not bound.`
2116
- );
2117
- }
2118
- return actor;
2119
- }
2120
- closeAll(signal) {
2121
- for (const actor of this.issued) {
2122
- if (!actor.isBound) continue;
2123
- this.bySlot.delete(actor.slot);
2124
- this.byId.delete(actor.actorId);
2125
- actor.close();
2126
- this.queue(this.unboundHandlers, actor, signal);
2127
- }
2128
- this.issued.length = 0;
2129
- }
2130
- bind(payload, signal) {
2131
- if (payload.length < 5 || payload[0] !== 1) {
2132
- throw invalidControl("Actor bound payload is invalid.");
2133
- }
2134
- const slot = payload[1] << 8 | payload[2];
2135
- const idLength = payload[3];
2136
- if (slot === 0 || idLength === 0 || payload.length !== 4 + idLength) {
2137
- throw invalidControl("Actor bound payload is invalid.");
2138
- }
2139
- let actorId;
2140
- try {
2141
- actorId = new TextDecoder("utf-8", { fatal: true }).decode(payload.subarray(4));
2142
- } catch (cause) {
2143
- throw connectorError(
2144
- "frameDecodeFailed" /* FrameDecodeFailed */,
2145
- "Actor id is not valid UTF-8.",
2146
- cause
2147
- );
2148
- }
2149
- if (actorId.length === 0 || this.bySlot.has(slot) || this.byId.has(actorId)) {
2150
- throw invalidControl("Actor bound identity is already in use.");
2151
- }
2152
- const actor = new DefaultZlinkStreamActor(this.connector, actorId, slot);
2153
- this.bySlot.set(slot, actor);
2154
- this.byId.set(actorId, actor);
2155
- this.issued.push(actor);
2156
- this.queue(this.boundHandlers, actor, signal);
2157
- }
2158
- unbind(payload, signal) {
2159
- if (payload.length !== 3 || payload[0] !== 1) {
2160
- throw invalidControl("Actor unbound payload is invalid.");
2161
- }
2162
- const slot = payload[1] << 8 | payload[2];
2163
- const actor = this.bySlot.get(slot);
2164
- if (slot === 0 || actor === void 0) {
2165
- throw invalidControl(`Actor slot '${slot}' is not bound.`);
2166
- }
2167
- this.bySlot.delete(slot);
2168
- this.byId.delete(actor.actorId);
2169
- actor.close();
2170
- this.queue(this.unboundHandlers, actor, signal);
2171
- }
2172
- queue(handlers, actor, signal) {
2173
- this.receivedMessages.enqueueCallback(() => {
2174
- for (const handler of Array.from(handlers)) {
2175
- this.invoke(handler, actor, signal);
2176
- }
2177
- });
2178
- }
2179
- invoke(handler, actor, signal) {
2180
- try {
2181
- Promise.resolve(handler(actor, signal)).catch((cause) => {
2182
- void this.events.publishError(
2183
- {
2184
- code: "userCallbackFailed" /* UserCallbackFailed */,
2185
- message: "Actor lifecycle handler failed.",
2186
- cause
2187
- },
2188
- signal
2189
- );
2190
- });
2191
- } catch (cause) {
2192
- void this.events.publishError(
2193
- {
2194
- code: "userCallbackFailed" /* UserCallbackFailed */,
2195
- message: "Actor lifecycle handler failed.",
2196
- cause
2197
- },
2198
- signal
2199
- );
2200
- }
2430
+ // packages/stream-connector/src/Runtime/ZlinkStreamReceiveDispatcher.ts
2431
+ function closeReasonFor(error) {
2432
+ return error.code === "frameDecodeFailed" /* FrameDecodeFailed */ || error.code === "frameTooLarge" /* FrameTooLarge */ ? "ProtocolError" : "TransportError";
2433
+ }
2434
+ var ZlinkStreamConnectionEnd = class extends Error {
2435
+ constructor(error) {
2436
+ super(error.message);
2437
+ this.error = error;
2201
2438
  }
2202
2439
  };
2203
- function invalidControl(message) {
2204
- return connectorError("frameDecodeFailed" /* FrameDecodeFailed */, message);
2205
- }
2206
-
2207
- // packages/stream-connector/src/Runtime/ZlinkStreamReceiveDispatcher.ts
2208
2440
  var ZlinkStreamReceiveDispatcher = class {
2209
2441
  constructor(protocol, pendingRequests, receivedMessages, frameSender, events, actors, serverClosing) {
2210
2442
  this.protocol = protocol;
@@ -2224,15 +2456,20 @@ var ZlinkStreamConnectorBundle = (() => {
2224
2456
  * the recheck the rest of a dead connection's batch lands on the new
2225
2457
  * connection's state, and the §10 arrival counts, which start at 0 when a
2226
2458
  * connection is established, take the stale frames on top.
2227
- * @param connectionForSend Resolves the connection a reply belongs on at the
2228
- * moment it is written. The captured `connection` is the one the batch was
2229
- * read from, which a reconnect may already have replaced.
2230
2459
  */
2231
- async readAndDispatch(connection, signal, isCurrent, connectionForSend) {
2460
+ async readAndDispatch(connection, signal, isCurrent) {
2232
2461
  if ((connection == null ? void 0 : connection.read) === void 0) {
2233
2462
  return { available: false, inbound: false };
2234
2463
  }
2235
- const frameBytes = await connection.read(signal);
2464
+ let frameBytes;
2465
+ try {
2466
+ frameBytes = await connection.read(signal);
2467
+ } catch (cause) {
2468
+ if ((signal == null ? void 0 : signal.aborted) === true) throw cause;
2469
+ throw new ZlinkStreamConnectionEnd(
2470
+ toStreamError(cause, "disconnected" /* Disconnected */, "Transport read failed.")
2471
+ );
2472
+ }
2236
2473
  if (isCurrent !== void 0 && !isCurrent()) {
2237
2474
  return { available: false, inbound: false };
2238
2475
  }
@@ -2243,87 +2480,102 @@ var ZlinkStreamConnectorBundle = (() => {
2243
2480
  try {
2244
2481
  frames = this.protocol.decodeFrames(frameBytes);
2245
2482
  } catch (cause) {
2246
- throw cause;
2483
+ throw new ZlinkStreamConnectionEnd(
2484
+ toStreamError(cause, "frameDecodeFailed" /* FrameDecodeFailed */, "Frame decode failed.")
2485
+ );
2247
2486
  }
2248
2487
  for (const frame of frames) {
2249
2488
  if (isCurrent !== void 0 && !isCurrent()) {
2250
2489
  break;
2251
2490
  }
2252
2491
  try {
2253
- await this.dispatch(connection, frame.header, frame.payload, signal, connectionForSend);
2492
+ await this.dispatch(connection, frame.header, frame.payload, signal);
2254
2493
  } catch (cause) {
2255
- if (frame.header.kind === 5 /* Control */ && frame.header.name === ZLINK_STREAM_HEARTBEAT_PING) {
2256
- throw cause;
2257
- }
2494
+ if (cause instanceof ZlinkStreamConnectionEnd) throw cause;
2258
2495
  const error = toStreamError(
2259
2496
  cause,
2260
2497
  "frameDecodeFailed" /* FrameDecodeFailed */,
2261
2498
  "Frame dispatch failed."
2262
2499
  );
2263
2500
  if (error.code === "frameDecodeFailed" /* FrameDecodeFailed */) {
2264
- throw cause;
2501
+ throw new ZlinkStreamConnectionEnd(error);
2265
2502
  }
2266
- await this.events.publishError(error, signal);
2503
+ this.events.publishError(error, signal);
2267
2504
  }
2268
2505
  }
2269
2506
  return { available: true, inbound: true };
2270
2507
  }
2271
- async dispatch(connection, header, payload, signal, connectionForSend) {
2508
+ /**
2509
+ * Spec stream-connector 32 §4.7 and §9: a decompressed payload over the
2510
+ * receive limit is `FrameTooLarge` and ends the connection as a protocol
2511
+ * error, like a wire payload over the limit. A payload that does not
2512
+ * decompress fails only its packet.
2513
+ */
2514
+ decodePayload(header, payload) {
2515
+ try {
2516
+ return this.protocol.decodePayload(header, payload);
2517
+ } catch (cause) {
2518
+ const error = toStreamError(
2519
+ cause,
2520
+ "decompressionFailed" /* DecompressionFailed */,
2521
+ "Decompression failed."
2522
+ );
2523
+ if (error.code === "frameTooLarge" /* FrameTooLarge */) {
2524
+ throw new ZlinkStreamConnectionEnd(error);
2525
+ }
2526
+ throw cause;
2527
+ }
2528
+ }
2529
+ async dispatch(connection, header, payload, signal) {
2272
2530
  const actor = header.actorSlot === void 0 ? void 0 : this.actors.resolve(header.actorSlot);
2273
2531
  if (header.kind === 3 /* Response */ && header.requestSeq !== void 0) {
2274
2532
  try {
2275
- if (!this.pendingRequests.resolve(
2533
+ this.pendingRequests.resolve(
2276
2534
  header.requestSeq,
2277
- {
2535
+ () => ({
2278
2536
  codec: header.codec,
2279
- payload: this.protocol.decodePayload(header, payload)
2280
- },
2537
+ payload: this.decodePayload(header, payload)
2538
+ }),
2281
2539
  header.metadata
2282
- )) {
2283
- await this.events.publishError(
2284
- {
2285
- code: "frameDecodeFailed" /* FrameDecodeFailed */,
2286
- message: `Response request sequence '${header.requestSeq}' has no pending request.`
2287
- },
2288
- signal
2289
- );
2290
- }
2540
+ );
2291
2541
  } catch (cause) {
2542
+ if (cause instanceof ZlinkStreamConnectionEnd) throw cause;
2292
2543
  const decodeError = toStreamError(
2293
2544
  cause,
2294
2545
  "decompressionFailed" /* DecompressionFailed */,
2295
2546
  "Decompression failed."
2296
2547
  );
2297
2548
  if (!this.pendingRequests.reject(header.requestSeq, decodeError)) {
2298
- await this.events.publishError(decodeError, signal);
2549
+ this.events.publishError(decodeError, signal);
2299
2550
  }
2300
2551
  }
2301
2552
  return;
2302
2553
  }
2303
2554
  if (header.kind === 4 /* Error */ && header.requestSeq !== void 0) {
2304
2555
  try {
2305
- const remoteError = decodeRemoteError(this.protocol, header, payload);
2556
+ const remoteError = decodeRemoteError(this.decodePayload(header, payload));
2306
2557
  if (!this.pendingRequests.reject(header.requestSeq, remoteError)) {
2307
- await this.events.publishError(remoteError, signal);
2558
+ this.events.publishError(remoteError, signal);
2308
2559
  }
2309
2560
  } catch (cause) {
2561
+ if (cause instanceof ZlinkStreamConnectionEnd) throw cause;
2310
2562
  const decodeError = toStreamError(
2311
2563
  cause,
2312
2564
  "frameDecodeFailed" /* FrameDecodeFailed */,
2313
2565
  "Remote error payload is invalid."
2314
2566
  );
2315
2567
  if (!this.pendingRequests.reject(header.requestSeq, decodeError)) {
2316
- await this.events.publishError(decodeError, signal);
2568
+ this.events.publishError(decodeError, signal);
2317
2569
  }
2318
2570
  }
2319
2571
  return;
2320
2572
  }
2321
2573
  if (header.kind === 4 /* Error */) {
2322
- await this.events.publishError(decodeRemoteError(this.protocol, header, payload), signal);
2574
+ this.events.publishError(decodeRemoteError(this.decodePayload(header, payload)), signal);
2323
2575
  return;
2324
2576
  }
2325
2577
  if (header.kind === 5 /* Control */) {
2326
- await this.dispatchControl(connection, header, payload, signal, connectionForSend);
2578
+ await this.dispatchControl(connection, header, payload, signal);
2327
2579
  return;
2328
2580
  }
2329
2581
  if (header.kind === 1 /* Send */) {
@@ -2331,7 +2583,7 @@ var ZlinkStreamConnectorBundle = (() => {
2331
2583
  {
2332
2584
  name: header.name,
2333
2585
  metadata: header.metadata,
2334
- payload: { codec: header.codec, payload: this.protocol.decodePayload(header, payload) },
2586
+ payload: { codec: header.codec, payload: this.decodePayload(header, payload) },
2335
2587
  actorId: actor == null ? void 0 : actor.actorId,
2336
2588
  [zlinkStreamActorBinding]: actor
2337
2589
  },
@@ -2339,8 +2591,8 @@ var ZlinkStreamConnectorBundle = (() => {
2339
2591
  );
2340
2592
  }
2341
2593
  }
2342
- async dispatchControl(connection, header, payload, signal, connectionForSend) {
2343
- var _a, _b;
2594
+ async dispatchControl(connection, header, payload, signal) {
2595
+ var _a;
2344
2596
  if (this.actors.processControl(header.name, payload, signal)) {
2345
2597
  return;
2346
2598
  }
@@ -2356,18 +2608,7 @@ var ZlinkStreamConnectorBundle = (() => {
2356
2608
  );
2357
2609
  }
2358
2610
  if (header.name === ZLINK_STREAM_HEARTBEAT_PING) {
2359
- try {
2360
- await this.frameSender.sendControl(
2361
- (_b = connectionForSend == null ? void 0 : connectionForSend()) != null ? _b : connection,
2362
- ZLINK_STREAM_HEARTBEAT_PONG,
2363
- signal
2364
- );
2365
- } catch (cause) {
2366
- throw connectorError(
2367
- "sendFailed" /* SendFailed */,
2368
- cause instanceof Error ? cause.message : "Heartbeat pong send failed."
2369
- );
2370
- }
2611
+ await this.frameSender.sendControl(connection, ZLINK_STREAM_HEARTBEAT_PONG);
2371
2612
  return;
2372
2613
  }
2373
2614
  if (header.name !== ZLINK_STREAM_HEARTBEAT_PONG) {
@@ -2375,8 +2616,7 @@ var ZlinkStreamConnectorBundle = (() => {
2375
2616
  }
2376
2617
  }
2377
2618
  };
2378
- function decodeRemoteError(protocol, header, payload) {
2379
- const decodedPayload = protocol.decodePayload(header, payload);
2619
+ function decodeRemoteError(decodedPayload) {
2380
2620
  let decoded;
2381
2621
  try {
2382
2622
  decoded = JSON.parse(utf8Decode2(decodedPayload));
@@ -2422,6 +2662,7 @@ var ZlinkStreamConnectorBundle = (() => {
2422
2662
  __publicField(this, "lastInboundAt", 0);
2423
2663
  __publicField(this, "closeTask");
2424
2664
  __publicField(this, "connectTask");
2665
+ __publicField(this, "connectAbort");
2425
2666
  __publicField(this, "disconnectTask");
2426
2667
  __publicField(this, "closeRequested", false);
2427
2668
  __publicField(this, "disconnectedPublished", false);
@@ -2455,11 +2696,20 @@ var ZlinkStreamConnectorBundle = (() => {
2455
2696
  });
2456
2697
  return await this.connectTask;
2457
2698
  }
2699
+ /**
2700
+ * Spec stream-connector 32 §7: close does not wait for a connect attempt or a
2701
+ * reconnect delay to run its course. The attempts listen to one controller
2702
+ * the lifecycle owns; close aborts it, and the caller's signal is forwarded
2703
+ * to it.
2704
+ */
2458
2705
  async connectOnce(signal) {
2459
- var _a;
2460
- await this.setState("connecting" /* Connecting */, void 0, signal);
2706
+ const attempts = new AbortController();
2707
+ const forwardAbort = () => attempts.abort();
2708
+ signal == null ? void 0 : signal.addEventListener("abort", forwardAbort, { once: true });
2709
+ this.connectAbort = attempts;
2710
+ this.setState("connecting" /* Connecting */, void 0, signal);
2461
2711
  try {
2462
- const connection = await this.connectWithReconnect(signal);
2712
+ const connection = await this.connectWithReconnect(attempts.signal);
2463
2713
  if (this.closeRequested) {
2464
2714
  try {
2465
2715
  await connection.close(signal);
@@ -2474,41 +2724,62 @@ var ZlinkStreamConnectorBundle = (() => {
2474
2724
  }
2475
2725
  this.currentConnection = connection;
2476
2726
  this.connectionGeneration += 1;
2727
+ const generation = this.connectionGeneration;
2728
+ this.frameSender.open(connection, (error) => {
2729
+ void this.disconnectForTransportFailure(error, connection, generation);
2730
+ });
2477
2731
  this.disconnectedPublished = false;
2478
2732
  this.receivedMessages.resetForNewConnection();
2479
2733
  this.lastInboundAt = Date.now();
2480
- await this.setState("connected" /* Connected */, void 0, signal);
2734
+ this.setState("connected" /* Connected */, void 0, signal);
2481
2735
  this.startHeartbeat();
2482
2736
  this.startReceiveLoop();
2483
2737
  } catch (cause) {
2484
2738
  if (this.closeRequested) {
2485
2739
  const message = cause instanceof Error ? cause.message : "Connector closed while connecting.";
2486
- const error2 = toStreamError(cause, "disconnected" /* Disconnected */, message);
2487
- throw new ZlinkStreamException(error2);
2740
+ throw connectorError("disconnected" /* Disconnected */, message, cause);
2741
+ }
2742
+ if ((signal == null ? void 0 : signal.aborted) === true) {
2743
+ this.setState("disconnected" /* Disconnected */, void 0, signal);
2744
+ throwIfAborted(signal);
2488
2745
  }
2489
2746
  const error = toStreamError(cause, "connectTimeout" /* ConnectTimeout */, "Connect failed.");
2490
- (_a = this.closeReasonValue) != null ? _a : this.closeReasonValue = "TransportError";
2491
- void this.setState("disconnected" /* Disconnected */, error, signal);
2492
- this.publishDisconnectedWithoutWaiting(signal);
2747
+ this.closeReasonValue = "TransportError";
2748
+ this.setState("disconnected" /* Disconnected */, error, signal);
2749
+ this.disconnectedPublished = false;
2750
+ this.publishDisconnected(signal);
2493
2751
  throw new ZlinkStreamException(error);
2752
+ } finally {
2753
+ signal == null ? void 0 : signal.removeEventListener("abort", forwardAbort);
2754
+ this.connectAbort = void 0;
2494
2755
  }
2495
2756
  }
2757
+ /**
2758
+ * Spec stream-connector 32 §7: a `close` called outside the registered
2759
+ * handlers and callbacks returns once the close work has ended. One called
2760
+ * from inside them returns right after starting it; the result goes to the
2761
+ * outside caller, so a callback never waits for the close of the path that
2762
+ * is running it.
2763
+ */
2496
2764
  async close(signal) {
2497
- this.closeReasonValue = "ClientClose";
2498
- this.closeRequested = true;
2499
- if (this.closeTask !== void 0) {
2500
- return await this.closeTask;
2765
+ var _a;
2766
+ if (this.currentConnection !== void 0 || this.closeReasonValue === void 0) {
2767
+ this.closeReasonValue = "ClientClose";
2501
2768
  }
2502
- if (this.currentState === "closed" /* Closed */) {
2769
+ this.closeRequested = true;
2770
+ if (this.closeTask === void 0 && this.currentState === "closed" /* Closed */) {
2503
2771
  return;
2504
2772
  }
2505
- this.closeTask = this.closeOnce(signal).finally(() => {
2773
+ const closeTask = (_a = this.closeTask) != null ? _a : this.closeTask = this.closeOnce(signal).finally(() => {
2506
2774
  this.closeTask = void 0;
2507
2775
  });
2508
- return await this.closeTask;
2776
+ if (this.receivedMessages.dispatching) {
2777
+ void closeTask.catch(() => void 0);
2778
+ return;
2779
+ }
2780
+ return await closeTask;
2509
2781
  }
2510
2782
  async serverClosing(reason) {
2511
- this.closeReasonValue = reason;
2512
2783
  const error = {
2513
2784
  code: "disconnected" /* Disconnected */,
2514
2785
  message: `Server closed the session: ${reason}.`
@@ -2516,40 +2787,30 @@ var ZlinkStreamConnectorBundle = (() => {
2516
2787
  await this.disconnectForTransportFailure(
2517
2788
  error,
2518
2789
  this.currentConnection,
2519
- this.connectionGeneration
2790
+ this.connectionGeneration,
2791
+ reason
2520
2792
  );
2521
2793
  }
2522
2794
  async closeOnce(signal) {
2523
- var _a, _b;
2524
- await ((_a = this.connectTask) == null ? void 0 : _a.catch(() => void 0));
2525
- await ((_b = this.disconnectTask) == null ? void 0 : _b.catch(() => void 0));
2526
- const connection = this.currentConnection;
2527
- this.stopHeartbeat();
2528
- this.stopReceiveLoop();
2529
- this.currentConnection = void 0;
2795
+ var _a, _b, _c;
2796
+ (_a = this.connectAbort) == null ? void 0 : _a.abort();
2797
+ await ((_b = this.connectTask) == null ? void 0 : _b.catch(() => void 0));
2798
+ await ((_c = this.disconnectTask) == null ? void 0 : _c.catch(() => void 0));
2530
2799
  const errors = [];
2531
2800
  if (this.lateConnectCleanupError !== void 0) {
2532
2801
  errors.push(this.lateConnectCleanupError);
2533
2802
  this.lateConnectCleanupError = void 0;
2534
2803
  }
2535
2804
  try {
2536
- await this.frameSender.drain(signal);
2537
- } catch (error) {
2538
- errors.push(error);
2539
- }
2540
- try {
2541
- await (connection == null ? void 0 : connection.close(signal));
2805
+ await this.tearDownConnection(
2806
+ { code: "disconnected" /* Disconnected */, message: "Connector closed." },
2807
+ signal
2808
+ );
2542
2809
  } catch (error) {
2543
2810
  errors.push(error);
2544
2811
  }
2545
- this.pendingRequests.failAll({
2546
- code: "disconnected" /* Disconnected */,
2547
- message: "Connector closed."
2548
- });
2549
- this.receivedMessages.connectionEnded();
2550
- this.actors.closeAll(signal);
2551
- this.queueState("closed" /* Closed */, void 0, signal);
2552
- this.queueDisconnected(signal);
2812
+ this.setState("closed" /* Closed */, void 0, signal);
2813
+ this.publishDisconnected(signal);
2553
2814
  if (errors.length === 1) throw errors[0];
2554
2815
  if (errors.length > 1) throw new AggregateError(errors, "Stream connector close failed.");
2555
2816
  }
@@ -2567,7 +2828,7 @@ var ZlinkStreamConnectorBundle = (() => {
2567
2828
  if (this.options.dispatchMode !== "immediate" /* Immediate */) {
2568
2829
  await this.settleReceiveLoop();
2569
2830
  }
2570
- await this.receivedMessages.pump();
2831
+ this.receivedMessages.pump();
2571
2832
  }
2572
2833
  connectionForSend() {
2573
2834
  if (this.currentConnection === void 0 || this.currentState !== "connected" /* Connected */) {
@@ -2580,8 +2841,7 @@ var ZlinkStreamConnectorBundle = (() => {
2580
2841
  const result = await this.receiveDispatcher.readAndDispatch(
2581
2842
  connection,
2582
2843
  signal,
2583
- () => this.isCurrentConnection(connection, generation),
2584
- () => this.connectionForSend()
2844
+ () => this.isCurrentConnection(connection, generation)
2585
2845
  );
2586
2846
  if (result.inbound && this.isCurrentConnection(connection, generation)) {
2587
2847
  this.lastInboundAt = Date.now();
@@ -2590,7 +2850,10 @@ var ZlinkStreamConnectorBundle = (() => {
2590
2850
  }
2591
2851
  async connectWithReconnect(signal) {
2592
2852
  let attempt = 0;
2593
- let delayMs = this.options.reconnect.initialDelayMs;
2853
+ let delayMs = Math.min(
2854
+ this.options.reconnect.initialDelayMs,
2855
+ this.options.reconnect.maxDelayMs
2856
+ );
2594
2857
  let lastError;
2595
2858
  const maxAttempts = this.options.reconnect.enabled ? this.options.reconnect.maxAttempts : 1;
2596
2859
  const unlimited = maxAttempts === null;
@@ -2600,10 +2863,10 @@ var ZlinkStreamConnectorBundle = (() => {
2600
2863
  return await this.options.transportFactory.connect(this.options, signal);
2601
2864
  } catch (cause) {
2602
2865
  lastError = toStreamError(cause, "connectTimeout" /* ConnectTimeout */, "Connect failed.");
2603
- if (!this.options.reconnect.enabled || !unlimited && attempt >= maxAttempts) {
2866
+ if (this.closeRequested || !this.options.reconnect.enabled || !unlimited && attempt >= maxAttempts) {
2604
2867
  break;
2605
2868
  }
2606
- await this.setState("reconnecting" /* Reconnecting */, lastError, signal);
2869
+ this.setState("reconnecting" /* Reconnecting */, lastError, signal);
2607
2870
  await delay(randomizedDelay(delayMs), signal);
2608
2871
  delayMs = Math.min(
2609
2872
  this.options.reconnect.maxDelayMs,
@@ -2673,12 +2936,8 @@ var ZlinkStreamConnectorBundle = (() => {
2673
2936
  }
2674
2937
  } catch (cause) {
2675
2938
  if (signal.aborted) return;
2676
- const error = toStreamError(
2677
- cause,
2678
- "frameDecodeFailed" /* FrameDecodeFailed */,
2679
- "Receive loop failed."
2680
- );
2681
- await this.disconnectForTransportFailure(error, connection, generation);
2939
+ if (!(cause instanceof ZlinkStreamConnectionEnd)) throw cause;
2940
+ await this.disconnectForTransportFailure(cause.error, connection, generation);
2682
2941
  } finally {
2683
2942
  this.receiveLoopSleeping = false;
2684
2943
  this.releaseReceiveLoopSettled();
@@ -2735,37 +2994,35 @@ var ZlinkStreamConnectorBundle = (() => {
2735
2994
  return;
2736
2995
  }
2737
2996
  if (Date.now() - this.lastInboundAt > this.options.heartbeat.timeoutMs) {
2738
- this.closeReasonValue = "HeartbeatTimeout";
2739
2997
  const error = { code: "disconnected" /* Disconnected */, message: "Heartbeat timed out." };
2740
2998
  await this.disconnectForTransportFailure(
2741
2999
  error,
2742
3000
  this.currentConnection,
2743
- this.connectionGeneration
3001
+ this.connectionGeneration,
3002
+ "HeartbeatTimeout"
2744
3003
  );
2745
3004
  return;
2746
3005
  }
2747
- const connection = this.currentConnection;
2748
- const generation = this.connectionGeneration;
2749
- try {
2750
- await this.frameSender.sendControl(this.connectionForSend(), ZLINK_STREAM_HEARTBEAT_PING);
2751
- } catch (cause) {
2752
- const error = toStreamError(cause, "sendFailed" /* SendFailed */, "Heartbeat send failed.");
2753
- await this.disconnectForTransportFailure(error, connection, generation);
2754
- }
3006
+ await this.frameSender.sendControl(this.connectionForSend(), ZLINK_STREAM_HEARTBEAT_PING);
2755
3007
  }
2756
- async disconnectForTransportFailure(error, origin, generation) {
2757
- var _a;
2758
- (_a = this.closeReasonValue) != null ? _a : this.closeReasonValue = "TransportError";
3008
+ /**
3009
+ * @param reason Given only where the reason is not read from `error`: a
3010
+ * server `session-closing` and the heartbeat timeout. Every other ending
3011
+ * takes it from {@link closeReasonFor}.
3012
+ */
3013
+ async disconnectForTransportFailure(error, origin, generation, reason = closeReasonFor(error)) {
2759
3014
  if (this.closeRequested || this.currentState === "closed" /* Closed */) {
2760
3015
  return;
2761
3016
  }
2762
3017
  if (origin !== void 0 && !this.isCurrentConnection(origin, generation)) {
2763
3018
  return;
2764
3019
  }
3020
+ this.closeReasonValue = reason;
2765
3021
  if (this.disconnectTask !== void 0) {
2766
3022
  return await this.disconnectTask;
2767
3023
  }
2768
- this.disconnectTask = this.tearDownConnection(error).finally(() => {
3024
+ this.disconnectTask = this.tearDownConnection(error).catch(() => {
3025
+ }).finally(() => {
2769
3026
  this.disconnectTask = void 0;
2770
3027
  });
2771
3028
  await this.disconnectTask;
@@ -2774,19 +3031,30 @@ var ZlinkStreamConnectorBundle = (() => {
2774
3031
  isCurrentConnection(connection, generation) {
2775
3032
  return !this.closeRequested && this.currentConnection === connection && this.connectionGeneration === generation;
2776
3033
  }
2777
- /** Transport teardown only — no application callback runs from here. */
2778
- async tearDownConnection(error) {
3034
+ /**
3035
+ * Ends the current connection, for close and for transport loss alike. No
3036
+ * application callback runs from here. Spec stream-connector 32 §7 and §9:
3037
+ * every operation the ending connection fails (the frames it has not
3038
+ * written, the one it is writing and the pending requests) fails with
3039
+ * `Disconnected`, whatever ended it; the cause stays in the close reason.
3040
+ * The transport is closed without waiting for the frames.
3041
+ */
3042
+ async tearDownConnection(error, signal) {
2779
3043
  this.stopHeartbeat();
2780
3044
  this.stopReceiveLoop();
2781
3045
  const connection = this.currentConnection;
2782
3046
  this.currentConnection = void 0;
2783
- this.pendingRequests.failAll(error);
2784
- this.receivedMessages.connectionEnded();
2785
- this.actors.closeAll();
2786
- try {
2787
- await (connection == null ? void 0 : connection.close());
2788
- } catch {
3047
+ const disconnected = { code: "disconnected" /* Disconnected */, message: error.message };
3048
+ if (connection !== void 0) {
3049
+ this.frameSender.failUnwritten(
3050
+ connection,
3051
+ connectorError(disconnected.code, disconnected.message)
3052
+ );
2789
3053
  }
3054
+ this.pendingRequests.failAll(disconnected);
3055
+ this.receivedMessages.connectionEnded();
3056
+ this.actors.closeAll(signal);
3057
+ await (connection == null ? void 0 : connection.close(signal));
2790
3058
  }
2791
3059
  /**
2792
3060
  * Runs once the teardown promise has settled, so a handler reached from here
@@ -2798,8 +3066,8 @@ var ZlinkStreamConnectorBundle = (() => {
2798
3066
  */
2799
3067
  async announceDisconnect(error) {
2800
3068
  if (this.closeRequested) return;
2801
- this.queueState("disconnected" /* Disconnected */, error);
2802
- this.queueDisconnected();
3069
+ this.setState("disconnected" /* Disconnected */, error);
3070
+ this.publishDisconnected();
2803
3071
  if (this.shouldReconnect()) {
2804
3072
  queueMicrotask(() => {
2805
3073
  void this.connect().catch(() => void 0);
@@ -2809,79 +3077,35 @@ var ZlinkStreamConnectorBundle = (() => {
2809
3077
  shouldReconnect() {
2810
3078
  return this.options.reconnect.enabled && !this.closeRequested;
2811
3079
  }
2812
- // Synchronous test-and-set, taken before any await, so the single disconnect
2813
- // notification the spec promises is claimed by exactly one caller even when
2814
- // the publishing itself is deferred.
2815
- claimDisconnectedPublish() {
2816
- if (this.disconnectedPublished) return false;
2817
- this.disconnectedPublished = true;
2818
- return true;
2819
- }
2820
- async publishDisconnectedOnce(signal) {
2821
- if (!this.claimDisconnectedPublish()) return;
2822
- await this.events.publishDisconnected(signal);
2823
- }
2824
3080
  /**
2825
- * Spec stream-connector 32 §7: the connector runs the registered handler, it
2826
- * does not wait for it to finish. `close` returns once its own work is done —
2827
- * the drain, the transport close, the pending requests — having run the
2828
- * disconnect handler but without looking at whether that handler has ended.
2829
- *
2830
- * Starting the publish without awaiting it still runs every handler right
2831
- * here: an `async` function body runs synchronously up to its first `await`,
2832
- * and `publishDisconnected` reaches each handler before that point. So the
2833
- * handler has run by the time `close` returns, which is what the spec asks
2834
- * for, while a handler that calls `close` no longer waits for the very
2835
- * `closeTask` it is running inside. Java and C++ hand the handler to a queue
2836
- * and return the same way.
2837
- *
2838
- * `claimDisconnectedPublish` is taken by the same call and before any await,
2839
- * so deferring the completion never turns the one notification into two.
2840
- *
2841
- * Nothing awaits the promise, so a rejection would reach the process as an
2842
- * unhandled rejection and kill it. Handler failures are already contained
2843
- * where they were before this call stopped waiting — the `Promise.allSettled`
2844
- * in `publishDisconnected` — and this `catch` covers what is left rather than
2845
- * reporting the same failure a second time on the error surface.
3081
+ * Spec stream-connector 32 §6 and §7: one disconnect notification per
3082
+ * lifecycle event, handed to the dispatch queue. The connector does not wait
3083
+ * for the handler; `Manual` runs it when the application pumps dispatch.
3084
+ * The flag is tested and set synchronously, so concurrent callers for the
3085
+ * same event claim it once.
2846
3086
  */
2847
- publishDisconnectedWithoutWaiting(signal) {
2848
- void this.publishDisconnectedOnce(signal).catch(() => void 0);
2849
- }
2850
- queueDisconnected(signal) {
2851
- if (!this.claimDisconnectedPublish()) return;
2852
- this.receivedMessages.enqueueCallback(() => {
2853
- void this.events.publishDisconnected(signal).catch(() => void 0);
2854
- });
2855
- }
2856
- queueState(current, error, signal) {
2857
- const change = this.transitionState(current, error);
2858
- if (change === void 0) return;
2859
- this.receivedMessages.enqueueCallback(() => {
2860
- void this.publishStateChange(change, signal).catch(() => void 0);
2861
- });
2862
- }
2863
- async setState(current, error, signal) {
2864
- const change = this.transitionState(current, error);
2865
- if (change === void 0) return;
2866
- await this.publishStateChange(change, signal);
3087
+ publishDisconnected(signal) {
3088
+ if (this.disconnectedPublished) return;
3089
+ this.disconnectedPublished = true;
3090
+ this.events.publishDisconnected(signal);
2867
3091
  }
2868
- transitionState(current, error) {
3092
+ setState(current, error, signal) {
2869
3093
  const previous = this.currentState;
2870
3094
  this.currentState = current;
2871
- if (previous === current && error === void 0) {
2872
- return void 0;
2873
- }
2874
- return { previous, current, error };
2875
- }
2876
- async publishStateChange(change, signal) {
2877
- await this.events.publishStateChanged(change, signal);
2878
- if (change.error !== void 0) await this.events.publishError(change.error, signal);
3095
+ if (previous === current && error === void 0) return;
3096
+ this.events.publishStateChanged({ previous, current, error }, signal);
3097
+ if (error !== void 0) this.events.publishError(error, signal);
2879
3098
  }
2880
3099
  };
2881
3100
 
2882
3101
  // packages/stream-connector/src/Runtime/ZlinkStreamConnectorEvents.ts
2883
3102
  var ZlinkStreamConnectorEvents = class {
2884
- constructor() {
3103
+ /**
3104
+ * @param enqueueCallback Queues a callback that calls the number of handlers
3105
+ * `callbacks` reports for the handlers registered at that moment.
3106
+ */
3107
+ constructor(enqueueCallback) {
3108
+ this.enqueueCallback = enqueueCallback;
2885
3109
  __publicField(this, "errorHandlers", /* @__PURE__ */ new Set());
2886
3110
  __publicField(this, "disconnectedHandlers", /* @__PURE__ */ new Set());
2887
3111
  __publicField(this, "stateHandlers", /* @__PURE__ */ new Set());
@@ -2898,23 +3122,78 @@ var ZlinkStreamConnectorBundle = (() => {
2898
3122
  this.stateHandlers.add(handler);
2899
3123
  return subscription(() => this.stateHandlers.delete(handler));
2900
3124
  }
2901
- async publishError(error, signal) {
2902
- await this.publish(
2903
- Array.from(this.errorHandlers).map((handler) => () => handler(error, signal))
3125
+ publishError(error, signal) {
3126
+ this.publish(this.errorHandlers, (handler) => handler(error, signal), signal);
3127
+ }
3128
+ publishDisconnected(signal) {
3129
+ this.publish(this.disconnectedHandlers, (handler) => handler(signal), signal);
3130
+ }
3131
+ publishStateChanged(change, signal) {
3132
+ this.publish(this.stateHandlers, (handler) => handler(change, signal), signal);
3133
+ }
3134
+ /**
3135
+ * Spec stream-connector 32 §7: the connector runs a registered callback and
3136
+ * does not wait for it to finish. A thrown or rejected failure reaches the
3137
+ * error handlers as `UserCallbackFailed`.
3138
+ */
3139
+ runUserCallback(run, failureMessage, signal) {
3140
+ this.invoke(
3141
+ run,
3142
+ (cause) => this.publishError(
3143
+ { code: "userCallbackFailed" /* UserCallbackFailed */, message: failureMessage, cause },
3144
+ signal
3145
+ )
2904
3146
  );
2905
3147
  }
2906
- async publishDisconnected(signal) {
2907
- await this.publish(
2908
- Array.from(this.disconnectedHandlers).map((handler) => () => handler(signal))
3148
+ publish(handlers, invoke, signal) {
3149
+ this.enqueueCallback(
3150
+ () => {
3151
+ for (const handler of Array.from(handlers)) {
3152
+ const report = (cause) => this.reportFailure(
3153
+ cause,
3154
+ signal,
3155
+ handlers === this.errorHandlers ? handler : void 0
3156
+ );
3157
+ this.invoke(() => invoke(handler), report);
3158
+ }
3159
+ },
3160
+ () => handlers.size
2909
3161
  );
2910
3162
  }
2911
- async publishStateChanged(change, signal) {
2912
- await this.publish(
2913
- Array.from(this.stateHandlers).map((handler) => () => handler(change, signal))
3163
+ reportFailure(cause, signal, failedHandler) {
3164
+ const error = {
3165
+ code: "userCallbackFailed" /* UserCallbackFailed */,
3166
+ message: "Connector event handler failed.",
3167
+ cause
3168
+ };
3169
+ if (failedHandler === void 0) {
3170
+ this.publishError(error, signal);
3171
+ return;
3172
+ }
3173
+ this.reportToRemaining(error, signal, /* @__PURE__ */ new Set([failedHandler]));
3174
+ }
3175
+ reportToRemaining(error, signal, attempted) {
3176
+ const remaining = Array.from(this.errorHandlers).filter(
3177
+ (candidate) => !attempted.has(candidate)
3178
+ );
3179
+ if (remaining.length === 0) return;
3180
+ const nextAttempted = /* @__PURE__ */ new Set([...attempted, ...remaining]);
3181
+ this.enqueueCallback(
3182
+ () => {
3183
+ for (const handler of remaining) {
3184
+ const report = (cause) => this.reportToRemaining({ ...error, cause }, signal, nextAttempted);
3185
+ this.invoke(() => handler(error, signal), report);
3186
+ }
3187
+ },
3188
+ () => remaining.length
2914
3189
  );
2915
3190
  }
2916
- async publish(handlers) {
2917
- await Promise.allSettled(handlers.map(async (handler) => handler()));
3191
+ invoke(run, report) {
3192
+ try {
3193
+ Promise.resolve(run()).catch(report);
3194
+ } catch (cause) {
3195
+ report(cause);
3196
+ }
2918
3197
  }
2919
3198
  };
2920
3199
 
@@ -2985,11 +3264,7 @@ var ZlinkStreamConnectorBundle = (() => {
2985
3264
  if (this.closed || this.socket.readyState !== 1) {
2986
3265
  throw connectorError("disconnected" /* Disconnected */, "Remote stream is not connected.");
2987
3266
  }
2988
- try {
2989
- this.socket.send(frame);
2990
- } catch (cause) {
2991
- throw connectorError("sendFailed" /* SendFailed */, "Send failed.", cause);
2992
- }
3267
+ this.socket.send(frame);
2993
3268
  }
2994
3269
  async read(signal) {
2995
3270
  throwIfAborted(signal);
@@ -3007,18 +3282,19 @@ var ZlinkStreamConnectorBundle = (() => {
3007
3282
  await this.waitForMessage(signal);
3008
3283
  }
3009
3284
  }
3285
+ /**
3286
+ * Spec stream-connector 32 §7: closing does not wait for the peer to read or
3287
+ * answer. `WebSocket.close` sends the close frame after the data already
3288
+ * handed to `send`, so a frame whose write completed is not torn.
3289
+ */
3010
3290
  async close(signal) {
3011
3291
  throwIfAborted(signal);
3292
+ this.removeListeners();
3012
3293
  if (!this.closed) {
3013
3294
  this.closed = true;
3014
3295
  this.socket.close();
3015
- this.wakeReader();
3016
- }
3017
- try {
3018
- await waitForClose(this.socket, signal);
3019
- } finally {
3020
- this.removeListeners();
3021
3296
  }
3297
+ this.wakeReader();
3022
3298
  }
3023
3299
  waitForMessage(signal) {
3024
3300
  if (this.readWaiter !== void 0) {
@@ -3064,21 +3340,6 @@ var ZlinkStreamConnectorBundle = (() => {
3064
3340
  return message;
3065
3341
  }
3066
3342
  };
3067
- function waitForClose(socket, signal) {
3068
- if (socket.readyState === 3) return Promise.resolve();
3069
- return new Promise((resolve, reject) => {
3070
- const onClose = () => finish();
3071
- const onAbort = () => finish(connectorError("disconnected" /* Disconnected */, "Close canceled."));
3072
- const finish = (error) => {
3073
- socket.removeEventListener("close", onClose);
3074
- signal == null ? void 0 : signal.removeEventListener("abort", onAbort);
3075
- if (error === void 0) resolve();
3076
- else reject(error);
3077
- };
3078
- socket.addEventListener("close", onClose, { once: true });
3079
- signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
3080
- });
3081
- }
3082
3343
  async function waitForOpen(socket, connectTimeoutMs, signal) {
3083
3344
  throwIfAborted(signal);
3084
3345
  await new Promise((resolve, reject) => {
@@ -3127,7 +3388,9 @@ var ZlinkStreamConnectorBundle = (() => {
3127
3388
  constructor(options) {
3128
3389
  __publicField(this, "receivedMessages");
3129
3390
  __publicField(this, "lifecycle");
3130
- __publicField(this, "events", new ZlinkStreamConnectorEvents());
3391
+ __publicField(this, "events", new ZlinkStreamConnectorEvents(
3392
+ (callback, callbacks) => this.receivedMessages.enqueueCallback(callback, callbacks)
3393
+ ));
3131
3394
  __publicField(this, "correlationCounter", 0n);
3132
3395
  __publicField(this, "pendingRequests", new ZlinkStreamPendingRequests());
3133
3396
  __publicField(this, "frameSender");
@@ -3173,7 +3436,7 @@ var ZlinkStreamConnectorBundle = (() => {
3173
3436
  return this.lifecycle.state;
3174
3437
  }
3175
3438
  get pendingDispatchCount() {
3176
- return this.pendingRequests.count;
3439
+ return this.receivedMessages.pendingCallbacks;
3177
3440
  }
3178
3441
  get actors() {
3179
3442
  return this.boundActors.snapshot;
@@ -3230,7 +3493,12 @@ var ZlinkStreamConnectorBundle = (() => {
3230
3493
  }
3231
3494
  request(payload, messageType) {
3232
3495
  const encoded = this.encodePayload(payload, messageType);
3233
- return new ZlinkStreamRequestBuilder(this, this.resolveNameOrDefault(encoded), encoded);
3496
+ return new ZlinkStreamRequestBuilder(
3497
+ this,
3498
+ this.resolveNameOrDefault(encoded),
3499
+ encoded,
3500
+ (callback) => this.receivedMessages.enqueueCallback(callback)
3501
+ );
3234
3502
  }
3235
3503
  on(nameOrType, handler, messageType) {
3236
3504
  const encodedHandler = (message, signal) => handler(
@@ -3263,29 +3531,25 @@ var ZlinkStreamConnectorBundle = (() => {
3263
3531
  this,
3264
3532
  this.resolveNameOrDefault(encoded),
3265
3533
  encoded,
3534
+ (callback) => this.receivedMessages.enqueueCallback(callback),
3266
3535
  actor.slot,
3267
3536
  () => actor.ensureBound()
3268
3537
  );
3269
3538
  }
3270
3539
  onActorMessage(actor, nameOrType, handler, messageType) {
3271
- const encodedHandler = (message, signal) => {
3272
- if (message[zlinkStreamActorBinding] !== actor) {
3273
- return;
3274
- }
3275
- return handler(
3276
- {
3277
- name: message.name,
3278
- metadata: message.metadata,
3279
- payload: this.decodePayload(
3280
- message.payload,
3281
- messageType != null ? messageType : typeof nameOrType === "function" ? nameOrType : void 0
3282
- ),
3283
- actorId: message.actorId
3284
- },
3285
- signal
3286
- );
3287
- };
3288
- return this.receivedMessages.on(this.observedName(nameOrType), encodedHandler);
3540
+ const encodedHandler = (message, signal) => handler(
3541
+ {
3542
+ name: message.name,
3543
+ metadata: message.metadata,
3544
+ payload: this.decodePayload(
3545
+ message.payload,
3546
+ messageType != null ? messageType : typeof nameOrType === "function" ? nameOrType : void 0
3547
+ ),
3548
+ actorId: message.actorId
3549
+ },
3550
+ signal
3551
+ );
3552
+ return this.receivedMessages.on(this.observedName(nameOrType), encodedHandler, actor);
3289
3553
  }
3290
3554
  waitFor(nameOrType) {
3291
3555
  return new ZlinkStreamWaitBuilder(this, this.observedName(nameOrType));
@@ -3410,7 +3674,7 @@ var ZlinkStreamConnectorBundle = (() => {
3410
3674
  }
3411
3675
  return payload;
3412
3676
  }
3413
- async sendEncoded(kind, name, payload, metadata, compress, requestSeq, signal, correlationId, actorSlot) {
3677
+ async sendEncoded(kind, name, payload, metadata, compress, requestSeq, signal, correlationId, actorSlot, expiry, onAccepted) {
3414
3678
  await this.frameSender.send(
3415
3679
  this.lifecycle.connectionForSend(),
3416
3680
  kind,
@@ -3421,7 +3685,9 @@ var ZlinkStreamConnectorBundle = (() => {
3421
3685
  requestSeq,
3422
3686
  signal,
3423
3687
  correlationId,
3424
- actorSlot
3688
+ actorSlot,
3689
+ expiry,
3690
+ onAccepted
3425
3691
  );
3426
3692
  }
3427
3693
  /**
@@ -3444,37 +3710,48 @@ var ZlinkStreamConnectorBundle = (() => {
3444
3710
  }
3445
3711
  };
3446
3712
  let pending;
3713
+ let stopCancellation;
3447
3714
  try {
3448
3715
  throwIfAborted(signal);
3449
3716
  for (const handler of Array.from(this.requestSendingHandlers)) {
3450
3717
  try {
3451
3718
  handler(sendingContext);
3452
3719
  } catch (cause) {
3453
- queueMicrotask(() => {
3454
- void this.events.publishError(
3455
- toStreamError(
3456
- cause,
3457
- "userCallbackFailed" /* UserCallbackFailed */,
3458
- "Request sending hook failed."
3459
- ),
3460
- signal
3461
- );
3462
- });
3720
+ this.events.publishError(
3721
+ {
3722
+ code: "userCallbackFailed" /* UserCallbackFailed */,
3723
+ message: "Request sending hook failed.",
3724
+ cause
3725
+ },
3726
+ signal
3727
+ );
3463
3728
  }
3464
3729
  }
3465
3730
  pending = this.pendingRequests.create(name, timeoutMs);
3466
- await this.sendEncoded(
3731
+ const accepted = pending;
3732
+ const write = this.sendEncoded(
3467
3733
  2 /* Request */,
3468
3734
  name,
3469
3735
  payload,
3470
3736
  requestMetadata,
3471
3737
  compress,
3472
- pending.requestSeq,
3738
+ accepted.requestSeq,
3473
3739
  signal,
3474
3740
  this.nextCorrelationId(),
3475
- actorSlot
3741
+ actorSlot,
3742
+ accepted.promise,
3743
+ accepted.startTimeout
3476
3744
  );
3477
- const reply = await pending.promise;
3745
+ const canceled = new Promise((_, reject) => {
3746
+ const onAbort = () => reject(connectorError("disconnected" /* Disconnected */, "Operation canceled."));
3747
+ signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
3748
+ stopCancellation = () => signal == null ? void 0 : signal.removeEventListener("abort", onAbort);
3749
+ });
3750
+ const reply = await Promise.race([
3751
+ write.then(() => accepted.promise),
3752
+ accepted.promise,
3753
+ canceled
3754
+ ]);
3478
3755
  this.publishReplyReceived(
3479
3756
  {
3480
3757
  requestPacketName: name,
@@ -3488,6 +3765,7 @@ var ZlinkStreamConnectorBundle = (() => {
3488
3765
  return reply.payload;
3489
3766
  } catch (error) {
3490
3767
  if (pending !== void 0) this.pendingRequests.cancel(pending.requestSeq);
3768
+ if ((signal == null ? void 0 : signal.aborted) === true) throw error;
3491
3769
  this.publishReplyReceived(
3492
3770
  {
3493
3771
  requestPacketName: name,
@@ -3499,26 +3777,24 @@ var ZlinkStreamConnectorBundle = (() => {
3499
3777
  signal
3500
3778
  );
3501
3779
  throw error;
3780
+ } finally {
3781
+ stopCancellation == null ? void 0 : stopCancellation();
3502
3782
  }
3503
3783
  }
3504
3784
  publishReplyReceived(context, signal) {
3505
3785
  if (this.replyReceivedHandlers.size === 0) return;
3506
- this.receivedMessages.enqueueCallback(async () => {
3507
- for (const handler of Array.from(this.replyReceivedHandlers)) {
3508
- try {
3509
- await handler(context, signal);
3510
- } catch (cause) {
3511
- await this.events.publishError(
3512
- toStreamError(
3513
- cause,
3514
- "userCallbackFailed" /* UserCallbackFailed */,
3515
- "Reply received hook failed."
3516
- ),
3786
+ this.receivedMessages.enqueueCallback(
3787
+ () => {
3788
+ for (const handler of Array.from(this.replyReceivedHandlers)) {
3789
+ this.events.runUserCallback(
3790
+ () => handler(context, signal),
3791
+ "Reply received hook failed.",
3517
3792
  signal
3518
3793
  );
3519
3794
  }
3520
- }
3521
- });
3795
+ },
3796
+ () => this.replyReceivedHandlers.size
3797
+ );
3522
3798
  }
3523
3799
  resolveNameOrDefault(payload) {
3524
3800
  if (payload.messageType === void 0) {