@zlink-systems/stream-connector 0.13.0 → 0.15.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/IZlinkStreamConnector.d.ts +1 -2
- package/dist/browser/Contracts/ZlinkStreamConnectorOptions.d.ts +0 -6
- package/dist/browser/Contracts/ZlinkStreamEnums.d.ts +0 -3
- package/dist/browser/Contracts/ZlinkStreamModels.d.ts +0 -13
- package/dist/browser/Contracts/index.d.ts +1 -1
- package/dist/browser/Runtime/ZlinkStreamConnector.d.ts +1 -3
- package/dist/browser/Runtime/ZlinkStreamConnectorLifecycle.d.ts +18 -1
- package/dist/browser/Runtime/ZlinkStreamReceiveDispatcher.d.ts +1 -3
- package/dist/browser/Runtime/ZlinkStreamReceivedMessages.d.ts +31 -4
- package/dist/browser/index.global.js +2934 -0
- package/dist/browser/index.mjs +172 -206
- package/package.json +2 -2
- package/dist/browser/Runtime/ZlinkStreamInboundObservers.d.ts +0 -25
package/dist/browser/index.mjs
CHANGED
|
@@ -9,7 +9,8 @@ function lz4PickleUncompressed(payload) {
|
|
|
9
9
|
pickled.set(payload, 1);
|
|
10
10
|
return pickled;
|
|
11
11
|
}
|
|
12
|
-
function lz4UnpicklePayload(payload,
|
|
12
|
+
function lz4UnpicklePayload(payload, maxSize) {
|
|
13
|
+
const maxDecompressedSize = maxSize ?? defaultMaxDecompressedPayloadSize;
|
|
13
14
|
if (payload.length === 0) {
|
|
14
15
|
return new Uint8Array();
|
|
15
16
|
}
|
|
@@ -152,7 +153,8 @@ function decodeStreamWireFrame(frame) {
|
|
|
152
153
|
payload: frame.slice(6 + headerLength)
|
|
153
154
|
};
|
|
154
155
|
}
|
|
155
|
-
function encodeStreamWireHeader(header,
|
|
156
|
+
function encodeStreamWireHeader(header, flagOverrides) {
|
|
157
|
+
const flags = flagOverrides ?? defaultHeaderFlags;
|
|
156
158
|
const reply = isReplyKind(header.kind);
|
|
157
159
|
const packetName = reply ? "" : header.name;
|
|
158
160
|
if (!reply) validateStreamWirePacketName(packetName);
|
|
@@ -213,7 +215,8 @@ function encodeStreamWireHeader(header, flags = defaultHeaderFlags) {
|
|
|
213
215
|
}
|
|
214
216
|
return buffer;
|
|
215
217
|
}
|
|
216
|
-
function decodeStreamWireHeader(header,
|
|
218
|
+
function decodeStreamWireHeader(header, flagOverrides, includeFlow = true) {
|
|
219
|
+
const flags = flagOverrides ?? defaultHeaderFlags;
|
|
217
220
|
let offset = 0;
|
|
218
221
|
if (header.length < 5) {
|
|
219
222
|
throw new Error("Stream header is incomplete.");
|
|
@@ -351,8 +354,8 @@ function decodeStreamWireMetadata(metadata) {
|
|
|
351
354
|
function lz4PickleUncompressed2(payload) {
|
|
352
355
|
return lz4PickleUncompressed(payload);
|
|
353
356
|
}
|
|
354
|
-
function lz4UnpicklePayload2(payload, maxDecompressedSize
|
|
355
|
-
return lz4UnpicklePayload(payload, maxDecompressedSize);
|
|
357
|
+
function lz4UnpicklePayload2(payload, maxDecompressedSize) {
|
|
358
|
+
return lz4UnpicklePayload(payload, maxDecompressedSize ?? defaultMaxDecompressedPayloadSize);
|
|
356
359
|
}
|
|
357
360
|
function utf8Encode(value) {
|
|
358
361
|
return new TextEncoder().encode(value);
|
|
@@ -497,20 +500,17 @@ var ZlinkStreamErrorCode = /* @__PURE__ */ ((ZlinkStreamErrorCode2) => {
|
|
|
497
500
|
ZlinkStreamErrorCode2["CompressionFailed"] = "compressionFailed";
|
|
498
501
|
ZlinkStreamErrorCode2["DecompressionFailed"] = "decompressionFailed";
|
|
499
502
|
ZlinkStreamErrorCode2["UserCallbackFailed"] = "userCallbackFailed";
|
|
500
|
-
ZlinkStreamErrorCode2["ObserverFailed"] = "observer-failed";
|
|
501
|
-
ZlinkStreamErrorCode2["ObserverDropped"] = "observer-dropped";
|
|
502
|
-
ZlinkStreamErrorCode2["ReceivedMessageDropped"] = "received-message-dropped";
|
|
503
503
|
ZlinkStreamErrorCode2["RemoteError"] = "remoteError";
|
|
504
504
|
return ZlinkStreamErrorCode2;
|
|
505
505
|
})(ZlinkStreamErrorCode || {});
|
|
506
|
-
var ZlinkStreamConnectionState = /* @__PURE__ */ ((
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
return
|
|
506
|
+
var ZlinkStreamConnectionState = /* @__PURE__ */ ((ZlinkStreamConnectionState3) => {
|
|
507
|
+
ZlinkStreamConnectionState3["Created"] = "created";
|
|
508
|
+
ZlinkStreamConnectionState3["Connecting"] = "connecting";
|
|
509
|
+
ZlinkStreamConnectionState3["Connected"] = "connected";
|
|
510
|
+
ZlinkStreamConnectionState3["Reconnecting"] = "reconnecting";
|
|
511
|
+
ZlinkStreamConnectionState3["Disconnected"] = "disconnected";
|
|
512
|
+
ZlinkStreamConnectionState3["Closed"] = "closed";
|
|
513
|
+
return ZlinkStreamConnectionState3;
|
|
514
514
|
})(ZlinkStreamConnectionState || {});
|
|
515
515
|
|
|
516
516
|
// packages/stream-connector/src/Contracts/ZlinkStreamMetadata.ts
|
|
@@ -1284,11 +1284,6 @@ function normalizeOptions(options, defaultTransportFactory) {
|
|
|
1284
1284
|
validatePositive(options.waitTimeoutMs ?? 5e3, "WaitTimeout");
|
|
1285
1285
|
validatePositive(options.maxSendPayloadSize ?? 64 * 1024, "MaxSendPayloadSize");
|
|
1286
1286
|
validatePositive(options.maxReceivePayloadSize ?? 64 * 1024, "MaxReceivePayloadSize");
|
|
1287
|
-
validatePositive(options.maxReceivedMessages ?? 1024, "MaxReceivedMessages");
|
|
1288
|
-
validatePositive(options.maxInboundObserverNotifications ?? 1024, "MaxInboundObserverNotifications");
|
|
1289
|
-
if ((options.maxInboundObserverPayloadPreviewBytes ?? 0) < 0) {
|
|
1290
|
-
throw connectorError("validationFailed" /* ValidationFailed */, "MaxInboundObserverPayloadPreviewBytes must not be negative.");
|
|
1291
|
-
}
|
|
1292
1287
|
validateHeartbeat(options.heartbeat);
|
|
1293
1288
|
validateReconnect(options.reconnect);
|
|
1294
1289
|
validateDiagnosticsLevel(options.diagnosticsLevel);
|
|
@@ -1312,9 +1307,6 @@ function normalizeOptions(options, defaultTransportFactory) {
|
|
|
1312
1307
|
},
|
|
1313
1308
|
maxSendPayloadSize: options.maxSendPayloadSize ?? 64 * 1024,
|
|
1314
1309
|
maxReceivePayloadSize: options.maxReceivePayloadSize ?? 64 * 1024,
|
|
1315
|
-
maxReceivedMessages: options.maxReceivedMessages ?? 1024,
|
|
1316
|
-
maxInboundObserverNotifications: options.maxInboundObserverNotifications ?? 1024,
|
|
1317
|
-
maxInboundObserverPayloadPreviewBytes: options.maxInboundObserverPayloadPreviewBytes ?? 0,
|
|
1318
1310
|
dispatchMode: options.dispatchMode ?? "manual" /* Manual */,
|
|
1319
1311
|
compression: options.compression ?? "lz4" /* Lz4 */,
|
|
1320
1312
|
compressionCodec: resolveCompressionCodec2(options),
|
|
@@ -1475,11 +1467,18 @@ var ZlinkStreamPendingRequests = class {
|
|
|
1475
1467
|
|
|
1476
1468
|
// packages/stream-connector/src/Runtime/ZlinkStreamReceivedMessages.ts
|
|
1477
1469
|
var ZlinkStreamReceivedMessages = class {
|
|
1478
|
-
|
|
1479
|
-
|
|
1470
|
+
/**
|
|
1471
|
+
* @param deliverOnArrival `Immediate` runs registered handlers on the receive
|
|
1472
|
+
* path; `Manual` leaves them queued until {@link pump} runs them on the
|
|
1473
|
+
* caller's thread (spec stream-connector 32 §7). Wait surfaces observe the
|
|
1474
|
+
* queue in both modes, so they never depend on this flag.
|
|
1475
|
+
*/
|
|
1476
|
+
constructor(events, deliverOnArrival) {
|
|
1480
1477
|
this.events = events;
|
|
1478
|
+
this.deliverOnArrival = deliverOnArrival;
|
|
1481
1479
|
}
|
|
1482
1480
|
handlers = /* @__PURE__ */ new Map();
|
|
1481
|
+
observers = /* @__PURE__ */ new Map();
|
|
1483
1482
|
// A handler can be registered after messages for another name arrive, so the
|
|
1484
1483
|
// queue is not a simple FIFO. Tombstones let us remove a deliverable entry
|
|
1485
1484
|
// without shifting every later message on the hot receive path.
|
|
@@ -1487,7 +1486,6 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1487
1486
|
queueHead = 0;
|
|
1488
1487
|
queuedCount = 0;
|
|
1489
1488
|
drainTask;
|
|
1490
|
-
dropReportPending = false;
|
|
1491
1489
|
on(name, handler) {
|
|
1492
1490
|
validateName(name);
|
|
1493
1491
|
let set = this.handlers.get(name);
|
|
@@ -1496,7 +1494,7 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1496
1494
|
this.handlers.set(name, set);
|
|
1497
1495
|
}
|
|
1498
1496
|
set.add(handler);
|
|
1499
|
-
if (this.hasQueuedMessage(name)) {
|
|
1497
|
+
if (this.deliverOnArrival && this.hasQueuedMessage(name)) {
|
|
1500
1498
|
queueMicrotask(() => this.scheduleDrain());
|
|
1501
1499
|
}
|
|
1502
1500
|
return subscription(() => {
|
|
@@ -1506,14 +1504,66 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1506
1504
|
}
|
|
1507
1505
|
});
|
|
1508
1506
|
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Registers a wait surface over the receive queue. Spec stream-connector 32
|
|
1509
|
+
* §7: these are not registered callbacks — they observe and consume the
|
|
1510
|
+
* packets the queue has not delivered yet, in both dispatch modes, so
|
|
1511
|
+
* `Manual` needs no dispatch pump to complete a wait. The queue is scanned in
|
|
1512
|
+
* a microtask so a message that arrived before the wait started is still
|
|
1513
|
+
* observed, and so the caller has its subscription in hand by then.
|
|
1514
|
+
*/
|
|
1515
|
+
observe(name, observer) {
|
|
1516
|
+
validateName(name);
|
|
1517
|
+
let set = this.observers.get(name);
|
|
1518
|
+
if (set === void 0) {
|
|
1519
|
+
set = /* @__PURE__ */ new Set();
|
|
1520
|
+
this.observers.set(name, set);
|
|
1521
|
+
}
|
|
1522
|
+
set.add(observer);
|
|
1523
|
+
queueMicrotask(() => {
|
|
1524
|
+
if (this.observers.get(name)?.has(observer) === true) {
|
|
1525
|
+
this.offerQueued(name, observer);
|
|
1526
|
+
}
|
|
1527
|
+
});
|
|
1528
|
+
return subscription(() => {
|
|
1529
|
+
set.delete(observer);
|
|
1530
|
+
if (set.size === 0 && this.observers.get(name) === set) {
|
|
1531
|
+
this.observers.delete(name);
|
|
1532
|
+
}
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1509
1535
|
enqueue(message, signal) {
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1536
|
+
for (const observer of [...this.observers.get(message.name) ?? []]) {
|
|
1537
|
+
if (observer(message)) {
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1513
1540
|
}
|
|
1514
1541
|
this.queue.push({ message, signal });
|
|
1515
1542
|
this.queuedCount += 1;
|
|
1543
|
+
if (this.deliverOnArrival) {
|
|
1544
|
+
this.scheduleDrain();
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Runs the registered handlers the receive path left queued. `Manual` calls
|
|
1549
|
+
* this from `dispatch`; `Immediate` has already drained on arrival.
|
|
1550
|
+
*/
|
|
1551
|
+
async pump() {
|
|
1516
1552
|
this.scheduleDrain();
|
|
1553
|
+
await this.drainTask;
|
|
1554
|
+
}
|
|
1555
|
+
offerQueued(name, observer) {
|
|
1556
|
+
for (let index = this.queueHead; index < this.queue.length; index += 1) {
|
|
1557
|
+
const queued = this.queue[index];
|
|
1558
|
+
if (queued === void 0 || queued.message.name !== name) {
|
|
1559
|
+
continue;
|
|
1560
|
+
}
|
|
1561
|
+
if (!observer(queued.message)) {
|
|
1562
|
+
continue;
|
|
1563
|
+
}
|
|
1564
|
+
this.removeAt(index);
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1517
1567
|
}
|
|
1518
1568
|
scheduleDrain() {
|
|
1519
1569
|
if (this.drainTask !== void 0) {
|
|
@@ -1521,7 +1571,7 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1521
1571
|
}
|
|
1522
1572
|
this.drainTask = this.drain().finally(() => {
|
|
1523
1573
|
this.drainTask = void 0;
|
|
1524
|
-
if (this.findDeliverableIndex() >= 0) {
|
|
1574
|
+
if (this.deliverOnArrival && this.findDeliverableIndex() >= 0) {
|
|
1525
1575
|
this.scheduleDrain();
|
|
1526
1576
|
}
|
|
1527
1577
|
});
|
|
@@ -1530,10 +1580,7 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1530
1580
|
for (let index = this.findDeliverableIndex(); index >= 0; index = this.findDeliverableIndex()) {
|
|
1531
1581
|
const queued = this.queue[index];
|
|
1532
1582
|
if (queued === void 0) continue;
|
|
1533
|
-
this.
|
|
1534
|
-
this.queuedCount -= 1;
|
|
1535
|
-
this.advanceHead();
|
|
1536
|
-
this.compactQueue();
|
|
1583
|
+
this.removeAt(index);
|
|
1537
1584
|
const { message, signal } = queued;
|
|
1538
1585
|
const handlers = [...this.handlers.get(message.name)];
|
|
1539
1586
|
for (const handler of handlers) {
|
|
@@ -1549,6 +1596,12 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1549
1596
|
}
|
|
1550
1597
|
}
|
|
1551
1598
|
}
|
|
1599
|
+
removeAt(index) {
|
|
1600
|
+
this.queue[index] = void 0;
|
|
1601
|
+
this.queuedCount -= 1;
|
|
1602
|
+
this.advanceHead();
|
|
1603
|
+
this.compactQueue();
|
|
1604
|
+
}
|
|
1552
1605
|
findDeliverableIndex() {
|
|
1553
1606
|
for (let index = this.queueHead; index < this.queue.length; index += 1) {
|
|
1554
1607
|
const queued = this.queue[index];
|
|
@@ -1580,144 +1633,6 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1580
1633
|
this.queueHead = 0;
|
|
1581
1634
|
}
|
|
1582
1635
|
}
|
|
1583
|
-
reportDropped(signal) {
|
|
1584
|
-
if (this.dropReportPending) {
|
|
1585
|
-
return;
|
|
1586
|
-
}
|
|
1587
|
-
this.dropReportPending = true;
|
|
1588
|
-
queueMicrotask(() => {
|
|
1589
|
-
void this.events.publishError({
|
|
1590
|
-
code: "received-message-dropped" /* ReceivedMessageDropped */,
|
|
1591
|
-
message: "Received stream message was dropped because the received-message queue is full."
|
|
1592
|
-
}, signal).finally(() => {
|
|
1593
|
-
this.dropReportPending = false;
|
|
1594
|
-
}).catch(() => {
|
|
1595
|
-
});
|
|
1596
|
-
});
|
|
1597
|
-
}
|
|
1598
|
-
};
|
|
1599
|
-
|
|
1600
|
-
// packages/stream-connector/src/Runtime/ZlinkStreamInboundObservers.ts
|
|
1601
|
-
var ZlinkStreamInboundObservers = class {
|
|
1602
|
-
constructor(maxNotifications, maxPayloadPreviewBytes, events) {
|
|
1603
|
-
this.maxNotifications = maxNotifications;
|
|
1604
|
-
this.maxPayloadPreviewBytes = maxPayloadPreviewBytes;
|
|
1605
|
-
this.events = events;
|
|
1606
|
-
}
|
|
1607
|
-
observers = /* @__PURE__ */ new Set();
|
|
1608
|
-
queue = [];
|
|
1609
|
-
queueHead = 0;
|
|
1610
|
-
queuedCount = 0;
|
|
1611
|
-
draining = false;
|
|
1612
|
-
dropReportPending = false;
|
|
1613
|
-
add(observer) {
|
|
1614
|
-
this.observers.add(observer);
|
|
1615
|
-
return subscription(() => this.observers.delete(observer));
|
|
1616
|
-
}
|
|
1617
|
-
enqueue(header, payload, signal) {
|
|
1618
|
-
if (this.observers.size === 0) {
|
|
1619
|
-
return;
|
|
1620
|
-
}
|
|
1621
|
-
if (this.queuedCount >= this.maxNotifications) {
|
|
1622
|
-
this.reportDropped(signal);
|
|
1623
|
-
return;
|
|
1624
|
-
}
|
|
1625
|
-
this.queue.push(this.createObservation(header, payload));
|
|
1626
|
-
this.queuedCount += 1;
|
|
1627
|
-
this.scheduleDrain(signal);
|
|
1628
|
-
}
|
|
1629
|
-
createObservation(header, payload) {
|
|
1630
|
-
const previewLength = Math.min(this.maxPayloadPreviewBytes, payload.length);
|
|
1631
|
-
return Object.freeze({
|
|
1632
|
-
kind: header.kind,
|
|
1633
|
-
name: header.name,
|
|
1634
|
-
codec: header.codec,
|
|
1635
|
-
requestSeq: header.requestSeq,
|
|
1636
|
-
flowId: header.flowId,
|
|
1637
|
-
flowOrigin: header.flowOrigin,
|
|
1638
|
-
metadata: ZlinkStreamMetadataMap.from(header.metadata.values),
|
|
1639
|
-
payloadLength: payload.length,
|
|
1640
|
-
isCompressed: (header.flags & 4 /* PayloadCompressed */) !== 0,
|
|
1641
|
-
receivedAt: /* @__PURE__ */ new Date(),
|
|
1642
|
-
payloadPreview: previewLength === 0 ? new Uint8Array() : payload.slice(0, previewLength)
|
|
1643
|
-
});
|
|
1644
|
-
}
|
|
1645
|
-
scheduleDrain(signal) {
|
|
1646
|
-
if (this.draining) {
|
|
1647
|
-
return;
|
|
1648
|
-
}
|
|
1649
|
-
this.draining = true;
|
|
1650
|
-
queueMicrotask(() => {
|
|
1651
|
-
void this.drain(signal);
|
|
1652
|
-
});
|
|
1653
|
-
}
|
|
1654
|
-
async drain(signal) {
|
|
1655
|
-
try {
|
|
1656
|
-
while (this.queuedCount > 0) {
|
|
1657
|
-
const observation = this.takeObservation();
|
|
1658
|
-
if (observation === void 0) continue;
|
|
1659
|
-
const observers = [...this.observers];
|
|
1660
|
-
for (const observer of observers) {
|
|
1661
|
-
if (!this.observers.has(observer)) {
|
|
1662
|
-
continue;
|
|
1663
|
-
}
|
|
1664
|
-
try {
|
|
1665
|
-
await observer(observation, signal);
|
|
1666
|
-
} catch (cause) {
|
|
1667
|
-
await this.reportObserverFailed(cause, signal);
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
} finally {
|
|
1672
|
-
this.draining = false;
|
|
1673
|
-
if (this.queuedCount > 0) {
|
|
1674
|
-
this.scheduleDrain(signal);
|
|
1675
|
-
}
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
reportDropped(signal) {
|
|
1679
|
-
if (this.dropReportPending) {
|
|
1680
|
-
return;
|
|
1681
|
-
}
|
|
1682
|
-
this.dropReportPending = true;
|
|
1683
|
-
queueMicrotask(() => {
|
|
1684
|
-
void this.events.publishError({
|
|
1685
|
-
code: "observer-dropped" /* ObserverDropped */,
|
|
1686
|
-
message: "Inbound observer notification was dropped because the observer queue is full."
|
|
1687
|
-
}, signal).finally(() => {
|
|
1688
|
-
this.dropReportPending = false;
|
|
1689
|
-
}).catch(() => {
|
|
1690
|
-
});
|
|
1691
|
-
});
|
|
1692
|
-
}
|
|
1693
|
-
async reportObserverFailed(cause, signal) {
|
|
1694
|
-
try {
|
|
1695
|
-
await this.events.publishError({
|
|
1696
|
-
code: "observer-failed" /* ObserverFailed */,
|
|
1697
|
-
message: "Inbound observer callback failed.",
|
|
1698
|
-
cause
|
|
1699
|
-
}, signal);
|
|
1700
|
-
} catch {
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
takeObservation() {
|
|
1704
|
-
while (this.queueHead < this.queue.length && this.queue[this.queueHead] === void 0) {
|
|
1705
|
-
this.queueHead += 1;
|
|
1706
|
-
}
|
|
1707
|
-
const observation = this.queue[this.queueHead];
|
|
1708
|
-
if (observation === void 0) return void 0;
|
|
1709
|
-
this.queue[this.queueHead] = void 0;
|
|
1710
|
-
this.queueHead += 1;
|
|
1711
|
-
this.queuedCount -= 1;
|
|
1712
|
-
if (this.queuedCount === 0) {
|
|
1713
|
-
this.queue.length = 0;
|
|
1714
|
-
this.queueHead = 0;
|
|
1715
|
-
} else if (this.queueHead >= 1024 && this.queueHead * 2 >= this.queue.length) {
|
|
1716
|
-
this.queue.splice(0, this.queueHead);
|
|
1717
|
-
this.queueHead = 0;
|
|
1718
|
-
}
|
|
1719
|
-
return observation;
|
|
1720
|
-
}
|
|
1721
1636
|
};
|
|
1722
1637
|
|
|
1723
1638
|
// packages/stream-connector/src/Runtime/ZlinkStreamFrameSender.ts
|
|
@@ -1780,10 +1695,9 @@ function decodeSessionClosing(payload) {
|
|
|
1780
1695
|
|
|
1781
1696
|
// packages/stream-connector/src/Runtime/ZlinkStreamReceiveDispatcher.ts
|
|
1782
1697
|
var ZlinkStreamReceiveDispatcher = class {
|
|
1783
|
-
constructor(protocol, pendingRequests,
|
|
1698
|
+
constructor(protocol, pendingRequests, receivedMessages, frameSender, events, flowContext, metrics, serverClosing) {
|
|
1784
1699
|
this.protocol = protocol;
|
|
1785
1700
|
this.pendingRequests = pendingRequests;
|
|
1786
|
-
this.inboundObservers = inboundObservers;
|
|
1787
1701
|
this.receivedMessages = receivedMessages;
|
|
1788
1702
|
this.frameSender = frameSender;
|
|
1789
1703
|
this.events = events;
|
|
@@ -1830,7 +1744,6 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
1830
1744
|
return { available: true, inbound: true };
|
|
1831
1745
|
}
|
|
1832
1746
|
async dispatch(connection, header, payload, signal, flowEnabled) {
|
|
1833
|
-
this.inboundObservers.enqueue(header, payload, signal);
|
|
1834
1747
|
if (header.kind === 3 /* Response */ && header.requestSeq !== void 0) {
|
|
1835
1748
|
try {
|
|
1836
1749
|
if (!this.pendingRequests.resolve(header.requestSeq, {
|
|
@@ -1942,15 +1855,19 @@ function decodeRemoteError(protocol, header, payload) {
|
|
|
1942
1855
|
|
|
1943
1856
|
// packages/stream-connector/src/Runtime/ZlinkStreamConnectorLifecycle.ts
|
|
1944
1857
|
var ZlinkStreamConnectorLifecycle = class {
|
|
1945
|
-
constructor(options, pendingRequests, frameSender, receiveDispatcher, events, metrics) {
|
|
1858
|
+
constructor(options, pendingRequests, frameSender, receiveDispatcher, receivedMessages, events, metrics) {
|
|
1946
1859
|
this.options = options;
|
|
1947
1860
|
this.pendingRequests = pendingRequests;
|
|
1948
1861
|
this.frameSender = frameSender;
|
|
1949
1862
|
this.receiveDispatcher = receiveDispatcher;
|
|
1863
|
+
this.receivedMessages = receivedMessages;
|
|
1950
1864
|
this.events = events;
|
|
1951
1865
|
this.metrics = metrics;
|
|
1952
1866
|
}
|
|
1953
1867
|
receiveLoopAbort;
|
|
1868
|
+
receiveLoopSleeping = false;
|
|
1869
|
+
receiveLoopWake;
|
|
1870
|
+
receiveLoopSettled = [];
|
|
1954
1871
|
currentConnection;
|
|
1955
1872
|
connectionGeneration = 0;
|
|
1956
1873
|
currentState = "created" /* Created */;
|
|
@@ -2067,16 +1984,21 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2067
1984
|
if (errors.length === 1) throw errors[0];
|
|
2068
1985
|
if (errors.length > 1) throw new AggregateError(errors, "Stream connector close failed.");
|
|
2069
1986
|
}
|
|
1987
|
+
/**
|
|
1988
|
+
* Spec stream-connector 32 §7: `dispatch` runs the callbacks the receive loop
|
|
1989
|
+
* queued, it does not drive the transport. Receiving is the receive loop's
|
|
1990
|
+
* job in both dispatch modes, which is what lets a `Manual` consumer complete
|
|
1991
|
+
* a `waitFor` without pumping, and what keeps this call from blocking on an
|
|
1992
|
+
* idle connection. In `Manual` it first lets the loop settle whatever has
|
|
1993
|
+
* already arrived, so a packet the transport is holding is delivered by this
|
|
1994
|
+
* pump rather than the next one.
|
|
1995
|
+
*/
|
|
2070
1996
|
async dispatch(signal) {
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
await this.dispatchAvailable(connection, generation, signal);
|
|
2075
|
-
} catch (cause) {
|
|
2076
|
-
const error = toStreamError(cause, "frameDecodeFailed" /* FrameDecodeFailed */, "Stream dispatch failed.");
|
|
2077
|
-
await this.disconnectForTransportFailure(error, connection, generation);
|
|
2078
|
-
throw new ZlinkStreamException(error);
|
|
1997
|
+
throwIfAborted(signal);
|
|
1998
|
+
if (this.options.dispatchMode !== "immediate" /* Immediate */) {
|
|
1999
|
+
await this.settleReceiveLoop();
|
|
2079
2000
|
}
|
|
2001
|
+
await this.receivedMessages.pump();
|
|
2080
2002
|
}
|
|
2081
2003
|
connectionForSend() {
|
|
2082
2004
|
if (this.currentConnection === void 0 || this.currentState !== "connected" /* Connected */) {
|
|
@@ -2145,8 +2067,12 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2145
2067
|
this.heartbeatTimer = void 0;
|
|
2146
2068
|
}
|
|
2147
2069
|
}
|
|
2070
|
+
// Spec stream-connector 32 §7: the receive loop runs in both dispatch modes.
|
|
2071
|
+
// `Manual` only changes what the loop does with a frame — it queues the
|
|
2072
|
+
// registered callbacks instead of running them — never whether frames are
|
|
2073
|
+
// read off the transport.
|
|
2148
2074
|
startReceiveLoop() {
|
|
2149
|
-
if (this.
|
|
2075
|
+
if (this.currentConnection?.read === void 0) {
|
|
2150
2076
|
return;
|
|
2151
2077
|
}
|
|
2152
2078
|
this.stopReceiveLoop();
|
|
@@ -2159,19 +2085,66 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2159
2085
|
stopReceiveLoop() {
|
|
2160
2086
|
this.receiveLoopAbort?.abort();
|
|
2161
2087
|
this.receiveLoopAbort = void 0;
|
|
2088
|
+
this.receiveLoopSleeping = false;
|
|
2089
|
+
this.releaseReceiveLoopSettled();
|
|
2162
2090
|
}
|
|
2163
2091
|
async runReceiveLoop(connection, generation, signal) {
|
|
2164
2092
|
try {
|
|
2165
2093
|
while (this.shouldContinueReceiveLoop(connection, generation, signal)) {
|
|
2166
2094
|
const dispatched = await this.dispatchAvailable(connection, generation, signal);
|
|
2167
2095
|
if (!dispatched && this.shouldContinueReceiveLoop(connection, generation, signal)) {
|
|
2168
|
-
await
|
|
2096
|
+
await this.sleepUntilWork(signal);
|
|
2169
2097
|
}
|
|
2170
2098
|
}
|
|
2171
2099
|
} catch (cause) {
|
|
2172
2100
|
if (signal.aborted) return;
|
|
2173
2101
|
const error = toStreamError(cause, "frameDecodeFailed" /* FrameDecodeFailed */, "Receive loop failed.");
|
|
2174
2102
|
await this.disconnectForTransportFailure(error, connection, generation);
|
|
2103
|
+
} finally {
|
|
2104
|
+
this.receiveLoopSleeping = false;
|
|
2105
|
+
this.releaseReceiveLoopSettled();
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
// A transport whose read resolves only when a frame arrives parks the loop
|
|
2109
|
+
// inside that read; one that reports "nothing available" instead parks it
|
|
2110
|
+
// here. Both are the loop waiting for new data, and `dispatch` treats them
|
|
2111
|
+
// the same way.
|
|
2112
|
+
async sleepUntilWork(signal) {
|
|
2113
|
+
this.receiveLoopSleeping = true;
|
|
2114
|
+
this.releaseReceiveLoopSettled();
|
|
2115
|
+
try {
|
|
2116
|
+
await new Promise((resolve) => {
|
|
2117
|
+
const finish = () => {
|
|
2118
|
+
clearTimeout(timer);
|
|
2119
|
+
signal.removeEventListener("abort", onAbort);
|
|
2120
|
+
this.receiveLoopWake = void 0;
|
|
2121
|
+
resolve();
|
|
2122
|
+
};
|
|
2123
|
+
const onAbort = () => finish();
|
|
2124
|
+
const timer = setTimeout(finish, 1);
|
|
2125
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2126
|
+
this.receiveLoopWake = finish;
|
|
2127
|
+
});
|
|
2128
|
+
} finally {
|
|
2129
|
+
this.receiveLoopSleeping = false;
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
// Returns once the loop has consumed everything the transport already had.
|
|
2133
|
+
// A loop that is mid-batch, or parked inside a read that has not produced a
|
|
2134
|
+
// frame, is already caught up, so only a sleeping loop is woken and awaited.
|
|
2135
|
+
async settleReceiveLoop() {
|
|
2136
|
+
if (this.receiveLoopAbort === void 0 || !this.receiveLoopSleeping) {
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
const settled = new Promise((resolve) => {
|
|
2140
|
+
this.receiveLoopSettled.push(resolve);
|
|
2141
|
+
});
|
|
2142
|
+
this.receiveLoopWake?.();
|
|
2143
|
+
await settled;
|
|
2144
|
+
}
|
|
2145
|
+
releaseReceiveLoopSettled() {
|
|
2146
|
+
for (const resolve of this.receiveLoopSettled.splice(0)) {
|
|
2147
|
+
resolve();
|
|
2175
2148
|
}
|
|
2176
2149
|
}
|
|
2177
2150
|
shouldContinueReceiveLoop(connection, generation, signal) {
|
|
@@ -2560,7 +2533,6 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2560
2533
|
pendingRequests = new ZlinkStreamPendingRequests();
|
|
2561
2534
|
frameSender;
|
|
2562
2535
|
receiveDispatcher;
|
|
2563
|
-
inboundObservers;
|
|
2564
2536
|
diagnosticsLevelCell;
|
|
2565
2537
|
options;
|
|
2566
2538
|
constructor(options) {
|
|
@@ -2575,19 +2547,13 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2575
2547
|
const metrics = new ZlinkStreamRuntimeMetrics(this.options);
|
|
2576
2548
|
const protocol = new ZlinkStreamFrameProtocol(this.options);
|
|
2577
2549
|
this.frameSender = new ZlinkStreamFrameSender(protocol, flowContext, metrics);
|
|
2578
|
-
this.inboundObservers = new ZlinkStreamInboundObservers(
|
|
2579
|
-
this.options.maxInboundObserverNotifications,
|
|
2580
|
-
this.options.maxInboundObserverPayloadPreviewBytes,
|
|
2581
|
-
this.events
|
|
2582
|
-
);
|
|
2583
2550
|
this.receivedMessages = new ZlinkStreamReceivedMessages(
|
|
2584
|
-
this.
|
|
2585
|
-
this.
|
|
2551
|
+
this.events,
|
|
2552
|
+
this.options.dispatchMode === "immediate" /* Immediate */
|
|
2586
2553
|
);
|
|
2587
2554
|
this.receiveDispatcher = new ZlinkStreamReceiveDispatcher(
|
|
2588
2555
|
protocol,
|
|
2589
2556
|
this.pendingRequests,
|
|
2590
|
-
this.inboundObservers,
|
|
2591
2557
|
this.receivedMessages,
|
|
2592
2558
|
this.frameSender,
|
|
2593
2559
|
this.events,
|
|
@@ -2600,6 +2566,7 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2600
2566
|
this.pendingRequests,
|
|
2601
2567
|
this.frameSender,
|
|
2602
2568
|
this.receiveDispatcher,
|
|
2569
|
+
this.receivedMessages,
|
|
2603
2570
|
this.events,
|
|
2604
2571
|
metrics
|
|
2605
2572
|
);
|
|
@@ -2667,12 +2634,6 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2667
2634
|
const encoded = this.encodePayload(payload, messageType);
|
|
2668
2635
|
return new ZlinkStreamRequestBuilder(this, this.resolveNameOrDefault(encoded), encoded);
|
|
2669
2636
|
}
|
|
2670
|
-
observeInbound(observer) {
|
|
2671
|
-
if (this.lifecycle.state !== "created" /* Created */) {
|
|
2672
|
-
throw connectorError("validationFailed" /* ValidationFailed */, "Inbound observers must be registered before connecting.");
|
|
2673
|
-
}
|
|
2674
|
-
return this.inboundObservers.add(observer);
|
|
2675
|
-
}
|
|
2676
2637
|
on(name, handler, messageType) {
|
|
2677
2638
|
const encodedHandler = (message, signal) => handler({
|
|
2678
2639
|
name: message.name,
|
|
@@ -2726,7 +2687,10 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2726
2687
|
finish(connectorError("requestTimeout" /* RequestTimeout */, "Wait for stream message timed out."));
|
|
2727
2688
|
}, timeoutMs);
|
|
2728
2689
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2729
|
-
disposable = this.receivedMessages.
|
|
2690
|
+
disposable = this.receivedMessages.observe(name, (message) => {
|
|
2691
|
+
if (done) {
|
|
2692
|
+
return false;
|
|
2693
|
+
}
|
|
2730
2694
|
try {
|
|
2731
2695
|
const decoded = {
|
|
2732
2696
|
name: message.name,
|
|
@@ -2735,12 +2699,14 @@ var DefaultZlinkStreamConnector = class {
|
|
|
2735
2699
|
flowId: message.flowId,
|
|
2736
2700
|
flowOrigin: message.flowOrigin
|
|
2737
2701
|
};
|
|
2738
|
-
if (predicate(decoded)) {
|
|
2739
|
-
|
|
2702
|
+
if (!predicate(decoded)) {
|
|
2703
|
+
return false;
|
|
2740
2704
|
}
|
|
2705
|
+
finish(void 0, decoded);
|
|
2741
2706
|
} catch (cause) {
|
|
2742
2707
|
finish(cause);
|
|
2743
2708
|
}
|
|
2709
|
+
return true;
|
|
2744
2710
|
});
|
|
2745
2711
|
});
|
|
2746
2712
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zlink-systems/stream-connector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@zlink-systems/stream-wire": "0.
|
|
24
|
+
"@zlink-systems/stream-wire": "0.15.0"
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Disposable, ZlinkStreamInboundObservation } from '../Contracts';
|
|
2
|
-
import type { ZlinkStreamHeader } from '../Contracts/ZlinkStreamModels';
|
|
3
|
-
import type { ZlinkStreamConnectorEvents } from './ZlinkStreamConnectorEvents';
|
|
4
|
-
type InboundObserver = (observation: ZlinkStreamInboundObservation, signal?: AbortSignal) => Promise<void> | void;
|
|
5
|
-
export declare class ZlinkStreamInboundObservers {
|
|
6
|
-
private readonly maxNotifications;
|
|
7
|
-
private readonly maxPayloadPreviewBytes;
|
|
8
|
-
private readonly events;
|
|
9
|
-
private readonly observers;
|
|
10
|
-
private readonly queue;
|
|
11
|
-
private queueHead;
|
|
12
|
-
private queuedCount;
|
|
13
|
-
private draining;
|
|
14
|
-
private dropReportPending;
|
|
15
|
-
constructor(maxNotifications: number, maxPayloadPreviewBytes: number, events: ZlinkStreamConnectorEvents);
|
|
16
|
-
add(observer: InboundObserver): Disposable;
|
|
17
|
-
enqueue(header: ZlinkStreamHeader, payload: Uint8Array, signal?: AbortSignal): void;
|
|
18
|
-
private createObservation;
|
|
19
|
-
private scheduleDrain;
|
|
20
|
-
private drain;
|
|
21
|
-
private reportDropped;
|
|
22
|
-
private reportObserverFailed;
|
|
23
|
-
private takeObservation;
|
|
24
|
-
}
|
|
25
|
-
export {};
|