@zlink-systems/stream-connector 0.16.0 → 0.18.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.
@@ -23,7 +23,6 @@ var ZlinkStreamConnectorBundle = (() => {
23
23
  // packages/stream-connector/src/index.ts
24
24
  var index_exports = {};
25
25
  __export(index_exports, {
26
- DefaultZlinkStreamConnector: () => DefaultZlinkStreamConnector,
27
26
  ZlinkStreamCodec: () => ZlinkStreamCodec,
28
27
  ZlinkStreamCompression: () => ZlinkStreamCompression,
29
28
  ZlinkStreamConnectionState: () => ZlinkStreamConnectionState,
@@ -707,6 +706,53 @@ var ZlinkStreamConnectorBundle = (() => {
707
706
  return new TextDecoder().decode(value);
708
707
  }
709
708
 
709
+ // packages/stream-connector/src/Runtime/ZlinkStreamAssertions.ts
710
+ var zlinkStreamAssert = {
711
+ ensure(condition, message) {
712
+ if (typeof message !== "string" || message.trim().length === 0) {
713
+ throw connectorError("validationFailed" /* ValidationFailed */, "zlinkStreamAssert.ensure requires a non-empty diagnostic message.");
714
+ }
715
+ if (!condition) {
716
+ throw connectorError("validationFailed" /* ValidationFailed */, message);
717
+ }
718
+ },
719
+ async expectFailure(action, errorKind) {
720
+ let failure;
721
+ try {
722
+ await action();
723
+ } catch (error) {
724
+ failure = error;
725
+ }
726
+ if (failure === void 0) {
727
+ throw connectorError("validationFailed" /* ValidationFailed */, "Expected action to fail.");
728
+ }
729
+ const streamError = unwrapStreamError(failure);
730
+ if (errorKind !== void 0 && streamError.code !== errorKind) {
731
+ throw connectorError(
732
+ "validationFailed" /* ValidationFailed */,
733
+ `Expected failure kind '${errorKind}', got '${streamError.code}'.`,
734
+ failure
735
+ );
736
+ }
737
+ return streamError;
738
+ },
739
+ async expectTimeout(action) {
740
+ let failure;
741
+ try {
742
+ await action();
743
+ } catch (error) {
744
+ failure = error;
745
+ }
746
+ if (failure === void 0) {
747
+ throw connectorError("validationFailed" /* ValidationFailed */, "Expected action to time out.");
748
+ }
749
+ const code = unwrapStreamError(failure).code;
750
+ if (code !== "requestTimeout" /* RequestTimeout */ && code !== "connectTimeout" /* ConnectTimeout */) {
751
+ throw failure;
752
+ }
753
+ }
754
+ };
755
+
710
756
  // packages/stream-connector/src/Runtime/Protocol/ZlinkStreamPacketNameValidator.ts
711
757
  function validateName(name, allowReserved = false) {
712
758
  if (name.length === 0) {
@@ -949,6 +995,9 @@ var ZlinkStreamConnectorBundle = (() => {
949
995
  this.timeoutMs = timeoutMs;
950
996
  return this;
951
997
  }
998
+ // Spec stream-connector 32 §10.1: the predicate reads the whole message, and
999
+ // the call answers with the messages themselves, so a caller can assert on the
1000
+ // packet name and the metadata and not only on the payload.
952
1001
  async run(signal) {
953
1002
  var _a;
954
1003
  this.markExecuted();
@@ -957,13 +1006,13 @@ var ZlinkStreamConnectorBundle = (() => {
957
1006
  }
958
1007
  const timeoutMs = (_a = this.timeoutMs) != null ? _a : this.connector.options.waitTimeoutMs;
959
1008
  const deadline = Date.now() + timeoutMs;
960
- const payloads = [];
1009
+ const messages = [];
961
1010
  for (const predicate of this.predicates) {
962
1011
  const message = await this.connector.waitForMessage(
963
1012
  this.name,
964
1013
  Math.max(0, deadline - Date.now()),
965
1014
  (candidate) => {
966
- if (!predicate(candidate.payload)) {
1015
+ if (!predicate(candidate)) {
967
1016
  throw connectorError(
968
1017
  "validationFailed" /* ValidationFailed */,
969
1018
  `Message '${this.name}' arrived out of the expected sequence.`
@@ -973,9 +1022,9 @@ var ZlinkStreamConnectorBundle = (() => {
973
1022
  },
974
1023
  signal
975
1024
  );
976
- payloads.push(message.payload);
1025
+ messages.push(message);
977
1026
  }
978
- return payloads;
1027
+ return messages;
979
1028
  }
980
1029
  ensureConfigurable() {
981
1030
  if (this.executed) {
@@ -1076,7 +1125,7 @@ var ZlinkStreamConnectorBundle = (() => {
1076
1125
  }
1077
1126
  function validatePayload(payloadLength, maxPayloadSize) {
1078
1127
  if (payloadLength > maxPayloadSize) {
1079
- throw connectorError("frameTooLarge" /* FrameTooLarge */, "Payload exceeds MaxSendPayloadSize.");
1128
+ throw connectorError("validationFailed" /* ValidationFailed */, "Payload exceeds MaxSendPayloadSize.");
1080
1129
  }
1081
1130
  }
1082
1131
 
@@ -1326,7 +1375,7 @@ var ZlinkStreamConnectorBundle = (() => {
1326
1375
  "detailed" /* Detailed */
1327
1376
  ]);
1328
1377
  function normalizeOptions(options, defaultTransportFactory) {
1329
- 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;
1378
+ 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;
1330
1379
  const endpoint = options.endpoint;
1331
1380
  if (endpoint.trim().length === 0) {
1332
1381
  throw connectorError("configurationError" /* ConfigurationError */, "Endpoint must not be empty.");
@@ -1359,22 +1408,30 @@ var ZlinkStreamConnectorBundle = (() => {
1359
1408
  initialDelayMs: (_r = (_q = options.reconnect) == null ? void 0 : _q.initialDelayMs) != null ? _r : 250,
1360
1409
  maxDelayMs: (_t = (_s = options.reconnect) == null ? void 0 : _s.maxDelayMs) != null ? _t : 5e3,
1361
1410
  backoffFactor: (_v = (_u = options.reconnect) == null ? void 0 : _u.backoffFactor) != null ? _v : 2,
1362
- maxAttempts: (_x = (_w = options.reconnect) == null ? void 0 : _w.maxAttempts) != null ? _x : 3
1411
+ maxAttempts: ((_w = options.reconnect) == null ? void 0 : _w.maxAttempts) === void 0 ? 3 : options.reconnect.maxAttempts
1363
1412
  },
1364
- maxSendPayloadSize: (_y = options.maxSendPayloadSize) != null ? _y : 64 * 1024,
1365
- maxReceivePayloadSize: (_z = options.maxReceivePayloadSize) != null ? _z : 64 * 1024,
1366
- dispatchMode: (_A = options.dispatchMode) != null ? _A : "manual" /* Manual */,
1367
- compression: (_B = options.compression) != null ? _B : "lz4" /* Lz4 */,
1413
+ maxSendPayloadSize: (_x = options.maxSendPayloadSize) != null ? _x : 64 * 1024,
1414
+ maxReceivePayloadSize: (_y = options.maxReceivePayloadSize) != null ? _y : 64 * 1024,
1415
+ dispatchMode: (_z = options.dispatchMode) != null ? _z : "manual" /* Manual */,
1416
+ compression: (_A = options.compression) != null ? _A : "lz4" /* Lz4 */,
1368
1417
  compressionCodec: resolveCompressionCodec2(options),
1369
- nameResolver: (_C = options.nameResolver) != null ? _C : { resolve: (type) => type.name },
1370
- transportFactory: (_D = options.transportFactory) != null ? _D : defaultTransportFactory,
1418
+ nameResolver: (_B = options.nameResolver) != null ? _B : defaultPacketNameResolver,
1419
+ transportFactory: (_C = options.transportFactory) != null ? _C : defaultTransportFactory,
1371
1420
  codec: options.codec,
1372
- meterProvider: options.meterProvider,
1373
1421
  // Spec 26 §4: the default diagnostics level is Errors, which preserves
1374
1422
  // the connector's established wire behavior.
1375
- diagnosticsLevel: (_E = options.diagnosticsLevel) != null ? _E : "errors" /* Errors */
1423
+ diagnosticsLevel: (_D = options.diagnosticsLevel) != null ? _D : "errors" /* Errors */
1376
1424
  };
1377
1425
  }
1426
+ var defaultPacketNameResolver = {
1427
+ resolve(payloadType) {
1428
+ const declared = payloadType.packetName;
1429
+ if (typeof declared === "string" && declared.length > 0) {
1430
+ return declared;
1431
+ }
1432
+ return payloadType.name;
1433
+ }
1434
+ };
1378
1435
  function resolveCompressionCodec2(options) {
1379
1436
  var _a;
1380
1437
  const compression = (_a = options.compression) != null ? _a : "lz4" /* Lz4 */;
@@ -1411,7 +1468,7 @@ var ZlinkStreamConnectorBundle = (() => {
1411
1468
  }
1412
1469
  }
1413
1470
  function validateReconnect(options) {
1414
- var _a, _b, _c, _d, _e;
1471
+ var _a, _b, _c, _d;
1415
1472
  const enabled = (_a = options == null ? void 0 : options.enabled) != null ? _a : true;
1416
1473
  if (!enabled) {
1417
1474
  return;
@@ -1421,7 +1478,8 @@ var ZlinkStreamConnectorBundle = (() => {
1421
1478
  if (((_d = options == null ? void 0 : options.backoffFactor) != null ? _d : 2) < 1) {
1422
1479
  throw connectorError("validationFailed" /* ValidationFailed */, "Reconnect BackoffFactor must be at least 1.0.");
1423
1480
  }
1424
- if (((_e = options == null ? void 0 : options.maxAttempts) != null ? _e : 3) <= 0) {
1481
+ const maxAttempts = (options == null ? void 0 : options.maxAttempts) === void 0 ? 3 : options.maxAttempts;
1482
+ if (maxAttempts !== null && !(Number.isFinite(maxAttempts) && maxAttempts > 0)) {
1425
1483
  throw connectorError("validationFailed" /* ValidationFailed */, "Reconnect MaxAttempts must be null or positive.");
1426
1484
  }
1427
1485
  }
@@ -1546,6 +1604,16 @@ var ZlinkStreamConnectorBundle = (() => {
1546
1604
  __publicField(this, "queueHead", 0);
1547
1605
  __publicField(this, "queuedCount", 0);
1548
1606
  __publicField(this, "drainTask");
1607
+ // True for as long as `drain` is on the stack, handler awaits included. It
1608
+ // marks the execution context a registered handler runs in, so a `dispatch`
1609
+ // made from inside a handler is recognised as re-entry rather than a fresh
1610
+ // pump. It is not a lock: a single event loop admits no second thread, and
1611
+ // nothing ever waits for this flag to fall.
1612
+ __publicField(this, "draining", false);
1613
+ // Spec stream-connector 32 §10: arrivals per packet name on the current
1614
+ // connection. It is raised where a packet arrives, never where one is taken,
1615
+ // so consuming does not lower it and the dispatch mode does not change it.
1616
+ __publicField(this, "receivedCounts", /* @__PURE__ */ new Map());
1549
1617
  }
1550
1618
  on(name, handler) {
1551
1619
  validateName(name);
@@ -1572,32 +1640,81 @@ var ZlinkStreamConnectorBundle = (() => {
1572
1640
  * `Manual` needs no dispatch pump to complete a wait. The queue is scanned in
1573
1641
  * a microtask so a message that arrived before the wait started is still
1574
1642
  * observed, and so the caller has its subscription in hand by then.
1643
+ *
1644
+ * @param onConnectionEnded Called, instead of {@link observer}, when
1645
+ * {@link resetForNewConnection} abandons this registration because the
1646
+ * connection it was watching ended before a message matched (spec
1647
+ * stream-connector 32 §10.1: "연결이 끝나 대기를 이어갈 수 없으면
1648
+ * `Disconnected`다").
1575
1649
  */
1576
- observe(name, observer) {
1650
+ observe(name, observer, onConnectionEnded) {
1577
1651
  validateName(name);
1578
1652
  let set = this.observers.get(name);
1579
1653
  if (set === void 0) {
1580
1654
  set = /* @__PURE__ */ new Set();
1581
1655
  this.observers.set(name, set);
1582
1656
  }
1583
- set.add(observer);
1657
+ const registration = { consume: observer, onConnectionEnded };
1658
+ set.add(registration);
1584
1659
  queueMicrotask(() => {
1585
1660
  var _a;
1586
- if (((_a = this.observers.get(name)) == null ? void 0 : _a.has(observer)) === true) {
1587
- this.offerQueued(name, observer);
1661
+ if (((_a = this.observers.get(name)) == null ? void 0 : _a.has(registration)) === true) {
1662
+ this.offerQueued(name, registration);
1588
1663
  }
1589
1664
  });
1590
1665
  return subscription(() => {
1591
- set.delete(observer);
1666
+ set.delete(registration);
1592
1667
  if (set.size === 0 && this.observers.get(name) === set) {
1593
1668
  this.observers.delete(name);
1594
1669
  }
1595
1670
  });
1596
1671
  }
1597
- enqueue(message, signal) {
1672
+ /** Spec stream-connector 32 §10: arrivals under `name` on this connection. */
1673
+ receivedCount(name) {
1598
1674
  var _a;
1599
- for (const observer of [...(_a = this.observers.get(message.name)) != null ? _a : []]) {
1600
- if (observer(message)) {
1675
+ return (_a = this.receivedCounts.get(name)) != null ? _a : 0;
1676
+ }
1677
+ /**
1678
+ * Rebaselines the queue for a connection that was just established. Spec
1679
+ * stream-connector 32 §10 (line ~649): the reference point is the moment a
1680
+ * connection is established, so counts restart at 0 and whatever the
1681
+ * previous connection left unconsumed goes with it — keeping the queue
1682
+ * while only the counts reset would let counts and queue describe two
1683
+ * different connections, and let `waitFor` hand back a packet from before
1684
+ * the drop as if the new connection had delivered it.
1685
+ *
1686
+ * `ZlinkStreamMessage`/`ZlinkStreamEncodedPayload` are plain data (name,
1687
+ * metadata, a `Uint8Array` payload) with no dispose/close of their own —
1688
+ * unlike Java's queued frames, which `closeMessage` releases — so dropping
1689
+ * the queue's references is the whole of the release here.
1690
+ *
1691
+ * @param replacesAnEarlierConnection False for the very first connection:
1692
+ * there is no earlier queue or wait to abandon yet. True for a reconnect,
1693
+ * which also fails every wait surface still registered from the
1694
+ * connection that just ended with `Disconnected` (Java
1695
+ * `ZLinkStreamDispatchQueue.resetForNewConnection` parity) — that
1696
+ * registration was watching a queue this call just discarded, so letting
1697
+ * it keep watching would silently rebind it to the new connection.
1698
+ */
1699
+ resetForNewConnection(replacesAnEarlierConnection) {
1700
+ this.receivedCounts.clear();
1701
+ this.queue.length = 0;
1702
+ this.queueHead = 0;
1703
+ this.queuedCount = 0;
1704
+ if (!replacesAnEarlierConnection || this.observers.size === 0) {
1705
+ return;
1706
+ }
1707
+ const abandoned = [...this.observers.values()].flatMap((set) => [...set]);
1708
+ this.observers.clear();
1709
+ for (const registration of abandoned) {
1710
+ registration.onConnectionEnded();
1711
+ }
1712
+ }
1713
+ enqueue(message, signal) {
1714
+ var _a, _b;
1715
+ this.receivedCounts.set(message.name, ((_a = this.receivedCounts.get(message.name)) != null ? _a : 0) + 1);
1716
+ for (const registration of [...(_b = this.observers.get(message.name)) != null ? _b : []]) {
1717
+ if (registration.consume(message)) {
1601
1718
  return;
1602
1719
  }
1603
1720
  }
@@ -1610,18 +1727,28 @@ var ZlinkStreamConnectorBundle = (() => {
1610
1727
  /**
1611
1728
  * Runs the registered handlers the receive path left queued. `Manual` calls
1612
1729
  * this from `dispatch`; `Immediate` has already drained on arrival.
1730
+ *
1731
+ * A handler that calls `dispatch` arrives back here from inside the drain it
1732
+ * was started by. `scheduleDrain` would find `drainTask` already set and
1733
+ * return, and the await below would then be the drain waiting on itself —
1734
+ * a deadlock with neither timeout nor error. The drain loop already takes
1735
+ * every message a handler exists for, so there is nothing a second drain
1736
+ * would deliver and returning is the whole of the correct behaviour.
1613
1737
  */
1614
1738
  async pump() {
1739
+ if (this.draining) {
1740
+ return;
1741
+ }
1615
1742
  this.scheduleDrain();
1616
1743
  await this.drainTask;
1617
1744
  }
1618
- offerQueued(name, observer) {
1745
+ offerQueued(name, registration) {
1619
1746
  for (let index = this.queueHead; index < this.queue.length; index += 1) {
1620
1747
  const queued = this.queue[index];
1621
1748
  if (queued === void 0 || queued.message.name !== name) {
1622
1749
  continue;
1623
1750
  }
1624
- if (!observer(queued.message)) {
1751
+ if (!registration.consume(queued.message)) {
1625
1752
  continue;
1626
1753
  }
1627
1754
  this.removeAt(index);
@@ -1640,23 +1767,28 @@ var ZlinkStreamConnectorBundle = (() => {
1640
1767
  });
1641
1768
  }
1642
1769
  async drain() {
1643
- for (let index = this.findDeliverableIndex(); index >= 0; index = this.findDeliverableIndex()) {
1644
- const queued = this.queue[index];
1645
- if (queued === void 0) continue;
1646
- this.removeAt(index);
1647
- const { message, signal } = queued;
1648
- const handlers = [...this.handlers.get(message.name)];
1649
- for (const handler of handlers) {
1650
- try {
1651
- await handler(message, signal);
1652
- } catch (cause) {
1653
- await this.events.publishError({
1654
- code: "userCallbackFailed" /* UserCallbackFailed */,
1655
- message: "Typed message handler failed.",
1656
- cause
1657
- }, signal);
1770
+ this.draining = true;
1771
+ try {
1772
+ for (let index = this.findDeliverableIndex(); index >= 0; index = this.findDeliverableIndex()) {
1773
+ const queued = this.queue[index];
1774
+ if (queued === void 0) continue;
1775
+ this.removeAt(index);
1776
+ const { message, signal } = queued;
1777
+ const handlers = [...this.handlers.get(message.name)];
1778
+ for (const handler of handlers) {
1779
+ try {
1780
+ await handler(message, signal);
1781
+ } catch (cause) {
1782
+ await this.events.publishError({
1783
+ code: "userCallbackFailed" /* UserCallbackFailed */,
1784
+ message: "Typed message handler failed.",
1785
+ cause
1786
+ }, signal);
1787
+ }
1658
1788
  }
1659
1789
  }
1790
+ } finally {
1791
+ this.draining = false;
1660
1792
  }
1661
1793
  }
1662
1794
  removeAt(index) {
@@ -1702,10 +1834,9 @@ var ZlinkStreamConnectorBundle = (() => {
1702
1834
 
1703
1835
  // packages/stream-connector/src/Runtime/ZlinkStreamFrameSender.ts
1704
1836
  var ZlinkStreamFrameSender = class {
1705
- constructor(protocol, flowContext, metrics) {
1837
+ constructor(protocol, flowContext) {
1706
1838
  this.protocol = protocol;
1707
1839
  this.flowContext = flowContext;
1708
- this.metrics = metrics;
1709
1840
  __publicField(this, "pendingWrites", /* @__PURE__ */ new Set());
1710
1841
  }
1711
1842
  async send(connection, kind, name, payload, metadata, compress, requestSeq, signal, correlationId, explicitFlow) {
@@ -1731,7 +1862,6 @@ var ZlinkStreamConnectorBundle = (() => {
1731
1862
  this.pendingWrites.add(write);
1732
1863
  try {
1733
1864
  await write;
1734
- this.metrics.outbound(frame.byteLength);
1735
1865
  } finally {
1736
1866
  this.pendingWrites.delete(write);
1737
1867
  }
@@ -1760,17 +1890,29 @@ var ZlinkStreamConnectorBundle = (() => {
1760
1890
 
1761
1891
  // packages/stream-connector/src/Runtime/ZlinkStreamReceiveDispatcher.ts
1762
1892
  var ZlinkStreamReceiveDispatcher = class {
1763
- constructor(protocol, pendingRequests, receivedMessages, frameSender, events, flowContext, metrics, serverClosing) {
1893
+ constructor(protocol, pendingRequests, receivedMessages, frameSender, events, flowContext, serverClosing) {
1764
1894
  this.protocol = protocol;
1765
1895
  this.pendingRequests = pendingRequests;
1766
1896
  this.receivedMessages = receivedMessages;
1767
1897
  this.frameSender = frameSender;
1768
1898
  this.events = events;
1769
1899
  this.flowContext = flowContext;
1770
- this.metrics = metrics;
1771
1900
  this.serverClosing = serverClosing;
1772
1901
  }
1773
- async readAndDispatch(connection, signal, isCurrent) {
1902
+ /**
1903
+ * @param isCurrent Tells whether the connection this batch was read from is
1904
+ * still the connector's connection. It is asked again at the head of every
1905
+ * frame, not only after the read: `dispatch` awaits application code —
1906
+ * registered handlers in `Immediate`, the error handler on a failed frame —
1907
+ * and a disconnect and reconnect can complete inside that await. Without
1908
+ * the recheck the rest of a dead connection's batch lands on the new
1909
+ * connection's state, and the §10 arrival counts, which start at 0 when a
1910
+ * connection is established, take the stale frames on top.
1911
+ * @param connectionForSend Resolves the connection a reply belongs on at the
1912
+ * moment it is written. The captured `connection` is the one the batch was
1913
+ * read from, which a reconnect may already have replaced.
1914
+ */
1915
+ async readAndDispatch(connection, signal, isCurrent, connectionForSend) {
1774
1916
  if ((connection == null ? void 0 : connection.read) === void 0) {
1775
1917
  return { available: false, inbound: false };
1776
1918
  }
@@ -1781,7 +1923,6 @@ var ZlinkStreamConnectorBundle = (() => {
1781
1923
  if (frameBytes === void 0) {
1782
1924
  return { available: false, inbound: false };
1783
1925
  }
1784
- this.metrics.inbound(frameBytes.byteLength);
1785
1926
  const flowEnabled = this.protocol.flowEnabled();
1786
1927
  let frames;
1787
1928
  try {
@@ -1794,8 +1935,11 @@ var ZlinkStreamConnectorBundle = (() => {
1794
1935
  return { available: true, inbound: false };
1795
1936
  }
1796
1937
  for (const frame of frames) {
1938
+ if (isCurrent !== void 0 && !isCurrent()) {
1939
+ break;
1940
+ }
1797
1941
  try {
1798
- await this.dispatch(connection, frame.header, frame.payload, signal, flowEnabled);
1942
+ await this.dispatch(connection, frame.header, frame.payload, signal, flowEnabled, connectionForSend);
1799
1943
  } catch (cause) {
1800
1944
  if (frame.header.kind === 5 /* Control */ && frame.header.name === ZLINK_STREAM_HEARTBEAT_PING) {
1801
1945
  throw cause;
@@ -1808,7 +1952,7 @@ var ZlinkStreamConnectorBundle = (() => {
1808
1952
  }
1809
1953
  return { available: true, inbound: true };
1810
1954
  }
1811
- async dispatch(connection, header, payload, signal, flowEnabled) {
1955
+ async dispatch(connection, header, payload, signal, flowEnabled, connectionForSend) {
1812
1956
  if (header.kind === 3 /* Response */ && header.requestSeq !== void 0) {
1813
1957
  try {
1814
1958
  if (!this.pendingRequests.resolve(header.requestSeq, {
@@ -1861,7 +2005,7 @@ var ZlinkStreamConnectorBundle = (() => {
1861
2005
  return;
1862
2006
  }
1863
2007
  if (header.kind === 5 /* Control */) {
1864
- await this.dispatchControl(connection, header, payload, signal);
2008
+ await this.dispatchControl(connection, header, payload, signal, connectionForSend);
1865
2009
  return;
1866
2010
  }
1867
2011
  if (header.kind === 1 /* Send */) {
@@ -1875,8 +2019,8 @@ var ZlinkStreamConnectorBundle = (() => {
1875
2019
  }, signal);
1876
2020
  }
1877
2021
  }
1878
- async dispatchControl(connection, header, payload, signal) {
1879
- var _a;
2022
+ async dispatchControl(connection, header, payload, signal, connectionForSend) {
2023
+ var _a, _b;
1880
2024
  if (header.name === ZLINK_SESSION_CLOSING) {
1881
2025
  const closing = decodeSessionClosing(payload);
1882
2026
  await ((_a = this.serverClosing) == null ? void 0 : _a.call(this, closing.closeReason));
@@ -1887,7 +2031,11 @@ var ZlinkStreamConnectorBundle = (() => {
1887
2031
  }
1888
2032
  if (header.name === ZLINK_STREAM_HEARTBEAT_PING) {
1889
2033
  try {
1890
- await this.frameSender.sendControl(connection, ZLINK_STREAM_HEARTBEAT_PONG, signal);
2034
+ await this.frameSender.sendControl(
2035
+ (_b = connectionForSend == null ? void 0 : connectionForSend()) != null ? _b : connection,
2036
+ ZLINK_STREAM_HEARTBEAT_PONG,
2037
+ signal
2038
+ );
1891
2039
  } catch (cause) {
1892
2040
  throw connectorError(
1893
2041
  "sendFailed" /* SendFailed */,
@@ -1920,15 +2068,17 @@ var ZlinkStreamConnectorBundle = (() => {
1920
2068
  }
1921
2069
 
1922
2070
  // packages/stream-connector/src/Runtime/ZlinkStreamConnectorLifecycle.ts
2071
+ function randomizedDelay(baseDelayMs) {
2072
+ return Math.round(baseDelayMs * (0.5 + Math.random() * 0.5));
2073
+ }
1923
2074
  var ZlinkStreamConnectorLifecycle = class {
1924
- constructor(options, pendingRequests, frameSender, receiveDispatcher, receivedMessages, events, metrics) {
2075
+ constructor(options, pendingRequests, frameSender, receiveDispatcher, receivedMessages, events) {
1925
2076
  this.options = options;
1926
2077
  this.pendingRequests = pendingRequests;
1927
2078
  this.frameSender = frameSender;
1928
2079
  this.receiveDispatcher = receiveDispatcher;
1929
2080
  this.receivedMessages = receivedMessages;
1930
2081
  this.events = events;
1931
- this.metrics = metrics;
1932
2082
  __publicField(this, "receiveLoopAbort");
1933
2083
  __publicField(this, "receiveLoopSleeping", false);
1934
2084
  __publicField(this, "receiveLoopWake");
@@ -1937,6 +2087,7 @@ var ZlinkStreamConnectorBundle = (() => {
1937
2087
  __publicField(this, "connectionGeneration", 0);
1938
2088
  __publicField(this, "currentState", "created" /* Created */);
1939
2089
  __publicField(this, "heartbeatTimer");
2090
+ __publicField(this, "heartbeatTickRunning", false);
1940
2091
  __publicField(this, "lastInboundAt", 0);
1941
2092
  __publicField(this, "closeTask");
1942
2093
  __publicField(this, "connectTask");
@@ -1974,6 +2125,7 @@ var ZlinkStreamConnectorBundle = (() => {
1974
2125
  return await this.connectTask;
1975
2126
  }
1976
2127
  async connectOnce(signal) {
2128
+ var _a;
1977
2129
  await this.setState("connecting" /* Connecting */, void 0, signal);
1978
2130
  try {
1979
2131
  const connection = await this.connectWithReconnect(signal);
@@ -1989,6 +2141,7 @@ var ZlinkStreamConnectorBundle = (() => {
1989
2141
  this.currentConnection = connection;
1990
2142
  this.connectionGeneration += 1;
1991
2143
  this.disconnectedPublished = false;
2144
+ this.receivedMessages.resetForNewConnection(this.connectionGeneration > 1);
1992
2145
  this.lastInboundAt = Date.now();
1993
2146
  await this.setState("connected" /* Connected */, void 0, signal);
1994
2147
  this.startHeartbeat();
@@ -2000,7 +2153,9 @@ var ZlinkStreamConnectorBundle = (() => {
2000
2153
  throw new ZlinkStreamException(error2);
2001
2154
  }
2002
2155
  const error = toStreamError(cause, "connectTimeout" /* ConnectTimeout */, "Connect failed.");
2156
+ (_a = this.closeReasonValue) != null ? _a : this.closeReasonValue = "TransportError";
2003
2157
  await this.setState("disconnected" /* Disconnected */, error, signal);
2158
+ await this.publishDisconnectedOnce(signal);
2004
2159
  throw new ZlinkStreamException(error);
2005
2160
  }
2006
2161
  }
@@ -2048,7 +2203,7 @@ var ZlinkStreamConnectorBundle = (() => {
2048
2203
  }
2049
2204
  this.pendingRequests.failAll({ code: "disconnected" /* Disconnected */, message: "Connector closed." });
2050
2205
  await this.setState("closed" /* Closed */, void 0, signal);
2051
- await this.publishDisconnectedOnce(signal);
2206
+ this.publishDisconnectedWithoutWaiting(signal);
2052
2207
  if (errors.length === 1) throw errors[0];
2053
2208
  if (errors.length > 1) throw new AggregateError(errors, "Stream connector close failed.");
2054
2209
  }
@@ -2079,7 +2234,8 @@ var ZlinkStreamConnectorBundle = (() => {
2079
2234
  const result = await this.receiveDispatcher.readAndDispatch(
2080
2235
  connection,
2081
2236
  signal,
2082
- () => this.isCurrentConnection(connection, generation)
2237
+ () => this.isCurrentConnection(connection, generation),
2238
+ () => this.connectionForSend()
2083
2239
  );
2084
2240
  if (result.inbound && this.isCurrentConnection(connection, generation)) {
2085
2241
  this.lastInboundAt = Date.now();
@@ -2091,25 +2247,18 @@ var ZlinkStreamConnectorBundle = (() => {
2091
2247
  let delayMs = this.options.reconnect.initialDelayMs;
2092
2248
  let lastError;
2093
2249
  const maxAttempts = this.options.reconnect.enabled ? this.options.reconnect.maxAttempts : 1;
2094
- while (attempt < maxAttempts) {
2250
+ const unlimited = maxAttempts === null;
2251
+ while (unlimited || attempt < maxAttempts) {
2095
2252
  attempt += 1;
2096
- if (attempt > 1) {
2097
- this.metrics.reconnect();
2098
- }
2099
- const handshakeStartedAt = performance.now();
2100
2253
  try {
2101
- const connection = await this.options.transportFactory.connect(this.options, signal);
2102
- this.metrics.handshakeCompleted(handshakeStartedAt);
2103
- return connection;
2254
+ return await this.options.transportFactory.connect(this.options, signal);
2104
2255
  } catch (cause) {
2105
- this.metrics.handshakeCompleted(handshakeStartedAt);
2106
- this.metrics.handshakeFailed(cause);
2107
2256
  lastError = toStreamError(cause, "connectTimeout" /* ConnectTimeout */, "Connect failed.");
2108
- if (!this.options.reconnect.enabled || attempt >= maxAttempts) {
2257
+ if (!this.options.reconnect.enabled || !unlimited && attempt >= maxAttempts) {
2109
2258
  break;
2110
2259
  }
2111
2260
  await this.setState("reconnecting" /* Reconnecting */, lastError, signal);
2112
- await delay(delayMs, signal);
2261
+ await delay(randomizedDelay(delayMs), signal);
2113
2262
  delayMs = Math.min(
2114
2263
  this.options.reconnect.maxDelayMs,
2115
2264
  Math.ceil(delayMs * this.options.reconnect.backoffFactor)
@@ -2125,15 +2274,25 @@ var ZlinkStreamConnectorBundle = (() => {
2125
2274
  if (!this.options.heartbeat.enabled) {
2126
2275
  return;
2127
2276
  }
2128
- this.heartbeatTimer = setInterval(() => {
2129
- void this.runHeartbeatTick();
2277
+ const timer = setInterval(() => {
2278
+ if (this.heartbeatTickRunning) {
2279
+ return;
2280
+ }
2281
+ this.heartbeatTickRunning = true;
2282
+ void this.runHeartbeatTick().finally(() => {
2283
+ if (this.heartbeatTimer === timer) {
2284
+ this.heartbeatTickRunning = false;
2285
+ }
2286
+ });
2130
2287
  }, this.options.heartbeat.intervalMs);
2288
+ this.heartbeatTimer = timer;
2131
2289
  }
2132
2290
  stopHeartbeat() {
2133
2291
  if (this.heartbeatTimer !== void 0) {
2134
2292
  clearInterval(this.heartbeatTimer);
2135
2293
  this.heartbeatTimer = void 0;
2136
2294
  }
2295
+ this.heartbeatTickRunning = false;
2137
2296
  }
2138
2297
  // Spec stream-connector 32 §7: the receive loop runs in both dispatch modes.
2139
2298
  // `Manual` only changes what the loop does with a frame — it queues the
@@ -2252,15 +2411,17 @@ var ZlinkStreamConnectorBundle = (() => {
2252
2411
  if (this.disconnectTask !== void 0) {
2253
2412
  return await this.disconnectTask;
2254
2413
  }
2255
- this.disconnectTask = this.disconnectOnce(error).finally(() => {
2414
+ this.disconnectTask = this.tearDownConnection(error).finally(() => {
2256
2415
  this.disconnectTask = void 0;
2257
2416
  });
2258
- return await this.disconnectTask;
2417
+ await this.disconnectTask;
2418
+ await this.announceDisconnect(error);
2259
2419
  }
2260
2420
  isCurrentConnection(connection, generation) {
2261
2421
  return !this.closeRequested && this.currentConnection === connection && this.connectionGeneration === generation;
2262
2422
  }
2263
- async disconnectOnce(error) {
2423
+ /** Transport teardown only — no application callback runs from here. */
2424
+ async tearDownConnection(error) {
2264
2425
  this.stopHeartbeat();
2265
2426
  this.stopReceiveLoop();
2266
2427
  const connection = this.currentConnection;
@@ -2270,23 +2431,75 @@ var ZlinkStreamConnectorBundle = (() => {
2270
2431
  await (connection == null ? void 0 : connection.close());
2271
2432
  } catch {
2272
2433
  }
2434
+ }
2435
+ /**
2436
+ * Runs once the teardown promise has settled, so a handler reached from here
2437
+ * may call `connect` without waiting for a task its own caller still holds.
2438
+ * The reconnect is queued before the notification is awaited for the same
2439
+ * reason: spec stream-connector 32 §6 has reconnect on by default, and a
2440
+ * handler that is slow — or whose promise never settles at all — must not
2441
+ * cost the connector the attempt. The state is already `Disconnected` and the
2442
+ * state handlers are already invoked by the time the queued microtask runs,
2443
+ * because `setState` records the state and hands the change to the handlers
2444
+ * before it awaits any of them.
2445
+ */
2446
+ async announceDisconnect(error) {
2273
2447
  if (this.closeRequested) return;
2274
- await this.setState("disconnected" /* Disconnected */, error);
2275
- await this.publishDisconnectedOnce();
2448
+ const announce = this.claimDisconnectedPublish();
2449
+ const notified = (async () => {
2450
+ await this.setState("disconnected" /* Disconnected */, error);
2451
+ if (announce) {
2452
+ await this.events.publishDisconnected();
2453
+ }
2454
+ })();
2276
2455
  if (this.shouldReconnect()) {
2277
2456
  queueMicrotask(() => {
2278
2457
  void this.connect().catch(() => void 0);
2279
2458
  });
2280
2459
  }
2460
+ await notified;
2281
2461
  }
2282
2462
  shouldReconnect() {
2283
2463
  return this.options.reconnect.enabled && !this.closeRequested;
2284
2464
  }
2285
- async publishDisconnectedOnce(signal) {
2286
- if (this.disconnectedPublished) return;
2465
+ // Synchronous test-and-set, taken before any await, so the single disconnect
2466
+ // notification the spec promises is claimed by exactly one caller even when
2467
+ // the publishing itself is deferred.
2468
+ claimDisconnectedPublish() {
2469
+ if (this.disconnectedPublished) return false;
2287
2470
  this.disconnectedPublished = true;
2471
+ return true;
2472
+ }
2473
+ async publishDisconnectedOnce(signal) {
2474
+ if (!this.claimDisconnectedPublish()) return;
2288
2475
  await this.events.publishDisconnected(signal);
2289
2476
  }
2477
+ /**
2478
+ * Spec stream-connector 32 §7: the connector runs the registered handler, it
2479
+ * does not wait for it to finish. `close` returns once its own work is done —
2480
+ * the drain, the transport close, the pending requests — having run the
2481
+ * disconnect handler but without looking at whether that handler has ended.
2482
+ *
2483
+ * Starting the publish without awaiting it still runs every handler right
2484
+ * here: an `async` function body runs synchronously up to its first `await`,
2485
+ * and `publishDisconnected` reaches each handler before that point. So the
2486
+ * handler has run by the time `close` returns, which is what the spec asks
2487
+ * for, while a handler that calls `close` no longer waits for the very
2488
+ * `closeTask` it is running inside. Java and C++ hand the handler to a queue
2489
+ * and return the same way.
2490
+ *
2491
+ * `claimDisconnectedPublish` is taken by the same call and before any await,
2492
+ * so deferring the completion never turns the one notification into two.
2493
+ *
2494
+ * Nothing awaits the promise, so a rejection would reach the process as an
2495
+ * unhandled rejection and kill it. Handler failures are already contained
2496
+ * where they were before this call stopped waiting — the `Promise.allSettled`
2497
+ * in `publishDisconnected` — and this `catch` covers what is left rather than
2498
+ * reporting the same failure a second time on the error surface.
2499
+ */
2500
+ publishDisconnectedWithoutWaiting(signal) {
2501
+ void this.publishDisconnectedOnce(signal).catch(() => void 0);
2502
+ }
2290
2503
  async setState(current, error, signal) {
2291
2504
  const previous = this.currentState;
2292
2505
  this.currentState = current;
@@ -2470,9 +2683,6 @@ var ZlinkStreamConnectorBundle = (() => {
2470
2683
  this.readWaiter = void 0;
2471
2684
  resolve();
2472
2685
  };
2473
- if (this.hasQueuedMessage() || this.closed) {
2474
- this.wakeReader();
2475
- }
2476
2686
  });
2477
2687
  }
2478
2688
  wakeReader() {
@@ -2553,66 +2763,6 @@ var ZlinkStreamConnectorBundle = (() => {
2553
2763
  throw connectorError("frameDecodeFailed" /* FrameDecodeFailed */, "WebSocket text messages are not supported.");
2554
2764
  }
2555
2765
 
2556
- // packages/stream-connector/src/Runtime/ZlinkStreamRuntimeMetrics.ts
2557
- var ZlinkStreamRuntimeMetrics = class {
2558
- constructor(options) {
2559
- this.options = options;
2560
- __publicField(this, "reconnects");
2561
- __publicField(this, "handshakeDuration");
2562
- __publicField(this, "handshakeFailures");
2563
- __publicField(this, "inboundBytes");
2564
- __publicField(this, "outboundBytes");
2565
- var _a;
2566
- const meter = (_a = options.meterProvider) == null ? void 0 : _a.getMeter("zlink.framework");
2567
- this.reconnects = meter == null ? void 0 : meter.createCounter("zlink.stream.reconnects", { unit: "{event}" });
2568
- this.handshakeDuration = meter == null ? void 0 : meter.createHistogram("zlink.stream.handshake.duration", { unit: "s" });
2569
- this.handshakeFailures = meter == null ? void 0 : meter.createCounter("zlink.stream.handshake.failures", { unit: "{failure}" });
2570
- this.inboundBytes = meter == null ? void 0 : meter.createCounter("zlink.stream.inbound.bytes", { unit: "By" });
2571
- this.outboundBytes = meter == null ? void 0 : meter.createCounter("zlink.stream.outbound.bytes", { unit: "By" });
2572
- }
2573
- reconnect() {
2574
- this.safe(() => {
2575
- var _a;
2576
- return (_a = this.reconnects) == null ? void 0 : _a.add(1, { transport: this.transportLabel() });
2577
- });
2578
- }
2579
- handshakeCompleted(startedAt) {
2580
- const seconds = (performance.now() - startedAt) / 1e3;
2581
- this.safe(() => {
2582
- var _a;
2583
- return (_a = this.handshakeDuration) == null ? void 0 : _a.record(seconds, { transport: this.transportLabel() });
2584
- });
2585
- }
2586
- handshakeFailed(error) {
2587
- const reason = error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError") ? "canceled" : "transport_error";
2588
- this.safe(() => {
2589
- var _a;
2590
- return (_a = this.handshakeFailures) == null ? void 0 : _a.add(1, { transport: this.transportLabel(), reason });
2591
- });
2592
- }
2593
- inbound(byteCount) {
2594
- this.safe(() => {
2595
- var _a;
2596
- return (_a = this.inboundBytes) == null ? void 0 : _a.add(byteCount, { transport: this.transportLabel() });
2597
- });
2598
- }
2599
- outbound(byteCount) {
2600
- this.safe(() => {
2601
- var _a;
2602
- return (_a = this.outboundBytes) == null ? void 0 : _a.add(byteCount, { transport: this.transportLabel() });
2603
- });
2604
- }
2605
- transportLabel() {
2606
- return this.options.endpoint.startsWith("wss:") ? "wss" : "ws";
2607
- }
2608
- safe(record) {
2609
- try {
2610
- record();
2611
- } catch {
2612
- }
2613
- }
2614
- };
2615
-
2616
2766
  // packages/stream-connector/src/Runtime/ZlinkStreamConnector.ts
2617
2767
  var DefaultZlinkStreamConnector = class {
2618
2768
  constructor(options) {
@@ -2633,9 +2783,8 @@ var ZlinkStreamConnectorBundle = (() => {
2633
2783
  configurable: true,
2634
2784
  get: () => this.diagnosticsLevelCell.level
2635
2785
  });
2636
- const metrics = new ZlinkStreamRuntimeMetrics(this.options);
2637
2786
  const protocol = new ZlinkStreamFrameProtocol(this.options);
2638
- this.frameSender = new ZlinkStreamFrameSender(protocol, flowContext, metrics);
2787
+ this.frameSender = new ZlinkStreamFrameSender(protocol, flowContext);
2639
2788
  this.receivedMessages = new ZlinkStreamReceivedMessages(
2640
2789
  this.events,
2641
2790
  this.options.dispatchMode === "immediate" /* Immediate */
@@ -2647,7 +2796,6 @@ var ZlinkStreamConnectorBundle = (() => {
2647
2796
  this.frameSender,
2648
2797
  this.events,
2649
2798
  flowContext,
2650
- metrics,
2651
2799
  (reason) => this.lifecycle.serverClosing(reason)
2652
2800
  );
2653
2801
  this.lifecycle = new ZlinkStreamConnectorLifecycle(
@@ -2656,8 +2804,7 @@ var ZlinkStreamConnectorBundle = (() => {
2656
2804
  this.frameSender,
2657
2805
  this.receiveDispatcher,
2658
2806
  this.receivedMessages,
2659
- this.events,
2660
- metrics
2807
+ this.events
2661
2808
  );
2662
2809
  }
2663
2810
  get isConnected() {
@@ -2672,6 +2819,17 @@ var ZlinkStreamConnectorBundle = (() => {
2672
2819
  get pendingDispatchCount() {
2673
2820
  return this.pendingRequests.count;
2674
2821
  }
2822
+ /**
2823
+ * Spec stream-connector 32 §10: how many packets carrying `name` arrived on
2824
+ * the current connection. Arrivals are what is counted, so a message a
2825
+ * handler dispatched or a wait surface consumed still counts, and `Manual`
2826
+ * and `Immediate` report the same number. Establishing a connection resets
2827
+ * the count to 0, a reconnect included.
2828
+ */
2829
+ receivedCount(name) {
2830
+ validateName(name);
2831
+ return this.receivedMessages.receivedCount(name);
2832
+ }
2675
2833
  /**
2676
2834
  * Current diagnostics level (spec 26 §4.1, spec stream-connector 32 §13).
2677
2835
  * Reflects the level set by the most recent {@link setDiagnosticsLevel}
@@ -2687,14 +2845,20 @@ var ZlinkStreamConnectorBundle = (() => {
2687
2845
  * state update: it applies to processing points that read the level after
2688
2846
  * this call returns and is never applied retroactively to frames already
2689
2847
  * built. Rejects unknown values with {@link ZlinkStreamErrorCode.ConfigurationError}.
2690
- * Do not call this synchronous bridge from a framework execution context such
2691
- * as a handler or callback; use setDiagnosticsLevelAsync there.
2848
+ * Spec stream-connector 32 §13: this surface changes the value without
2849
+ * waiting for anything; it is not a blocking call over the asynchronous pair,
2850
+ * which a receive callback would otherwise make wait for its own completion.
2692
2851
  */
2693
2852
  setDiagnosticsLevel(level) {
2694
- void this.setDiagnosticsLevelAsync(level);
2853
+ this.diagnosticsLevelCell.set(level);
2695
2854
  }
2855
+ /**
2856
+ * Asynchronous counterpart of {@link setDiagnosticsLevel} (spec
2857
+ * stream-connector 32 §13). It changes the same value; awaiting it is how a
2858
+ * caller observes the change, and it never replaces the synchronous surface.
2859
+ */
2696
2860
  setDiagnosticsLevelAsync(level) {
2697
- this.diagnosticsLevelCell.set(level);
2861
+ this.setDiagnosticsLevel(level);
2698
2862
  return Promise.resolve();
2699
2863
  }
2700
2864
  onErrorReceived(handler) {
@@ -2733,17 +2897,28 @@ var ZlinkStreamConnectorBundle = (() => {
2733
2897
  }, signal);
2734
2898
  return this.receivedMessages.on(name, encodedHandler);
2735
2899
  }
2736
- waitFor(name) {
2737
- validateName(name);
2738
- return new ZlinkStreamWaitBuilder(this, name);
2900
+ waitFor(nameOrType) {
2901
+ return new ZlinkStreamWaitBuilder(this, this.observedName(nameOrType));
2739
2902
  }
2740
- expectNone(name) {
2741
- validateName(name);
2742
- return new ZlinkStreamExpectNoneBuilder(this, name);
2903
+ expectNone(nameOrType) {
2904
+ return new ZlinkStreamExpectNoneBuilder(this, this.observedName(nameOrType));
2905
+ }
2906
+ waitForSequence(nameOrType) {
2907
+ return new ZlinkStreamSequenceBuilder(this, this.observedName(nameOrType));
2743
2908
  }
2744
- waitForSequence(name) {
2909
+ /**
2910
+ * Spec stream-connector 32 §10.1: each wait surface offers both ways of
2911
+ * naming a packet. A string is the name the caller states; a constructor
2912
+ * goes through the options' `nameResolver`, which is the same resolver
2913
+ * `send` and `request` use, so both paths land on one name for one type.
2914
+ */
2915
+ observedName(nameOrType) {
2916
+ const name = typeof nameOrType === "function" ? this.options.nameResolver.resolve(nameOrType) : nameOrType;
2917
+ if (typeof name !== "string") {
2918
+ throw connectorError("validationFailed" /* ValidationFailed */, "Packet name must be a string or a payload constructor.");
2919
+ }
2745
2920
  validateName(name);
2746
- return new ZlinkStreamSequenceBuilder(this, name);
2921
+ return name;
2747
2922
  }
2748
2923
  waitForMessage(name, timeoutMs, predicate, signal) {
2749
2924
  validateName(name);
@@ -2796,7 +2971,10 @@ var ZlinkStreamConnectorBundle = (() => {
2796
2971
  finish(cause);
2797
2972
  }
2798
2973
  return true;
2799
- });
2974
+ }, () => finish(connectorError(
2975
+ "disconnected" /* Disconnected */,
2976
+ `The connection this wait for '${name}' observed has ended.`
2977
+ )));
2800
2978
  });
2801
2979
  }
2802
2980
  encodePayload(payload, messageType) {
@@ -2880,50 +3058,6 @@ var ZlinkStreamConnectorBundle = (() => {
2880
3058
  return typeof candidate.codec === "number" && candidate.payload instanceof Uint8Array;
2881
3059
  }
2882
3060
 
2883
- // packages/stream-connector/src/Runtime/ZlinkStreamAssertions.ts
2884
- var zlinkStreamAssert = {
2885
- ensure(condition, message) {
2886
- if (!condition) {
2887
- throw connectorError("validationFailed" /* ValidationFailed */, message);
2888
- }
2889
- },
2890
- async expectFailure(action, errorKind) {
2891
- let failure;
2892
- try {
2893
- await action();
2894
- } catch (error) {
2895
- failure = error;
2896
- }
2897
- if (failure === void 0) {
2898
- throw connectorError("validationFailed" /* ValidationFailed */, "Expected action to fail.");
2899
- }
2900
- const streamError = unwrapStreamError(failure);
2901
- if (errorKind !== void 0 && streamError.code !== errorKind) {
2902
- throw connectorError(
2903
- "validationFailed" /* ValidationFailed */,
2904
- `Expected failure kind '${errorKind}', got '${streamError.code}'.`,
2905
- failure
2906
- );
2907
- }
2908
- return streamError;
2909
- },
2910
- async expectTimeout(action) {
2911
- let failure;
2912
- try {
2913
- await action();
2914
- } catch (error) {
2915
- failure = error;
2916
- }
2917
- if (failure === void 0) {
2918
- throw connectorError("validationFailed" /* ValidationFailed */, "Expected action to time out.");
2919
- }
2920
- const code = unwrapStreamError(failure).code;
2921
- if (code !== "requestTimeout" /* RequestTimeout */ && code !== "connectTimeout" /* ConnectTimeout */) {
2922
- throw failure;
2923
- }
2924
- }
2925
- };
2926
-
2927
3061
  // packages/stream-connector/src/index.ts
2928
3062
  var zlinkStreamConnectorFactory = {
2929
3063
  create(options) {