@zlink-systems/stream-connector 0.17.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.
- package/dist/browser/Contracts/Calls/ZlinkStreamCalls.d.ts +8 -2
- package/dist/browser/Contracts/IZlinkStreamConnector.d.ts +24 -3
- package/dist/browser/Contracts/ZlinkStreamConnectorOptions.d.ts +5 -17
- package/dist/browser/Runtime/Calls/ZlinkStreamObservationBuilders.d.ts +2 -2
- package/dist/browser/Runtime/ZlinkStreamConnector.d.ts +26 -5
- package/dist/browser/Runtime/ZlinkStreamConnectorLifecycle.d.ts +41 -4
- package/dist/browser/Runtime/ZlinkStreamConnectorOptions.d.ts +15 -1
- package/dist/browser/Runtime/ZlinkStreamFrameSender.d.ts +1 -3
- package/dist/browser/Runtime/ZlinkStreamReceiveDispatcher.d.ts +15 -4
- package/dist/browser/Runtime/ZlinkStreamReceivedMessages.d.ts +41 -1
- package/dist/browser/index.d.ts +9 -4
- package/dist/browser/index.global.js +340 -206
- package/dist/browser/index.mjs +328 -180
- package/package.json +2 -2
- package/dist/browser/Runtime/ZlinkStreamRuntimeMetrics.d.ts +0 -17
|
@@ -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
|
|
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
|
|
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
|
-
|
|
1025
|
+
messages.push(message);
|
|
977
1026
|
}
|
|
978
|
-
return
|
|
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("
|
|
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
|
|
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: (
|
|
1411
|
+
maxAttempts: ((_w = options.reconnect) == null ? void 0 : _w.maxAttempts) === void 0 ? 3 : options.reconnect.maxAttempts
|
|
1363
1412
|
},
|
|
1364
|
-
maxSendPayloadSize: (
|
|
1365
|
-
maxReceivePayloadSize: (
|
|
1366
|
-
dispatchMode: (
|
|
1367
|
-
compression: (
|
|
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: (
|
|
1370
|
-
transportFactory: (
|
|
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: (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
1587
|
-
this.offerQueued(name,
|
|
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(
|
|
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
|
-
|
|
1672
|
+
/** Spec stream-connector 32 §10: arrivals under `name` on this connection. */
|
|
1673
|
+
receivedCount(name) {
|
|
1598
1674
|
var _a;
|
|
1599
|
-
|
|
1600
|
-
|
|
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,
|
|
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 (!
|
|
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
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2129
|
-
|
|
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.
|
|
2414
|
+
this.disconnectTask = this.tearDownConnection(error).finally(() => {
|
|
2256
2415
|
this.disconnectTask = void 0;
|
|
2257
2416
|
});
|
|
2258
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2275
|
-
|
|
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
|
-
|
|
2286
|
-
|
|
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
|
|
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
|
-
*
|
|
2691
|
-
*
|
|
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
|
-
|
|
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.
|
|
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(
|
|
2737
|
-
|
|
2738
|
-
return new ZlinkStreamWaitBuilder(this, name);
|
|
2900
|
+
waitFor(nameOrType) {
|
|
2901
|
+
return new ZlinkStreamWaitBuilder(this, this.observedName(nameOrType));
|
|
2739
2902
|
}
|
|
2740
|
-
expectNone(
|
|
2741
|
-
|
|
2742
|
-
|
|
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
|
-
|
|
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
|
|
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) {
|