@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
package/dist/browser/index.mjs
CHANGED
|
@@ -659,6 +659,53 @@ function utf8Decode2(value) {
|
|
|
659
659
|
return new TextDecoder().decode(value);
|
|
660
660
|
}
|
|
661
661
|
|
|
662
|
+
// packages/stream-connector/src/Runtime/ZlinkStreamAssertions.ts
|
|
663
|
+
var zlinkStreamAssert = {
|
|
664
|
+
ensure(condition, message) {
|
|
665
|
+
if (typeof message !== "string" || message.trim().length === 0) {
|
|
666
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "zlinkStreamAssert.ensure requires a non-empty diagnostic message.");
|
|
667
|
+
}
|
|
668
|
+
if (!condition) {
|
|
669
|
+
throw connectorError("validationFailed" /* ValidationFailed */, message);
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
async expectFailure(action, errorKind) {
|
|
673
|
+
let failure;
|
|
674
|
+
try {
|
|
675
|
+
await action();
|
|
676
|
+
} catch (error) {
|
|
677
|
+
failure = error;
|
|
678
|
+
}
|
|
679
|
+
if (failure === void 0) {
|
|
680
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "Expected action to fail.");
|
|
681
|
+
}
|
|
682
|
+
const streamError = unwrapStreamError(failure);
|
|
683
|
+
if (errorKind !== void 0 && streamError.code !== errorKind) {
|
|
684
|
+
throw connectorError(
|
|
685
|
+
"validationFailed" /* ValidationFailed */,
|
|
686
|
+
`Expected failure kind '${errorKind}', got '${streamError.code}'.`,
|
|
687
|
+
failure
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
return streamError;
|
|
691
|
+
},
|
|
692
|
+
async expectTimeout(action) {
|
|
693
|
+
let failure;
|
|
694
|
+
try {
|
|
695
|
+
await action();
|
|
696
|
+
} catch (error) {
|
|
697
|
+
failure = error;
|
|
698
|
+
}
|
|
699
|
+
if (failure === void 0) {
|
|
700
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "Expected action to time out.");
|
|
701
|
+
}
|
|
702
|
+
const code = unwrapStreamError(failure).code;
|
|
703
|
+
if (code !== "requestTimeout" /* RequestTimeout */ && code !== "connectTimeout" /* ConnectTimeout */) {
|
|
704
|
+
throw failure;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
|
|
662
709
|
// packages/stream-connector/src/Runtime/Protocol/ZlinkStreamPacketNameValidator.ts
|
|
663
710
|
function validateName(name, allowReserved = false) {
|
|
664
711
|
if (name.length === 0) {
|
|
@@ -895,6 +942,9 @@ var ZlinkStreamSequenceBuilder = class {
|
|
|
895
942
|
this.timeoutMs = timeoutMs;
|
|
896
943
|
return this;
|
|
897
944
|
}
|
|
945
|
+
// Spec stream-connector 32 §10.1: the predicate reads the whole message, and
|
|
946
|
+
// the call answers with the messages themselves, so a caller can assert on the
|
|
947
|
+
// packet name and the metadata and not only on the payload.
|
|
898
948
|
async run(signal) {
|
|
899
949
|
this.markExecuted();
|
|
900
950
|
if (this.predicates.length === 0) {
|
|
@@ -902,13 +952,13 @@ var ZlinkStreamSequenceBuilder = class {
|
|
|
902
952
|
}
|
|
903
953
|
const timeoutMs = this.timeoutMs ?? this.connector.options.waitTimeoutMs;
|
|
904
954
|
const deadline = Date.now() + timeoutMs;
|
|
905
|
-
const
|
|
955
|
+
const messages = [];
|
|
906
956
|
for (const predicate of this.predicates) {
|
|
907
957
|
const message = await this.connector.waitForMessage(
|
|
908
958
|
this.name,
|
|
909
959
|
Math.max(0, deadline - Date.now()),
|
|
910
960
|
(candidate) => {
|
|
911
|
-
if (!predicate(candidate
|
|
961
|
+
if (!predicate(candidate)) {
|
|
912
962
|
throw connectorError(
|
|
913
963
|
"validationFailed" /* ValidationFailed */,
|
|
914
964
|
`Message '${this.name}' arrived out of the expected sequence.`
|
|
@@ -918,9 +968,9 @@ var ZlinkStreamSequenceBuilder = class {
|
|
|
918
968
|
},
|
|
919
969
|
signal
|
|
920
970
|
);
|
|
921
|
-
|
|
971
|
+
messages.push(message);
|
|
922
972
|
}
|
|
923
|
-
return
|
|
973
|
+
return messages;
|
|
924
974
|
}
|
|
925
975
|
ensureConfigurable() {
|
|
926
976
|
if (this.executed) {
|
|
@@ -1021,7 +1071,7 @@ function splitZlinkStreamFrames(chunk) {
|
|
|
1021
1071
|
}
|
|
1022
1072
|
function validatePayload(payloadLength, maxPayloadSize) {
|
|
1023
1073
|
if (payloadLength > maxPayloadSize) {
|
|
1024
|
-
throw connectorError("
|
|
1074
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "Payload exceeds MaxSendPayloadSize.");
|
|
1025
1075
|
}
|
|
1026
1076
|
}
|
|
1027
1077
|
|
|
@@ -1303,22 +1353,30 @@ function normalizeOptions(options, defaultTransportFactory) {
|
|
|
1303
1353
|
initialDelayMs: options.reconnect?.initialDelayMs ?? 250,
|
|
1304
1354
|
maxDelayMs: options.reconnect?.maxDelayMs ?? 5e3,
|
|
1305
1355
|
backoffFactor: options.reconnect?.backoffFactor ?? 2,
|
|
1306
|
-
maxAttempts: options.reconnect?.maxAttempts
|
|
1356
|
+
maxAttempts: options.reconnect?.maxAttempts === void 0 ? 3 : options.reconnect.maxAttempts
|
|
1307
1357
|
},
|
|
1308
1358
|
maxSendPayloadSize: options.maxSendPayloadSize ?? 64 * 1024,
|
|
1309
1359
|
maxReceivePayloadSize: options.maxReceivePayloadSize ?? 64 * 1024,
|
|
1310
1360
|
dispatchMode: options.dispatchMode ?? "manual" /* Manual */,
|
|
1311
1361
|
compression: options.compression ?? "lz4" /* Lz4 */,
|
|
1312
1362
|
compressionCodec: resolveCompressionCodec2(options),
|
|
1313
|
-
nameResolver: options.nameResolver ??
|
|
1363
|
+
nameResolver: options.nameResolver ?? defaultPacketNameResolver,
|
|
1314
1364
|
transportFactory: options.transportFactory ?? defaultTransportFactory,
|
|
1315
1365
|
codec: options.codec,
|
|
1316
|
-
meterProvider: options.meterProvider,
|
|
1317
1366
|
// Spec 26 §4: the default diagnostics level is Errors, which preserves
|
|
1318
1367
|
// the connector's established wire behavior.
|
|
1319
1368
|
diagnosticsLevel: options.diagnosticsLevel ?? "errors" /* Errors */
|
|
1320
1369
|
};
|
|
1321
1370
|
}
|
|
1371
|
+
var defaultPacketNameResolver = {
|
|
1372
|
+
resolve(payloadType) {
|
|
1373
|
+
const declared = payloadType.packetName;
|
|
1374
|
+
if (typeof declared === "string" && declared.length > 0) {
|
|
1375
|
+
return declared;
|
|
1376
|
+
}
|
|
1377
|
+
return payloadType.name;
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1322
1380
|
function resolveCompressionCodec2(options) {
|
|
1323
1381
|
const compression = options.compression ?? "lz4" /* Lz4 */;
|
|
1324
1382
|
if (compression === "none" /* None */) {
|
|
@@ -1362,7 +1420,8 @@ function validateReconnect(options) {
|
|
|
1362
1420
|
if ((options?.backoffFactor ?? 2) < 1) {
|
|
1363
1421
|
throw connectorError("validationFailed" /* ValidationFailed */, "Reconnect BackoffFactor must be at least 1.0.");
|
|
1364
1422
|
}
|
|
1365
|
-
|
|
1423
|
+
const maxAttempts = options?.maxAttempts === void 0 ? 3 : options.maxAttempts;
|
|
1424
|
+
if (maxAttempts !== null && !(Number.isFinite(maxAttempts) && maxAttempts > 0)) {
|
|
1366
1425
|
throw connectorError("validationFailed" /* ValidationFailed */, "Reconnect MaxAttempts must be null or positive.");
|
|
1367
1426
|
}
|
|
1368
1427
|
}
|
|
@@ -1486,6 +1545,16 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1486
1545
|
queueHead = 0;
|
|
1487
1546
|
queuedCount = 0;
|
|
1488
1547
|
drainTask;
|
|
1548
|
+
// True for as long as `drain` is on the stack, handler awaits included. It
|
|
1549
|
+
// marks the execution context a registered handler runs in, so a `dispatch`
|
|
1550
|
+
// made from inside a handler is recognised as re-entry rather than a fresh
|
|
1551
|
+
// pump. It is not a lock: a single event loop admits no second thread, and
|
|
1552
|
+
// nothing ever waits for this flag to fall.
|
|
1553
|
+
draining = false;
|
|
1554
|
+
// Spec stream-connector 32 §10: arrivals per packet name on the current
|
|
1555
|
+
// connection. It is raised where a packet arrives, never where one is taken,
|
|
1556
|
+
// so consuming does not lower it and the dispatch mode does not change it.
|
|
1557
|
+
receivedCounts = /* @__PURE__ */ new Map();
|
|
1489
1558
|
on(name, handler) {
|
|
1490
1559
|
validateName(name);
|
|
1491
1560
|
let set = this.handlers.get(name);
|
|
@@ -1511,30 +1580,78 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1511
1580
|
* `Manual` needs no dispatch pump to complete a wait. The queue is scanned in
|
|
1512
1581
|
* a microtask so a message that arrived before the wait started is still
|
|
1513
1582
|
* observed, and so the caller has its subscription in hand by then.
|
|
1583
|
+
*
|
|
1584
|
+
* @param onConnectionEnded Called, instead of {@link observer}, when
|
|
1585
|
+
* {@link resetForNewConnection} abandons this registration because the
|
|
1586
|
+
* connection it was watching ended before a message matched (spec
|
|
1587
|
+
* stream-connector 32 §10.1: "연결이 끝나 대기를 이어갈 수 없으면
|
|
1588
|
+
* `Disconnected`다").
|
|
1514
1589
|
*/
|
|
1515
|
-
observe(name, observer) {
|
|
1590
|
+
observe(name, observer, onConnectionEnded) {
|
|
1516
1591
|
validateName(name);
|
|
1517
1592
|
let set = this.observers.get(name);
|
|
1518
1593
|
if (set === void 0) {
|
|
1519
1594
|
set = /* @__PURE__ */ new Set();
|
|
1520
1595
|
this.observers.set(name, set);
|
|
1521
1596
|
}
|
|
1522
|
-
|
|
1597
|
+
const registration = { consume: observer, onConnectionEnded };
|
|
1598
|
+
set.add(registration);
|
|
1523
1599
|
queueMicrotask(() => {
|
|
1524
|
-
if (this.observers.get(name)?.has(
|
|
1525
|
-
this.offerQueued(name,
|
|
1600
|
+
if (this.observers.get(name)?.has(registration) === true) {
|
|
1601
|
+
this.offerQueued(name, registration);
|
|
1526
1602
|
}
|
|
1527
1603
|
});
|
|
1528
1604
|
return subscription(() => {
|
|
1529
|
-
set.delete(
|
|
1605
|
+
set.delete(registration);
|
|
1530
1606
|
if (set.size === 0 && this.observers.get(name) === set) {
|
|
1531
1607
|
this.observers.delete(name);
|
|
1532
1608
|
}
|
|
1533
1609
|
});
|
|
1534
1610
|
}
|
|
1611
|
+
/** Spec stream-connector 32 §10: arrivals under `name` on this connection. */
|
|
1612
|
+
receivedCount(name) {
|
|
1613
|
+
return this.receivedCounts.get(name) ?? 0;
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Rebaselines the queue for a connection that was just established. Spec
|
|
1617
|
+
* stream-connector 32 §10 (line ~649): the reference point is the moment a
|
|
1618
|
+
* connection is established, so counts restart at 0 and whatever the
|
|
1619
|
+
* previous connection left unconsumed goes with it — keeping the queue
|
|
1620
|
+
* while only the counts reset would let counts and queue describe two
|
|
1621
|
+
* different connections, and let `waitFor` hand back a packet from before
|
|
1622
|
+
* the drop as if the new connection had delivered it.
|
|
1623
|
+
*
|
|
1624
|
+
* `ZlinkStreamMessage`/`ZlinkStreamEncodedPayload` are plain data (name,
|
|
1625
|
+
* metadata, a `Uint8Array` payload) with no dispose/close of their own —
|
|
1626
|
+
* unlike Java's queued frames, which `closeMessage` releases — so dropping
|
|
1627
|
+
* the queue's references is the whole of the release here.
|
|
1628
|
+
*
|
|
1629
|
+
* @param replacesAnEarlierConnection False for the very first connection:
|
|
1630
|
+
* there is no earlier queue or wait to abandon yet. True for a reconnect,
|
|
1631
|
+
* which also fails every wait surface still registered from the
|
|
1632
|
+
* connection that just ended with `Disconnected` (Java
|
|
1633
|
+
* `ZLinkStreamDispatchQueue.resetForNewConnection` parity) — that
|
|
1634
|
+
* registration was watching a queue this call just discarded, so letting
|
|
1635
|
+
* it keep watching would silently rebind it to the new connection.
|
|
1636
|
+
*/
|
|
1637
|
+
resetForNewConnection(replacesAnEarlierConnection) {
|
|
1638
|
+
this.receivedCounts.clear();
|
|
1639
|
+
this.queue.length = 0;
|
|
1640
|
+
this.queueHead = 0;
|
|
1641
|
+
this.queuedCount = 0;
|
|
1642
|
+
if (!replacesAnEarlierConnection || this.observers.size === 0) {
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
const abandoned = [...this.observers.values()].flatMap((set) => [...set]);
|
|
1646
|
+
this.observers.clear();
|
|
1647
|
+
for (const registration of abandoned) {
|
|
1648
|
+
registration.onConnectionEnded();
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1535
1651
|
enqueue(message, signal) {
|
|
1536
|
-
|
|
1537
|
-
|
|
1652
|
+
this.receivedCounts.set(message.name, (this.receivedCounts.get(message.name) ?? 0) + 1);
|
|
1653
|
+
for (const registration of [...this.observers.get(message.name) ?? []]) {
|
|
1654
|
+
if (registration.consume(message)) {
|
|
1538
1655
|
return;
|
|
1539
1656
|
}
|
|
1540
1657
|
}
|
|
@@ -1547,18 +1664,28 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1547
1664
|
/**
|
|
1548
1665
|
* Runs the registered handlers the receive path left queued. `Manual` calls
|
|
1549
1666
|
* this from `dispatch`; `Immediate` has already drained on arrival.
|
|
1667
|
+
*
|
|
1668
|
+
* A handler that calls `dispatch` arrives back here from inside the drain it
|
|
1669
|
+
* was started by. `scheduleDrain` would find `drainTask` already set and
|
|
1670
|
+
* return, and the await below would then be the drain waiting on itself —
|
|
1671
|
+
* a deadlock with neither timeout nor error. The drain loop already takes
|
|
1672
|
+
* every message a handler exists for, so there is nothing a second drain
|
|
1673
|
+
* would deliver and returning is the whole of the correct behaviour.
|
|
1550
1674
|
*/
|
|
1551
1675
|
async pump() {
|
|
1676
|
+
if (this.draining) {
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1552
1679
|
this.scheduleDrain();
|
|
1553
1680
|
await this.drainTask;
|
|
1554
1681
|
}
|
|
1555
|
-
offerQueued(name,
|
|
1682
|
+
offerQueued(name, registration) {
|
|
1556
1683
|
for (let index = this.queueHead; index < this.queue.length; index += 1) {
|
|
1557
1684
|
const queued = this.queue[index];
|
|
1558
1685
|
if (queued === void 0 || queued.message.name !== name) {
|
|
1559
1686
|
continue;
|
|
1560
1687
|
}
|
|
1561
|
-
if (!
|
|
1688
|
+
if (!registration.consume(queued.message)) {
|
|
1562
1689
|
continue;
|
|
1563
1690
|
}
|
|
1564
1691
|
this.removeAt(index);
|
|
@@ -1577,23 +1704,28 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1577
1704
|
});
|
|
1578
1705
|
}
|
|
1579
1706
|
async drain() {
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1707
|
+
this.draining = true;
|
|
1708
|
+
try {
|
|
1709
|
+
for (let index = this.findDeliverableIndex(); index >= 0; index = this.findDeliverableIndex()) {
|
|
1710
|
+
const queued = this.queue[index];
|
|
1711
|
+
if (queued === void 0) continue;
|
|
1712
|
+
this.removeAt(index);
|
|
1713
|
+
const { message, signal } = queued;
|
|
1714
|
+
const handlers = [...this.handlers.get(message.name)];
|
|
1715
|
+
for (const handler of handlers) {
|
|
1716
|
+
try {
|
|
1717
|
+
await handler(message, signal);
|
|
1718
|
+
} catch (cause) {
|
|
1719
|
+
await this.events.publishError({
|
|
1720
|
+
code: "userCallbackFailed" /* UserCallbackFailed */,
|
|
1721
|
+
message: "Typed message handler failed.",
|
|
1722
|
+
cause
|
|
1723
|
+
}, signal);
|
|
1724
|
+
}
|
|
1595
1725
|
}
|
|
1596
1726
|
}
|
|
1727
|
+
} finally {
|
|
1728
|
+
this.draining = false;
|
|
1597
1729
|
}
|
|
1598
1730
|
}
|
|
1599
1731
|
removeAt(index) {
|
|
@@ -1637,10 +1769,9 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1637
1769
|
|
|
1638
1770
|
// packages/stream-connector/src/Runtime/ZlinkStreamFrameSender.ts
|
|
1639
1771
|
var ZlinkStreamFrameSender = class {
|
|
1640
|
-
constructor(protocol, flowContext
|
|
1772
|
+
constructor(protocol, flowContext) {
|
|
1641
1773
|
this.protocol = protocol;
|
|
1642
1774
|
this.flowContext = flowContext;
|
|
1643
|
-
this.metrics = metrics;
|
|
1644
1775
|
}
|
|
1645
1776
|
pendingWrites = /* @__PURE__ */ new Set();
|
|
1646
1777
|
async send(connection, kind, name, payload, metadata, compress, requestSeq, signal, correlationId, explicitFlow) {
|
|
@@ -1666,7 +1797,6 @@ var ZlinkStreamFrameSender = class {
|
|
|
1666
1797
|
this.pendingWrites.add(write);
|
|
1667
1798
|
try {
|
|
1668
1799
|
await write;
|
|
1669
|
-
this.metrics.outbound(frame.byteLength);
|
|
1670
1800
|
} finally {
|
|
1671
1801
|
this.pendingWrites.delete(write);
|
|
1672
1802
|
}
|
|
@@ -1695,17 +1825,29 @@ function decodeSessionClosing(payload) {
|
|
|
1695
1825
|
|
|
1696
1826
|
// packages/stream-connector/src/Runtime/ZlinkStreamReceiveDispatcher.ts
|
|
1697
1827
|
var ZlinkStreamReceiveDispatcher = class {
|
|
1698
|
-
constructor(protocol, pendingRequests, receivedMessages, frameSender, events, flowContext,
|
|
1828
|
+
constructor(protocol, pendingRequests, receivedMessages, frameSender, events, flowContext, serverClosing) {
|
|
1699
1829
|
this.protocol = protocol;
|
|
1700
1830
|
this.pendingRequests = pendingRequests;
|
|
1701
1831
|
this.receivedMessages = receivedMessages;
|
|
1702
1832
|
this.frameSender = frameSender;
|
|
1703
1833
|
this.events = events;
|
|
1704
1834
|
this.flowContext = flowContext;
|
|
1705
|
-
this.metrics = metrics;
|
|
1706
1835
|
this.serverClosing = serverClosing;
|
|
1707
1836
|
}
|
|
1708
|
-
|
|
1837
|
+
/**
|
|
1838
|
+
* @param isCurrent Tells whether the connection this batch was read from is
|
|
1839
|
+
* still the connector's connection. It is asked again at the head of every
|
|
1840
|
+
* frame, not only after the read: `dispatch` awaits application code —
|
|
1841
|
+
* registered handlers in `Immediate`, the error handler on a failed frame —
|
|
1842
|
+
* and a disconnect and reconnect can complete inside that await. Without
|
|
1843
|
+
* the recheck the rest of a dead connection's batch lands on the new
|
|
1844
|
+
* connection's state, and the §10 arrival counts, which start at 0 when a
|
|
1845
|
+
* connection is established, take the stale frames on top.
|
|
1846
|
+
* @param connectionForSend Resolves the connection a reply belongs on at the
|
|
1847
|
+
* moment it is written. The captured `connection` is the one the batch was
|
|
1848
|
+
* read from, which a reconnect may already have replaced.
|
|
1849
|
+
*/
|
|
1850
|
+
async readAndDispatch(connection, signal, isCurrent, connectionForSend) {
|
|
1709
1851
|
if (connection?.read === void 0) {
|
|
1710
1852
|
return { available: false, inbound: false };
|
|
1711
1853
|
}
|
|
@@ -1716,7 +1858,6 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
1716
1858
|
if (frameBytes === void 0) {
|
|
1717
1859
|
return { available: false, inbound: false };
|
|
1718
1860
|
}
|
|
1719
|
-
this.metrics.inbound(frameBytes.byteLength);
|
|
1720
1861
|
const flowEnabled = this.protocol.flowEnabled();
|
|
1721
1862
|
let frames;
|
|
1722
1863
|
try {
|
|
@@ -1729,8 +1870,11 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
1729
1870
|
return { available: true, inbound: false };
|
|
1730
1871
|
}
|
|
1731
1872
|
for (const frame of frames) {
|
|
1873
|
+
if (isCurrent !== void 0 && !isCurrent()) {
|
|
1874
|
+
break;
|
|
1875
|
+
}
|
|
1732
1876
|
try {
|
|
1733
|
-
await this.dispatch(connection, frame.header, frame.payload, signal, flowEnabled);
|
|
1877
|
+
await this.dispatch(connection, frame.header, frame.payload, signal, flowEnabled, connectionForSend);
|
|
1734
1878
|
} catch (cause) {
|
|
1735
1879
|
if (frame.header.kind === 5 /* Control */ && frame.header.name === ZLINK_STREAM_HEARTBEAT_PING) {
|
|
1736
1880
|
throw cause;
|
|
@@ -1743,7 +1887,7 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
1743
1887
|
}
|
|
1744
1888
|
return { available: true, inbound: true };
|
|
1745
1889
|
}
|
|
1746
|
-
async dispatch(connection, header, payload, signal, flowEnabled) {
|
|
1890
|
+
async dispatch(connection, header, payload, signal, flowEnabled, connectionForSend) {
|
|
1747
1891
|
if (header.kind === 3 /* Response */ && header.requestSeq !== void 0) {
|
|
1748
1892
|
try {
|
|
1749
1893
|
if (!this.pendingRequests.resolve(header.requestSeq, {
|
|
@@ -1796,7 +1940,7 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
1796
1940
|
return;
|
|
1797
1941
|
}
|
|
1798
1942
|
if (header.kind === 5 /* Control */) {
|
|
1799
|
-
await this.dispatchControl(connection, header, payload, signal);
|
|
1943
|
+
await this.dispatchControl(connection, header, payload, signal, connectionForSend);
|
|
1800
1944
|
return;
|
|
1801
1945
|
}
|
|
1802
1946
|
if (header.kind === 1 /* Send */) {
|
|
@@ -1810,7 +1954,7 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
1810
1954
|
}, signal);
|
|
1811
1955
|
}
|
|
1812
1956
|
}
|
|
1813
|
-
async dispatchControl(connection, header, payload, signal) {
|
|
1957
|
+
async dispatchControl(connection, header, payload, signal, connectionForSend) {
|
|
1814
1958
|
if (header.name === ZLINK_SESSION_CLOSING) {
|
|
1815
1959
|
const closing = decodeSessionClosing(payload);
|
|
1816
1960
|
await this.serverClosing?.(closing.closeReason);
|
|
@@ -1821,7 +1965,11 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
1821
1965
|
}
|
|
1822
1966
|
if (header.name === ZLINK_STREAM_HEARTBEAT_PING) {
|
|
1823
1967
|
try {
|
|
1824
|
-
await this.frameSender.sendControl(
|
|
1968
|
+
await this.frameSender.sendControl(
|
|
1969
|
+
connectionForSend?.() ?? connection,
|
|
1970
|
+
ZLINK_STREAM_HEARTBEAT_PONG,
|
|
1971
|
+
signal
|
|
1972
|
+
);
|
|
1825
1973
|
} catch (cause) {
|
|
1826
1974
|
throw connectorError(
|
|
1827
1975
|
"sendFailed" /* SendFailed */,
|
|
@@ -1854,15 +2002,17 @@ function decodeRemoteError(protocol, header, payload) {
|
|
|
1854
2002
|
}
|
|
1855
2003
|
|
|
1856
2004
|
// packages/stream-connector/src/Runtime/ZlinkStreamConnectorLifecycle.ts
|
|
2005
|
+
function randomizedDelay(baseDelayMs) {
|
|
2006
|
+
return Math.round(baseDelayMs * (0.5 + Math.random() * 0.5));
|
|
2007
|
+
}
|
|
1857
2008
|
var ZlinkStreamConnectorLifecycle = class {
|
|
1858
|
-
constructor(options, pendingRequests, frameSender, receiveDispatcher, receivedMessages, events
|
|
2009
|
+
constructor(options, pendingRequests, frameSender, receiveDispatcher, receivedMessages, events) {
|
|
1859
2010
|
this.options = options;
|
|
1860
2011
|
this.pendingRequests = pendingRequests;
|
|
1861
2012
|
this.frameSender = frameSender;
|
|
1862
2013
|
this.receiveDispatcher = receiveDispatcher;
|
|
1863
2014
|
this.receivedMessages = receivedMessages;
|
|
1864
2015
|
this.events = events;
|
|
1865
|
-
this.metrics = metrics;
|
|
1866
2016
|
}
|
|
1867
2017
|
receiveLoopAbort;
|
|
1868
2018
|
receiveLoopSleeping = false;
|
|
@@ -1872,6 +2022,7 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
1872
2022
|
connectionGeneration = 0;
|
|
1873
2023
|
currentState = "created" /* Created */;
|
|
1874
2024
|
heartbeatTimer;
|
|
2025
|
+
heartbeatTickRunning = false;
|
|
1875
2026
|
lastInboundAt = 0;
|
|
1876
2027
|
closeTask;
|
|
1877
2028
|
connectTask;
|
|
@@ -1922,6 +2073,7 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
1922
2073
|
this.currentConnection = connection;
|
|
1923
2074
|
this.connectionGeneration += 1;
|
|
1924
2075
|
this.disconnectedPublished = false;
|
|
2076
|
+
this.receivedMessages.resetForNewConnection(this.connectionGeneration > 1);
|
|
1925
2077
|
this.lastInboundAt = Date.now();
|
|
1926
2078
|
await this.setState("connected" /* Connected */, void 0, signal);
|
|
1927
2079
|
this.startHeartbeat();
|
|
@@ -1933,7 +2085,9 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
1933
2085
|
throw new ZlinkStreamException(error2);
|
|
1934
2086
|
}
|
|
1935
2087
|
const error = toStreamError(cause, "connectTimeout" /* ConnectTimeout */, "Connect failed.");
|
|
2088
|
+
this.closeReasonValue ??= "TransportError";
|
|
1936
2089
|
await this.setState("disconnected" /* Disconnected */, error, signal);
|
|
2090
|
+
await this.publishDisconnectedOnce(signal);
|
|
1937
2091
|
throw new ZlinkStreamException(error);
|
|
1938
2092
|
}
|
|
1939
2093
|
}
|
|
@@ -1980,7 +2134,7 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
1980
2134
|
}
|
|
1981
2135
|
this.pendingRequests.failAll({ code: "disconnected" /* Disconnected */, message: "Connector closed." });
|
|
1982
2136
|
await this.setState("closed" /* Closed */, void 0, signal);
|
|
1983
|
-
|
|
2137
|
+
this.publishDisconnectedWithoutWaiting(signal);
|
|
1984
2138
|
if (errors.length === 1) throw errors[0];
|
|
1985
2139
|
if (errors.length > 1) throw new AggregateError(errors, "Stream connector close failed.");
|
|
1986
2140
|
}
|
|
@@ -2011,7 +2165,8 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2011
2165
|
const result = await this.receiveDispatcher.readAndDispatch(
|
|
2012
2166
|
connection,
|
|
2013
2167
|
signal,
|
|
2014
|
-
() => this.isCurrentConnection(connection, generation)
|
|
2168
|
+
() => this.isCurrentConnection(connection, generation),
|
|
2169
|
+
() => this.connectionForSend()
|
|
2015
2170
|
);
|
|
2016
2171
|
if (result.inbound && this.isCurrentConnection(connection, generation)) {
|
|
2017
2172
|
this.lastInboundAt = Date.now();
|
|
@@ -2023,25 +2178,18 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2023
2178
|
let delayMs = this.options.reconnect.initialDelayMs;
|
|
2024
2179
|
let lastError;
|
|
2025
2180
|
const maxAttempts = this.options.reconnect.enabled ? this.options.reconnect.maxAttempts : 1;
|
|
2026
|
-
|
|
2181
|
+
const unlimited = maxAttempts === null;
|
|
2182
|
+
while (unlimited || attempt < maxAttempts) {
|
|
2027
2183
|
attempt += 1;
|
|
2028
|
-
if (attempt > 1) {
|
|
2029
|
-
this.metrics.reconnect();
|
|
2030
|
-
}
|
|
2031
|
-
const handshakeStartedAt = performance.now();
|
|
2032
2184
|
try {
|
|
2033
|
-
|
|
2034
|
-
this.metrics.handshakeCompleted(handshakeStartedAt);
|
|
2035
|
-
return connection;
|
|
2185
|
+
return await this.options.transportFactory.connect(this.options, signal);
|
|
2036
2186
|
} catch (cause) {
|
|
2037
|
-
this.metrics.handshakeCompleted(handshakeStartedAt);
|
|
2038
|
-
this.metrics.handshakeFailed(cause);
|
|
2039
2187
|
lastError = toStreamError(cause, "connectTimeout" /* ConnectTimeout */, "Connect failed.");
|
|
2040
|
-
if (!this.options.reconnect.enabled || attempt >= maxAttempts) {
|
|
2188
|
+
if (!this.options.reconnect.enabled || !unlimited && attempt >= maxAttempts) {
|
|
2041
2189
|
break;
|
|
2042
2190
|
}
|
|
2043
2191
|
await this.setState("reconnecting" /* Reconnecting */, lastError, signal);
|
|
2044
|
-
await delay(delayMs, signal);
|
|
2192
|
+
await delay(randomizedDelay(delayMs), signal);
|
|
2045
2193
|
delayMs = Math.min(
|
|
2046
2194
|
this.options.reconnect.maxDelayMs,
|
|
2047
2195
|
Math.ceil(delayMs * this.options.reconnect.backoffFactor)
|
|
@@ -2057,15 +2205,25 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2057
2205
|
if (!this.options.heartbeat.enabled) {
|
|
2058
2206
|
return;
|
|
2059
2207
|
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2208
|
+
const timer = setInterval(() => {
|
|
2209
|
+
if (this.heartbeatTickRunning) {
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
this.heartbeatTickRunning = true;
|
|
2213
|
+
void this.runHeartbeatTick().finally(() => {
|
|
2214
|
+
if (this.heartbeatTimer === timer) {
|
|
2215
|
+
this.heartbeatTickRunning = false;
|
|
2216
|
+
}
|
|
2217
|
+
});
|
|
2062
2218
|
}, this.options.heartbeat.intervalMs);
|
|
2219
|
+
this.heartbeatTimer = timer;
|
|
2063
2220
|
}
|
|
2064
2221
|
stopHeartbeat() {
|
|
2065
2222
|
if (this.heartbeatTimer !== void 0) {
|
|
2066
2223
|
clearInterval(this.heartbeatTimer);
|
|
2067
2224
|
this.heartbeatTimer = void 0;
|
|
2068
2225
|
}
|
|
2226
|
+
this.heartbeatTickRunning = false;
|
|
2069
2227
|
}
|
|
2070
2228
|
// Spec stream-connector 32 §7: the receive loop runs in both dispatch modes.
|
|
2071
2229
|
// `Manual` only changes what the loop does with a frame — it queues the
|
|
@@ -2180,15 +2338,17 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2180
2338
|
if (this.disconnectTask !== void 0) {
|
|
2181
2339
|
return await this.disconnectTask;
|
|
2182
2340
|
}
|
|
2183
|
-
this.disconnectTask = this.
|
|
2341
|
+
this.disconnectTask = this.tearDownConnection(error).finally(() => {
|
|
2184
2342
|
this.disconnectTask = void 0;
|
|
2185
2343
|
});
|
|
2186
|
-
|
|
2344
|
+
await this.disconnectTask;
|
|
2345
|
+
await this.announceDisconnect(error);
|
|
2187
2346
|
}
|
|
2188
2347
|
isCurrentConnection(connection, generation) {
|
|
2189
2348
|
return !this.closeRequested && this.currentConnection === connection && this.connectionGeneration === generation;
|
|
2190
2349
|
}
|
|
2191
|
-
|
|
2350
|
+
/** Transport teardown only — no application callback runs from here. */
|
|
2351
|
+
async tearDownConnection(error) {
|
|
2192
2352
|
this.stopHeartbeat();
|
|
2193
2353
|
this.stopReceiveLoop();
|
|
2194
2354
|
const connection = this.currentConnection;
|
|
@@ -2198,23 +2358,75 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2198
2358
|
await connection?.close();
|
|
2199
2359
|
} catch {
|
|
2200
2360
|
}
|
|
2361
|
+
}
|
|
2362
|
+
/**
|
|
2363
|
+
* Runs once the teardown promise has settled, so a handler reached from here
|
|
2364
|
+
* may call `connect` without waiting for a task its own caller still holds.
|
|
2365
|
+
* The reconnect is queued before the notification is awaited for the same
|
|
2366
|
+
* reason: spec stream-connector 32 §6 has reconnect on by default, and a
|
|
2367
|
+
* handler that is slow — or whose promise never settles at all — must not
|
|
2368
|
+
* cost the connector the attempt. The state is already `Disconnected` and the
|
|
2369
|
+
* state handlers are already invoked by the time the queued microtask runs,
|
|
2370
|
+
* because `setState` records the state and hands the change to the handlers
|
|
2371
|
+
* before it awaits any of them.
|
|
2372
|
+
*/
|
|
2373
|
+
async announceDisconnect(error) {
|
|
2201
2374
|
if (this.closeRequested) return;
|
|
2202
|
-
|
|
2203
|
-
|
|
2375
|
+
const announce = this.claimDisconnectedPublish();
|
|
2376
|
+
const notified = (async () => {
|
|
2377
|
+
await this.setState("disconnected" /* Disconnected */, error);
|
|
2378
|
+
if (announce) {
|
|
2379
|
+
await this.events.publishDisconnected();
|
|
2380
|
+
}
|
|
2381
|
+
})();
|
|
2204
2382
|
if (this.shouldReconnect()) {
|
|
2205
2383
|
queueMicrotask(() => {
|
|
2206
2384
|
void this.connect().catch(() => void 0);
|
|
2207
2385
|
});
|
|
2208
2386
|
}
|
|
2387
|
+
await notified;
|
|
2209
2388
|
}
|
|
2210
2389
|
shouldReconnect() {
|
|
2211
2390
|
return this.options.reconnect.enabled && !this.closeRequested;
|
|
2212
2391
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2392
|
+
// Synchronous test-and-set, taken before any await, so the single disconnect
|
|
2393
|
+
// notification the spec promises is claimed by exactly one caller even when
|
|
2394
|
+
// the publishing itself is deferred.
|
|
2395
|
+
claimDisconnectedPublish() {
|
|
2396
|
+
if (this.disconnectedPublished) return false;
|
|
2215
2397
|
this.disconnectedPublished = true;
|
|
2398
|
+
return true;
|
|
2399
|
+
}
|
|
2400
|
+
async publishDisconnectedOnce(signal) {
|
|
2401
|
+
if (!this.claimDisconnectedPublish()) return;
|
|
2216
2402
|
await this.events.publishDisconnected(signal);
|
|
2217
2403
|
}
|
|
2404
|
+
/**
|
|
2405
|
+
* Spec stream-connector 32 §7: the connector runs the registered handler, it
|
|
2406
|
+
* does not wait for it to finish. `close` returns once its own work is done —
|
|
2407
|
+
* the drain, the transport close, the pending requests — having run the
|
|
2408
|
+
* disconnect handler but without looking at whether that handler has ended.
|
|
2409
|
+
*
|
|
2410
|
+
* Starting the publish without awaiting it still runs every handler right
|
|
2411
|
+
* here: an `async` function body runs synchronously up to its first `await`,
|
|
2412
|
+
* and `publishDisconnected` reaches each handler before that point. So the
|
|
2413
|
+
* handler has run by the time `close` returns, which is what the spec asks
|
|
2414
|
+
* for, while a handler that calls `close` no longer waits for the very
|
|
2415
|
+
* `closeTask` it is running inside. Java and C++ hand the handler to a queue
|
|
2416
|
+
* and return the same way.
|
|
2417
|
+
*
|
|
2418
|
+
* `claimDisconnectedPublish` is taken by the same call and before any await,
|
|
2419
|
+
* so deferring the completion never turns the one notification into two.
|
|
2420
|
+
*
|
|
2421
|
+
* Nothing awaits the promise, so a rejection would reach the process as an
|
|
2422
|
+
* unhandled rejection and kill it. Handler failures are already contained
|
|
2423
|
+
* where they were before this call stopped waiting — the `Promise.allSettled`
|
|
2424
|
+
* in `publishDisconnected` — and this `catch` covers what is left rather than
|
|
2425
|
+
* reporting the same failure a second time on the error surface.
|
|
2426
|
+
*/
|
|
2427
|
+
publishDisconnectedWithoutWaiting(signal) {
|
|
2428
|
+
void this.publishDisconnectedOnce(signal).catch(() => void 0);
|
|
2429
|
+
}
|
|
2218
2430
|
async setState(current, error, signal) {
|
|
2219
2431
|
const previous = this.currentState;
|
|
2220
2432
|
this.currentState = current;
|
|
@@ -2396,9 +2608,6 @@ var BrowserWebSocketConnection = class {
|
|
|
2396
2608
|
this.readWaiter = void 0;
|
|
2397
2609
|
resolve();
|
|
2398
2610
|
};
|
|
2399
|
-
if (this.hasQueuedMessage() || this.closed) {
|
|
2400
|
-
this.wakeReader();
|
|
2401
|
-
}
|
|
2402
2611
|
});
|
|
2403
2612
|
}
|
|
2404
2613
|
wakeReader() {
|
|
@@ -2478,50 +2687,6 @@ function toUint8Array(data) {
|
|
|
2478
2687
|
throw connectorError("frameDecodeFailed" /* FrameDecodeFailed */, "WebSocket text messages are not supported.");
|
|
2479
2688
|
}
|
|
2480
2689
|
|
|
2481
|
-
// packages/stream-connector/src/Runtime/ZlinkStreamRuntimeMetrics.ts
|
|
2482
|
-
var ZlinkStreamRuntimeMetrics = class {
|
|
2483
|
-
constructor(options) {
|
|
2484
|
-
this.options = options;
|
|
2485
|
-
const meter = options.meterProvider?.getMeter("zlink.framework");
|
|
2486
|
-
this.reconnects = meter?.createCounter("zlink.stream.reconnects", { unit: "{event}" });
|
|
2487
|
-
this.handshakeDuration = meter?.createHistogram("zlink.stream.handshake.duration", { unit: "s" });
|
|
2488
|
-
this.handshakeFailures = meter?.createCounter("zlink.stream.handshake.failures", { unit: "{failure}" });
|
|
2489
|
-
this.inboundBytes = meter?.createCounter("zlink.stream.inbound.bytes", { unit: "By" });
|
|
2490
|
-
this.outboundBytes = meter?.createCounter("zlink.stream.outbound.bytes", { unit: "By" });
|
|
2491
|
-
}
|
|
2492
|
-
reconnects;
|
|
2493
|
-
handshakeDuration;
|
|
2494
|
-
handshakeFailures;
|
|
2495
|
-
inboundBytes;
|
|
2496
|
-
outboundBytes;
|
|
2497
|
-
reconnect() {
|
|
2498
|
-
this.safe(() => this.reconnects?.add(1, { transport: this.transportLabel() }));
|
|
2499
|
-
}
|
|
2500
|
-
handshakeCompleted(startedAt) {
|
|
2501
|
-
const seconds = (performance.now() - startedAt) / 1e3;
|
|
2502
|
-
this.safe(() => this.handshakeDuration?.record(seconds, { transport: this.transportLabel() }));
|
|
2503
|
-
}
|
|
2504
|
-
handshakeFailed(error) {
|
|
2505
|
-
const reason = error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError") ? "canceled" : "transport_error";
|
|
2506
|
-
this.safe(() => this.handshakeFailures?.add(1, { transport: this.transportLabel(), reason }));
|
|
2507
|
-
}
|
|
2508
|
-
inbound(byteCount) {
|
|
2509
|
-
this.safe(() => this.inboundBytes?.add(byteCount, { transport: this.transportLabel() }));
|
|
2510
|
-
}
|
|
2511
|
-
outbound(byteCount) {
|
|
2512
|
-
this.safe(() => this.outboundBytes?.add(byteCount, { transport: this.transportLabel() }));
|
|
2513
|
-
}
|
|
2514
|
-
transportLabel() {
|
|
2515
|
-
return this.options.endpoint.startsWith("wss:") ? "wss" : "ws";
|
|
2516
|
-
}
|
|
2517
|
-
safe(record) {
|
|
2518
|
-
try {
|
|
2519
|
-
record();
|
|
2520
|
-
} catch {
|
|
2521
|
-
}
|
|
2522
|
-
}
|
|
2523
|
-
};
|
|
2524
|
-
|
|
2525
2690
|
// packages/stream-connector/src/Runtime/ZlinkStreamConnector.ts
|
|
2526
2691
|
var DefaultZlinkStreamConnector = class {
|
|
2527
2692
|
static heartbeatPingName = ZLINK_STREAM_HEARTBEAT_PING;
|
|
@@ -2544,9 +2709,8 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2544
2709
|
configurable: true,
|
|
2545
2710
|
get: () => this.diagnosticsLevelCell.level
|
|
2546
2711
|
});
|
|
2547
|
-
const metrics = new ZlinkStreamRuntimeMetrics(this.options);
|
|
2548
2712
|
const protocol = new ZlinkStreamFrameProtocol(this.options);
|
|
2549
|
-
this.frameSender = new ZlinkStreamFrameSender(protocol, flowContext
|
|
2713
|
+
this.frameSender = new ZlinkStreamFrameSender(protocol, flowContext);
|
|
2550
2714
|
this.receivedMessages = new ZlinkStreamReceivedMessages(
|
|
2551
2715
|
this.events,
|
|
2552
2716
|
this.options.dispatchMode === "immediate" /* Immediate */
|
|
@@ -2558,7 +2722,6 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2558
2722
|
this.frameSender,
|
|
2559
2723
|
this.events,
|
|
2560
2724
|
flowContext,
|
|
2561
|
-
metrics,
|
|
2562
2725
|
(reason) => this.lifecycle.serverClosing(reason)
|
|
2563
2726
|
);
|
|
2564
2727
|
this.lifecycle = new ZlinkStreamConnectorLifecycle(
|
|
@@ -2567,8 +2730,7 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2567
2730
|
this.frameSender,
|
|
2568
2731
|
this.receiveDispatcher,
|
|
2569
2732
|
this.receivedMessages,
|
|
2570
|
-
this.events
|
|
2571
|
-
metrics
|
|
2733
|
+
this.events
|
|
2572
2734
|
);
|
|
2573
2735
|
}
|
|
2574
2736
|
get isConnected() {
|
|
@@ -2583,6 +2745,17 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2583
2745
|
get pendingDispatchCount() {
|
|
2584
2746
|
return this.pendingRequests.count;
|
|
2585
2747
|
}
|
|
2748
|
+
/**
|
|
2749
|
+
* Spec stream-connector 32 §10: how many packets carrying `name` arrived on
|
|
2750
|
+
* the current connection. Arrivals are what is counted, so a message a
|
|
2751
|
+
* handler dispatched or a wait surface consumed still counts, and `Manual`
|
|
2752
|
+
* and `Immediate` report the same number. Establishing a connection resets
|
|
2753
|
+
* the count to 0, a reconnect included.
|
|
2754
|
+
*/
|
|
2755
|
+
receivedCount(name) {
|
|
2756
|
+
validateName(name);
|
|
2757
|
+
return this.receivedMessages.receivedCount(name);
|
|
2758
|
+
}
|
|
2586
2759
|
/**
|
|
2587
2760
|
* Current diagnostics level (spec 26 §4.1, spec stream-connector 32 §13).
|
|
2588
2761
|
* Reflects the level set by the most recent {@link setDiagnosticsLevel}
|
|
@@ -2598,14 +2771,20 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2598
2771
|
* state update: it applies to processing points that read the level after
|
|
2599
2772
|
* this call returns and is never applied retroactively to frames already
|
|
2600
2773
|
* built. Rejects unknown values with {@link ZlinkStreamErrorCode.ConfigurationError}.
|
|
2601
|
-
*
|
|
2602
|
-
*
|
|
2774
|
+
* Spec stream-connector 32 §13: this surface changes the value without
|
|
2775
|
+
* waiting for anything; it is not a blocking call over the asynchronous pair,
|
|
2776
|
+
* which a receive callback would otherwise make wait for its own completion.
|
|
2603
2777
|
*/
|
|
2604
2778
|
setDiagnosticsLevel(level) {
|
|
2605
|
-
|
|
2779
|
+
this.diagnosticsLevelCell.set(level);
|
|
2606
2780
|
}
|
|
2781
|
+
/**
|
|
2782
|
+
* Asynchronous counterpart of {@link setDiagnosticsLevel} (spec
|
|
2783
|
+
* stream-connector 32 §13). It changes the same value; awaiting it is how a
|
|
2784
|
+
* caller observes the change, and it never replaces the synchronous surface.
|
|
2785
|
+
*/
|
|
2607
2786
|
setDiagnosticsLevelAsync(level) {
|
|
2608
|
-
this.
|
|
2787
|
+
this.setDiagnosticsLevel(level);
|
|
2609
2788
|
return Promise.resolve();
|
|
2610
2789
|
}
|
|
2611
2790
|
onErrorReceived(handler) {
|
|
@@ -2644,17 +2823,28 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2644
2823
|
}, signal);
|
|
2645
2824
|
return this.receivedMessages.on(name, encodedHandler);
|
|
2646
2825
|
}
|
|
2647
|
-
waitFor(
|
|
2648
|
-
|
|
2649
|
-
return new ZlinkStreamWaitBuilder(this, name);
|
|
2826
|
+
waitFor(nameOrType) {
|
|
2827
|
+
return new ZlinkStreamWaitBuilder(this, this.observedName(nameOrType));
|
|
2650
2828
|
}
|
|
2651
|
-
expectNone(
|
|
2652
|
-
|
|
2653
|
-
|
|
2829
|
+
expectNone(nameOrType) {
|
|
2830
|
+
return new ZlinkStreamExpectNoneBuilder(this, this.observedName(nameOrType));
|
|
2831
|
+
}
|
|
2832
|
+
waitForSequence(nameOrType) {
|
|
2833
|
+
return new ZlinkStreamSequenceBuilder(this, this.observedName(nameOrType));
|
|
2654
2834
|
}
|
|
2655
|
-
|
|
2835
|
+
/**
|
|
2836
|
+
* Spec stream-connector 32 §10.1: each wait surface offers both ways of
|
|
2837
|
+
* naming a packet. A string is the name the caller states; a constructor
|
|
2838
|
+
* goes through the options' `nameResolver`, which is the same resolver
|
|
2839
|
+
* `send` and `request` use, so both paths land on one name for one type.
|
|
2840
|
+
*/
|
|
2841
|
+
observedName(nameOrType) {
|
|
2842
|
+
const name = typeof nameOrType === "function" ? this.options.nameResolver.resolve(nameOrType) : nameOrType;
|
|
2843
|
+
if (typeof name !== "string") {
|
|
2844
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "Packet name must be a string or a payload constructor.");
|
|
2845
|
+
}
|
|
2656
2846
|
validateName(name);
|
|
2657
|
-
return
|
|
2847
|
+
return name;
|
|
2658
2848
|
}
|
|
2659
2849
|
waitForMessage(name, timeoutMs, predicate, signal) {
|
|
2660
2850
|
validateName(name);
|
|
@@ -2707,7 +2897,10 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2707
2897
|
finish(cause);
|
|
2708
2898
|
}
|
|
2709
2899
|
return true;
|
|
2710
|
-
})
|
|
2900
|
+
}, () => finish(connectorError(
|
|
2901
|
+
"disconnected" /* Disconnected */,
|
|
2902
|
+
`The connection this wait for '${name}' observed has ended.`
|
|
2903
|
+
)));
|
|
2711
2904
|
});
|
|
2712
2905
|
}
|
|
2713
2906
|
encodePayload(payload, messageType) {
|
|
@@ -2786,50 +2979,6 @@ function isEncodedPayload(value) {
|
|
|
2786
2979
|
return typeof candidate.codec === "number" && candidate.payload instanceof Uint8Array;
|
|
2787
2980
|
}
|
|
2788
2981
|
|
|
2789
|
-
// packages/stream-connector/src/Runtime/ZlinkStreamAssertions.ts
|
|
2790
|
-
var zlinkStreamAssert = {
|
|
2791
|
-
ensure(condition, message) {
|
|
2792
|
-
if (!condition) {
|
|
2793
|
-
throw connectorError("validationFailed" /* ValidationFailed */, message);
|
|
2794
|
-
}
|
|
2795
|
-
},
|
|
2796
|
-
async expectFailure(action, errorKind) {
|
|
2797
|
-
let failure;
|
|
2798
|
-
try {
|
|
2799
|
-
await action();
|
|
2800
|
-
} catch (error) {
|
|
2801
|
-
failure = error;
|
|
2802
|
-
}
|
|
2803
|
-
if (failure === void 0) {
|
|
2804
|
-
throw connectorError("validationFailed" /* ValidationFailed */, "Expected action to fail.");
|
|
2805
|
-
}
|
|
2806
|
-
const streamError = unwrapStreamError(failure);
|
|
2807
|
-
if (errorKind !== void 0 && streamError.code !== errorKind) {
|
|
2808
|
-
throw connectorError(
|
|
2809
|
-
"validationFailed" /* ValidationFailed */,
|
|
2810
|
-
`Expected failure kind '${errorKind}', got '${streamError.code}'.`,
|
|
2811
|
-
failure
|
|
2812
|
-
);
|
|
2813
|
-
}
|
|
2814
|
-
return streamError;
|
|
2815
|
-
},
|
|
2816
|
-
async expectTimeout(action) {
|
|
2817
|
-
let failure;
|
|
2818
|
-
try {
|
|
2819
|
-
await action();
|
|
2820
|
-
} catch (error) {
|
|
2821
|
-
failure = error;
|
|
2822
|
-
}
|
|
2823
|
-
if (failure === void 0) {
|
|
2824
|
-
throw connectorError("validationFailed" /* ValidationFailed */, "Expected action to time out.");
|
|
2825
|
-
}
|
|
2826
|
-
const code = unwrapStreamError(failure).code;
|
|
2827
|
-
if (code !== "requestTimeout" /* RequestTimeout */ && code !== "connectTimeout" /* ConnectTimeout */) {
|
|
2828
|
-
throw failure;
|
|
2829
|
-
}
|
|
2830
|
-
}
|
|
2831
|
-
};
|
|
2832
|
-
|
|
2833
2982
|
// packages/stream-connector/src/index.ts
|
|
2834
2983
|
var zlinkStreamConnectorFactory = {
|
|
2835
2984
|
create(options) {
|
|
@@ -2837,7 +2986,6 @@ var zlinkStreamConnectorFactory = {
|
|
|
2837
2986
|
}
|
|
2838
2987
|
};
|
|
2839
2988
|
export {
|
|
2840
|
-
DefaultZlinkStreamConnector,
|
|
2841
2989
|
ZlinkStreamCodec,
|
|
2842
2990
|
ZlinkStreamCompression,
|
|
2843
2991
|
ZlinkStreamConnectionState,
|