@zlink-systems/stream-connector 0.25.0 → 0.27.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/ZlinkStreamConnectorOptions.d.ts +1 -0
- package/dist/browser/Contracts/ZlinkStreamEnums.d.ts +3 -17
- package/dist/browser/Contracts/index.d.ts +1 -1
- package/dist/browser/Runtime/Calls/ZlinkStreamCallBuilders.d.ts +2 -1
- package/dist/browser/Runtime/Protocol/ZlinkSessionClosing.d.ts +2 -7
- package/dist/browser/Runtime/Protocol/ZlinkStreamFrameProtocol.d.ts +3 -4
- package/dist/browser/Runtime/Protocol/ZlinkStreamMetadataCodec.d.ts +1 -3
- package/dist/browser/Runtime/Transport/BrowserWebSocketConnection.d.ts +6 -2
- package/dist/browser/Runtime/ZlinkStreamActors.d.ts +2 -4
- package/dist/browser/Runtime/ZlinkStreamConnector.d.ts +4 -4
- package/dist/browser/Runtime/ZlinkStreamConnectorEvents.d.ts +18 -3
- package/dist/browser/Runtime/ZlinkStreamConnectorLifecycle.d.ts +37 -35
- package/dist/browser/Runtime/ZlinkStreamConnectorOptions.d.ts +1 -0
- package/dist/browser/Runtime/ZlinkStreamFrameSender.d.ts +21 -5
- package/dist/browser/Runtime/ZlinkStreamPendingRequests.d.ts +2 -2
- package/dist/browser/Runtime/ZlinkStreamReceiveDispatcher.d.ts +21 -5
- package/dist/browser/Runtime/ZlinkStreamReceivedMessages.d.ts +36 -14
- package/dist/browser/Runtime/ZlinkStreamSupport.d.ts +8 -7
- package/dist/browser/index.global.js +1349 -997
- package/dist/browser/index.mjs +1401 -1042
- package/package.json +2 -2
package/dist/browser/index.mjs
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
// packages/stream-wire/src/lz4-pickle.ts
|
|
2
|
+
var LZ4_MIN_MATCH_LENGTH = 4;
|
|
3
|
+
var LZ4_MATCH_OFFSET_BYTES = 2;
|
|
4
|
+
var LZ4_EXTENDED_LENGTH_NIBBLE = 15;
|
|
5
|
+
var LZ4_EXTENDED_LENGTH_BYTE = 255;
|
|
2
6
|
var defaultMaxDecompressedPayloadSize = 64 * 1024;
|
|
3
7
|
function lz4PickleUncompressed(payload) {
|
|
4
8
|
if (payload.length === 0) {
|
|
@@ -9,11 +13,7 @@ function lz4PickleUncompressed(payload) {
|
|
|
9
13
|
pickled.set(payload, 1);
|
|
10
14
|
return pickled;
|
|
11
15
|
}
|
|
12
|
-
function
|
|
13
|
-
const maxDecompressedSize = maxSize ?? defaultMaxDecompressedPayloadSize;
|
|
14
|
-
if (payload.length === 0) {
|
|
15
|
-
return new Uint8Array();
|
|
16
|
-
}
|
|
16
|
+
function readLz4Pickle(payload) {
|
|
17
17
|
const header = payload[0];
|
|
18
18
|
if ((header & 7) !== 0) {
|
|
19
19
|
throw new Error("Unexpected LZ4 pickle version.");
|
|
@@ -25,7 +25,17 @@ function lz4UnpicklePayload(payload, maxSize) {
|
|
|
25
25
|
}
|
|
26
26
|
const data = payload.subarray(dataOffset);
|
|
27
27
|
const resultDiff = sizeOfDiff === 0 ? 0 : readLittleEndian(payload, 1, sizeOfDiff);
|
|
28
|
-
|
|
28
|
+
return { data, resultDiff, resultLength: data.length + resultDiff };
|
|
29
|
+
}
|
|
30
|
+
function lz4PickledLength(payload) {
|
|
31
|
+
return payload.length === 0 ? 0 : readLz4Pickle(payload).resultLength;
|
|
32
|
+
}
|
|
33
|
+
function lz4UnpicklePayload(payload, maxSize) {
|
|
34
|
+
const maxDecompressedSize = maxSize ?? defaultMaxDecompressedPayloadSize;
|
|
35
|
+
if (payload.length === 0) {
|
|
36
|
+
return new Uint8Array();
|
|
37
|
+
}
|
|
38
|
+
const { data, resultDiff, resultLength } = readLz4Pickle(payload);
|
|
29
39
|
if (resultLength > maxDecompressedSize) {
|
|
30
40
|
throw new Error("LZ4 decoded payload exceeds maximum stream payload size.");
|
|
31
41
|
}
|
|
@@ -68,15 +78,15 @@ function decodeLz4Block(source, resultLength) {
|
|
|
68
78
|
if (sourceOffset >= source.length) {
|
|
69
79
|
break;
|
|
70
80
|
}
|
|
71
|
-
if (source.length - sourceOffset <
|
|
81
|
+
if (source.length - sourceOffset < LZ4_MATCH_OFFSET_BYTES) {
|
|
72
82
|
throw new Error("LZ4 match offset is incomplete.");
|
|
73
83
|
}
|
|
74
84
|
const matchOffset = source[sourceOffset] | source[sourceOffset + 1] << 8;
|
|
75
|
-
sourceOffset +=
|
|
85
|
+
sourceOffset += LZ4_MATCH_OFFSET_BYTES;
|
|
76
86
|
if (matchOffset === 0 || matchOffset > targetOffset) {
|
|
77
87
|
throw new Error("LZ4 match offset is invalid.");
|
|
78
88
|
}
|
|
79
|
-
const matchLength = readLz4Length(source, token & 15, () => sourceOffset++) +
|
|
89
|
+
const matchLength = readLz4Length(source, token & 15, () => sourceOffset++) + LZ4_MIN_MATCH_LENGTH;
|
|
80
90
|
if (target.length - targetOffset < matchLength) {
|
|
81
91
|
throw new Error("LZ4 match run exceeds output size.");
|
|
82
92
|
}
|
|
@@ -92,7 +102,7 @@ function decodeLz4Block(source, resultLength) {
|
|
|
92
102
|
}
|
|
93
103
|
function readLz4Length(source, nibble, nextOffset) {
|
|
94
104
|
let length = nibble;
|
|
95
|
-
if (length !==
|
|
105
|
+
if (length !== LZ4_EXTENDED_LENGTH_NIBBLE) {
|
|
96
106
|
return length;
|
|
97
107
|
}
|
|
98
108
|
for (; ; ) {
|
|
@@ -102,7 +112,7 @@ function readLz4Length(source, nibble, nextOffset) {
|
|
|
102
112
|
}
|
|
103
113
|
const value = source[offset];
|
|
104
114
|
length += value;
|
|
105
|
-
if (value !==
|
|
115
|
+
if (value !== LZ4_EXTENDED_LENGTH_BYTE) {
|
|
106
116
|
return length;
|
|
107
117
|
}
|
|
108
118
|
}
|
|
@@ -116,54 +126,200 @@ var ZlinkStreamCodec = /* @__PURE__ */ ((ZlinkStreamCodec2) => {
|
|
|
116
126
|
ZlinkStreamCodec2[ZlinkStreamCodec2["Protobuf"] = 3] = "Protobuf";
|
|
117
127
|
return ZlinkStreamCodec2;
|
|
118
128
|
})(ZlinkStreamCodec || {});
|
|
129
|
+
var ZlinkStreamMessageKind = /* @__PURE__ */ ((ZlinkStreamMessageKind2) => {
|
|
130
|
+
ZlinkStreamMessageKind2[ZlinkStreamMessageKind2["Send"] = 1] = "Send";
|
|
131
|
+
ZlinkStreamMessageKind2[ZlinkStreamMessageKind2["Request"] = 2] = "Request";
|
|
132
|
+
ZlinkStreamMessageKind2[ZlinkStreamMessageKind2["Response"] = 3] = "Response";
|
|
133
|
+
ZlinkStreamMessageKind2[ZlinkStreamMessageKind2["Error"] = 4] = "Error";
|
|
134
|
+
ZlinkStreamMessageKind2[ZlinkStreamMessageKind2["Control"] = 5] = "Control";
|
|
135
|
+
return ZlinkStreamMessageKind2;
|
|
136
|
+
})(ZlinkStreamMessageKind || {});
|
|
137
|
+
var ZlinkStreamHeaderFlags = /* @__PURE__ */ ((ZlinkStreamHeaderFlags2) => {
|
|
138
|
+
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["None"] = 0] = "None";
|
|
139
|
+
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["HasRequestSeq"] = 1] = "HasRequestSeq";
|
|
140
|
+
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["HasMetadata"] = 2] = "HasMetadata";
|
|
141
|
+
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["PayloadCompressed"] = 4] = "PayloadCompressed";
|
|
142
|
+
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["HasCorrelationId"] = 8] = "HasCorrelationId";
|
|
143
|
+
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["HasFlowId"] = 16] = "HasFlowId";
|
|
144
|
+
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["HasActorSlot"] = 32] = "HasActorSlot";
|
|
145
|
+
return ZlinkStreamHeaderFlags2;
|
|
146
|
+
})(ZlinkStreamHeaderFlags || {});
|
|
147
|
+
var ZlinkStreamCloseReasonCode = /* @__PURE__ */ ((ZlinkStreamCloseReasonCode2) => {
|
|
148
|
+
ZlinkStreamCloseReasonCode2[ZlinkStreamCloseReasonCode2["ClientClose"] = 1] = "ClientClose";
|
|
149
|
+
ZlinkStreamCloseReasonCode2[ZlinkStreamCloseReasonCode2["IdleTimeout"] = 2] = "IdleTimeout";
|
|
150
|
+
ZlinkStreamCloseReasonCode2[ZlinkStreamCloseReasonCode2["HeartbeatTimeout"] = 3] = "HeartbeatTimeout";
|
|
151
|
+
ZlinkStreamCloseReasonCode2[ZlinkStreamCloseReasonCode2["ServerDrain"] = 4] = "ServerDrain";
|
|
152
|
+
ZlinkStreamCloseReasonCode2[ZlinkStreamCloseReasonCode2["ProtocolError"] = 5] = "ProtocolError";
|
|
153
|
+
ZlinkStreamCloseReasonCode2[ZlinkStreamCloseReasonCode2["TransportError"] = 6] = "TransportError";
|
|
154
|
+
return ZlinkStreamCloseReasonCode2;
|
|
155
|
+
})(ZlinkStreamCloseReasonCode || {});
|
|
156
|
+
var unicodeWhiteSpaceOnly = /^\p{White_Space}*$/u;
|
|
157
|
+
var validMessageKinds = new Set(
|
|
158
|
+
Object.values(ZlinkStreamMessageKind).filter((value) => typeof value === "number")
|
|
159
|
+
);
|
|
160
|
+
var validCodecs = new Set(
|
|
161
|
+
Object.values(ZlinkStreamCodec).filter((value) => typeof value === "number")
|
|
162
|
+
);
|
|
163
|
+
function isStreamWireMessageKind(value) {
|
|
164
|
+
return validMessageKinds.has(value);
|
|
165
|
+
}
|
|
166
|
+
function isStreamWireCodec(value) {
|
|
167
|
+
return validCodecs.has(value);
|
|
168
|
+
}
|
|
169
|
+
var UTF8_TWO_BYTE_MIN = 128;
|
|
170
|
+
var UTF8_THREE_BYTE_MIN = 2048;
|
|
171
|
+
var UTF16_HIGH_SURROGATE_MIN = 55296;
|
|
172
|
+
var UTF16_HIGH_SURROGATE_MAX = 56319;
|
|
173
|
+
var UTF16_LOW_SURROGATE_MIN = 56320;
|
|
174
|
+
var UTF16_LOW_SURROGATE_MAX = 57343;
|
|
175
|
+
var UINT8_MAX = 255;
|
|
176
|
+
var UINT16_MAX = 65535;
|
|
177
|
+
var UINT16_BYTES = 2;
|
|
178
|
+
var UINT32_BYTES = 4;
|
|
179
|
+
var UINT64_BYTES = 8;
|
|
180
|
+
var UINT64_MAX = 0xffffffffffffffffn;
|
|
181
|
+
var UINT32_MAX = 4294967295;
|
|
182
|
+
var ZLINK_STREAM_FRAME_PREFIX_BYTES = UINT16_BYTES + UINT32_BYTES;
|
|
183
|
+
var ZLINK_STREAM_MAX_PACKET_NAME_BYTES = UINT8_MAX;
|
|
184
|
+
var ZLINK_STREAM_MAX_METADATA_BYTES = 1024;
|
|
185
|
+
var FLOW_ID_BYTES = 36;
|
|
186
|
+
var FLOW_FIELDS_BYTES = FLOW_ID_BYTES + 1;
|
|
187
|
+
var HEADER_MIN_BYTES = 5;
|
|
188
|
+
var HEADER_FLAG_MASK = 1 /* HasRequestSeq */ | 2 /* HasMetadata */ | 4 /* PayloadCompressed */ | 8 /* HasCorrelationId */ | 16 /* HasFlowId */ | 32 /* HasActorSlot */;
|
|
189
|
+
var CONTROL_PAYLOAD_VERSION = 1;
|
|
190
|
+
var ACTOR_SLOT_OFFSET = 1;
|
|
191
|
+
var ACTOR_ID_LENGTH_OFFSET = ACTOR_SLOT_OFFSET + UINT16_BYTES;
|
|
192
|
+
var ACTOR_BOUND_PREFIX_BYTES = ACTOR_ID_LENGTH_OFFSET + 1;
|
|
193
|
+
var ACTOR_UNBOUND_BYTES = ACTOR_SLOT_OFFSET + UINT16_BYTES;
|
|
194
|
+
var CLOSING_DIAGNOSTIC_LENGTH_OFFSET = 2;
|
|
195
|
+
var CLOSING_PREFIX_BYTES = CLOSING_DIAGNOSTIC_LENGTH_OFFSET + UINT16_BYTES;
|
|
196
|
+
var MAX_CLOSING_DIAGNOSTIC_BYTES = 512;
|
|
197
|
+
function decodeStreamWireActorBoundPayload(payload) {
|
|
198
|
+
if (payload.length < ACTOR_BOUND_PREFIX_BYTES + 1 || payload[0] !== CONTROL_PAYLOAD_VERSION) {
|
|
199
|
+
throw new Error("Actor bound payload is invalid.");
|
|
200
|
+
}
|
|
201
|
+
const slot = readUInt16BE(payload, ACTOR_SLOT_OFFSET);
|
|
202
|
+
const idLength = payload[ACTOR_ID_LENGTH_OFFSET];
|
|
203
|
+
if (slot === 0 || idLength === 0 || payload.length !== ACTOR_BOUND_PREFIX_BYTES + idLength) {
|
|
204
|
+
throw new Error("Actor bound payload is invalid.");
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
return { slot, actorId: decodeControlText(payload.subarray(ACTOR_BOUND_PREFIX_BYTES)) };
|
|
208
|
+
} catch (cause) {
|
|
209
|
+
throw new Error("Actor id is not valid UTF-8.", { cause });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function decodeStreamWireActorUnboundPayload(payload) {
|
|
213
|
+
if (payload.length !== ACTOR_UNBOUND_BYTES || payload[0] !== CONTROL_PAYLOAD_VERSION) {
|
|
214
|
+
throw new Error("Actor unbound payload is invalid.");
|
|
215
|
+
}
|
|
216
|
+
return readUInt16BE(payload, ACTOR_SLOT_OFFSET);
|
|
217
|
+
}
|
|
218
|
+
function decodeStreamWireSessionClosing(payload) {
|
|
219
|
+
if (payload.length < CLOSING_PREFIX_BYTES || payload[0] !== CONTROL_PAYLOAD_VERSION) {
|
|
220
|
+
throw new Error("Unsupported session-closing version.");
|
|
221
|
+
}
|
|
222
|
+
const closeReason = ZlinkStreamCloseReasonCode[payload[1]];
|
|
223
|
+
if (closeReason === void 0) throw new Error("Unknown session-closing reason.");
|
|
224
|
+
const length = readUInt16BE(payload, CLOSING_DIAGNOSTIC_LENGTH_OFFSET);
|
|
225
|
+
if (length > MAX_CLOSING_DIAGNOSTIC_BYTES || payload.length !== CLOSING_PREFIX_BYTES + length) {
|
|
226
|
+
throw new Error("Invalid session-closing diagnostic length.");
|
|
227
|
+
}
|
|
228
|
+
const diagnostic = length === 0 ? void 0 : decodeControlText(payload.subarray(CLOSING_PREFIX_BYTES));
|
|
229
|
+
return { closeReason, diagnostic };
|
|
230
|
+
}
|
|
231
|
+
function decodeControlText(value) {
|
|
232
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(value);
|
|
233
|
+
}
|
|
234
|
+
function splitStreamWireFrames(chunk) {
|
|
235
|
+
if (chunk.length === 0) throw new Error("Stream frame prefix is incomplete.");
|
|
236
|
+
const frames = [];
|
|
237
|
+
let offset = 0;
|
|
238
|
+
while (offset < chunk.length) {
|
|
239
|
+
const remaining = chunk.length - offset;
|
|
240
|
+
if (remaining < ZLINK_STREAM_FRAME_PREFIX_BYTES) {
|
|
241
|
+
throw new Error("Stream frame prefix is incomplete.");
|
|
242
|
+
}
|
|
243
|
+
const headerLength = readUInt16BE(chunk, offset);
|
|
244
|
+
const payloadLength = readUInt32BE(chunk, offset + UINT16_BYTES);
|
|
245
|
+
const frameLength = ZLINK_STREAM_FRAME_PREFIX_BYTES + headerLength + payloadLength;
|
|
246
|
+
if (frameLength > remaining) throw new Error("Frame length does not match prefix.");
|
|
247
|
+
frames.push(chunk.subarray(offset, offset + frameLength));
|
|
248
|
+
offset += frameLength;
|
|
249
|
+
}
|
|
250
|
+
return frames;
|
|
251
|
+
}
|
|
252
|
+
var ZLINK_STREAM_RESERVED_PACKET_NAME_PREFIX = "$zlink.";
|
|
253
|
+
var ZlinkStreamControlPacket = Object.freeze({
|
|
254
|
+
HeartbeatPing: `${ZLINK_STREAM_RESERVED_PACKET_NAME_PREFIX}heartbeat.ping`,
|
|
255
|
+
HeartbeatPong: `${ZLINK_STREAM_RESERVED_PACKET_NAME_PREFIX}heartbeat.pong`,
|
|
256
|
+
ActorBound: `${ZLINK_STREAM_RESERVED_PACKET_NAME_PREFIX}actor.bound`,
|
|
257
|
+
ActorUnbound: `${ZLINK_STREAM_RESERVED_PACKET_NAME_PREFIX}actor.unbound`,
|
|
258
|
+
SessionClosing: "session-closing"
|
|
259
|
+
});
|
|
260
|
+
var ZlinkStreamContentType = Object.freeze({
|
|
261
|
+
Raw: "application/octet-stream",
|
|
262
|
+
Json: "application/json",
|
|
263
|
+
MessagePack: "application/x-msgpack",
|
|
264
|
+
Protobuf: "application/x-protobuf"
|
|
265
|
+
});
|
|
266
|
+
var contentTypesByCodec = /* @__PURE__ */ new Map([
|
|
267
|
+
[0 /* Raw */, ZlinkStreamContentType.Raw],
|
|
268
|
+
[1 /* Json */, ZlinkStreamContentType.Json],
|
|
269
|
+
[2 /* MessagePack */, ZlinkStreamContentType.MessagePack],
|
|
270
|
+
[3 /* Protobuf */, ZlinkStreamContentType.Protobuf]
|
|
271
|
+
]);
|
|
272
|
+
var codecsByContentType = new Map(
|
|
273
|
+
Array.from(contentTypesByCodec, ([codec, contentType]) => [contentType, codec])
|
|
274
|
+
);
|
|
119
275
|
var defaultHeaderFlags = {
|
|
120
|
-
hasRequestSeq: 1
|
|
121
|
-
hasMetadata: 2
|
|
122
|
-
hasCorrelationId: 8
|
|
123
|
-
hasFlowId: 16
|
|
124
|
-
hasActorSlot: 32
|
|
276
|
+
hasRequestSeq: 1 /* HasRequestSeq */,
|
|
277
|
+
hasMetadata: 2 /* HasMetadata */,
|
|
278
|
+
hasCorrelationId: 8 /* HasCorrelationId */,
|
|
279
|
+
hasFlowId: 16 /* HasFlowId */,
|
|
280
|
+
hasActorSlot: 32 /* HasActorSlot */
|
|
125
281
|
};
|
|
126
282
|
var ZLINK_STREAM_FORMAT_MARKER = 242;
|
|
127
|
-
var ZLINK_STREAM_RESPONSE_KIND = 3;
|
|
128
|
-
var ZLINK_STREAM_ERROR_KIND = 4;
|
|
129
283
|
function encodeStreamWireFrame(header, payload) {
|
|
130
|
-
if (header.length >
|
|
284
|
+
if (header.length > UINT16_MAX) {
|
|
131
285
|
throw new Error("Stream header is too large.");
|
|
132
286
|
}
|
|
133
|
-
if (payload.length >
|
|
287
|
+
if (payload.length > UINT32_MAX) {
|
|
134
288
|
throw new Error("Stream payload is too large.");
|
|
135
289
|
}
|
|
136
|
-
const frame = new Uint8Array(
|
|
290
|
+
const frame = new Uint8Array(ZLINK_STREAM_FRAME_PREFIX_BYTES + header.length + payload.length);
|
|
137
291
|
writeUInt16BE(frame, 0, header.length);
|
|
138
|
-
writeUInt32BE(frame,
|
|
139
|
-
frame.set(header,
|
|
140
|
-
frame.set(payload,
|
|
292
|
+
writeUInt32BE(frame, UINT16_BYTES, payload.length);
|
|
293
|
+
frame.set(header, ZLINK_STREAM_FRAME_PREFIX_BYTES);
|
|
294
|
+
frame.set(payload, ZLINK_STREAM_FRAME_PREFIX_BYTES + header.length);
|
|
141
295
|
return frame;
|
|
142
296
|
}
|
|
143
297
|
function decodeStreamWireFrame(frame) {
|
|
144
|
-
if (frame.length <
|
|
298
|
+
if (frame.length < ZLINK_STREAM_FRAME_PREFIX_BYTES) {
|
|
145
299
|
throw new Error("Stream frame prefix is incomplete.");
|
|
146
300
|
}
|
|
147
301
|
const headerLength = readUInt16BE(frame, 0);
|
|
148
|
-
const payloadLength = readUInt32BE(frame,
|
|
149
|
-
if (frame.length !==
|
|
302
|
+
const payloadLength = readUInt32BE(frame, UINT16_BYTES);
|
|
303
|
+
if (frame.length !== ZLINK_STREAM_FRAME_PREFIX_BYTES + headerLength + payloadLength) {
|
|
150
304
|
throw new Error("Stream frame length does not match prefix.");
|
|
151
305
|
}
|
|
152
306
|
return {
|
|
153
|
-
header: frame.slice(
|
|
154
|
-
|
|
307
|
+
header: frame.slice(
|
|
308
|
+
ZLINK_STREAM_FRAME_PREFIX_BYTES,
|
|
309
|
+
ZLINK_STREAM_FRAME_PREFIX_BYTES + headerLength
|
|
310
|
+
),
|
|
311
|
+
payload: frame.slice(ZLINK_STREAM_FRAME_PREFIX_BYTES + headerLength)
|
|
155
312
|
};
|
|
156
313
|
}
|
|
157
314
|
function encodeStreamWireHeader(header, flagOverrides) {
|
|
158
315
|
const flags = flagOverrides ?? defaultHeaderFlags;
|
|
159
316
|
const reply = isReplyKind(header.kind);
|
|
160
317
|
const packetName = reply ? "" : header.name;
|
|
161
|
-
|
|
162
|
-
const nameBytes = utf8Encode(packetName);
|
|
318
|
+
const nameBytes = reply ? new Uint8Array() : validateStreamWirePacketName(packetName);
|
|
163
319
|
const hasRequestSeq = header.requestSeq !== void 0;
|
|
164
320
|
const hasMetadata = header.metadata.size > 0;
|
|
165
321
|
const correlationBytes = header.correlationId !== void 0 && header.correlationId.length > 0 ? utf8Encode(header.correlationId) : void 0;
|
|
166
|
-
if (correlationBytes !== void 0 && correlationBytes.length >
|
|
322
|
+
if (correlationBytes !== void 0 && correlationBytes.length > UINT8_MAX) {
|
|
167
323
|
throw new Error("Stream correlation id is too large.");
|
|
168
324
|
}
|
|
169
325
|
const hasCorrelation = correlationBytes !== void 0;
|
|
@@ -176,7 +332,7 @@ function encodeStreamWireHeader(header, flagOverrides) {
|
|
|
176
332
|
if (header.flowOrigin !== void 0 && ![1, 2, 3, 4].includes(header.flowOrigin)) {
|
|
177
333
|
throw new Error("Stream flow origin is invalid.");
|
|
178
334
|
}
|
|
179
|
-
if (header.actorSlot !== void 0 && (!Number.isInteger(header.actorSlot) || header.actorSlot < 1 || header.actorSlot >
|
|
335
|
+
if (header.actorSlot !== void 0 && (!Number.isInteger(header.actorSlot) || header.actorSlot < 1 || header.actorSlot > UINT16_MAX)) {
|
|
180
336
|
throw new Error("Stream actor slot is invalid.");
|
|
181
337
|
}
|
|
182
338
|
let headerFlags = header.flags;
|
|
@@ -186,7 +342,7 @@ function encodeStreamWireHeader(header, flagOverrides) {
|
|
|
186
342
|
headerFlags = hasFlow ? headerFlags | flags.hasFlowId : headerFlags & ~flags.hasFlowId;
|
|
187
343
|
headerFlags = hasActorSlot ? headerFlags | flags.hasActorSlot : headerFlags & ~flags.hasActorSlot;
|
|
188
344
|
const metadataBytes = hasMetadata ? encodeStreamWireMetadata(header.metadata) : new Uint8Array();
|
|
189
|
-
const size = 4 + (hasRequestSeq ?
|
|
345
|
+
const size = 4 + (hasRequestSeq ? UINT64_BYTES : 0) + 1 + nameBytes.length + (hasMetadata ? UINT16_BYTES + metadataBytes.length : 0) + (hasCorrelation ? 1 + correlationBytes.length : 0) + (hasFlow ? FLOW_FIELDS_BYTES : 0) + (hasActorSlot ? UINT16_BYTES : 0);
|
|
190
346
|
const buffer = new Uint8Array(size);
|
|
191
347
|
let offset = 0;
|
|
192
348
|
buffer[offset++] = ZLINK_STREAM_FORMAT_MARKER;
|
|
@@ -198,14 +354,14 @@ function encodeStreamWireHeader(header, flagOverrides) {
|
|
|
198
354
|
throw new Error("Request sequence must not be zero.");
|
|
199
355
|
}
|
|
200
356
|
writeBigUInt64BE(buffer, offset, header.requestSeq);
|
|
201
|
-
offset +=
|
|
357
|
+
offset += UINT64_BYTES;
|
|
202
358
|
}
|
|
203
359
|
buffer[offset++] = nameBytes.length;
|
|
204
360
|
buffer.set(nameBytes, offset);
|
|
205
361
|
offset += nameBytes.length;
|
|
206
362
|
if (hasMetadata) {
|
|
207
363
|
writeUInt16BE(buffer, offset, metadataBytes.length);
|
|
208
|
-
offset +=
|
|
364
|
+
offset += UINT16_BYTES;
|
|
209
365
|
buffer.set(metadataBytes, offset);
|
|
210
366
|
offset += metadataBytes.length;
|
|
211
367
|
}
|
|
@@ -216,7 +372,7 @@ function encodeStreamWireHeader(header, flagOverrides) {
|
|
|
216
372
|
}
|
|
217
373
|
if (hasFlow) {
|
|
218
374
|
buffer.set(asciiEncode(header.flowId), offset);
|
|
219
|
-
offset +=
|
|
375
|
+
offset += FLOW_ID_BYTES;
|
|
220
376
|
buffer[offset++] = header.flowOrigin;
|
|
221
377
|
}
|
|
222
378
|
if (hasActorSlot) {
|
|
@@ -227,7 +383,7 @@ function encodeStreamWireHeader(header, flagOverrides) {
|
|
|
227
383
|
function decodeStreamWireHeader(header, flagOverrides, includeFlow = true) {
|
|
228
384
|
const flags = flagOverrides ?? defaultHeaderFlags;
|
|
229
385
|
let offset = 0;
|
|
230
|
-
if (header.length <
|
|
386
|
+
if (header.length < HEADER_MIN_BYTES) {
|
|
231
387
|
throw new Error("Stream header is incomplete.");
|
|
232
388
|
}
|
|
233
389
|
if (header[offset++] !== ZLINK_STREAM_FORMAT_MARKER) {
|
|
@@ -241,29 +397,28 @@ function decodeStreamWireHeader(header, flagOverrides, includeFlow = true) {
|
|
|
241
397
|
const hasCorrelation = (headerFlags & flags.hasCorrelationId) !== 0;
|
|
242
398
|
const hasFlow = (headerFlags & flags.hasFlowId) !== 0;
|
|
243
399
|
const hasActorSlot = (headerFlags & flags.hasActorSlot) !== 0;
|
|
244
|
-
if ((headerFlags & ~
|
|
400
|
+
if ((headerFlags & ~HEADER_FLAG_MASK) !== 0) {
|
|
245
401
|
throw new Error("Unknown mandatory stream header flag.");
|
|
246
402
|
}
|
|
247
403
|
let requestSeq;
|
|
248
404
|
if (hasRequestSeq) {
|
|
249
|
-
if (header.length - offset <
|
|
405
|
+
if (header.length - offset < UINT64_BYTES) {
|
|
250
406
|
throw new Error("Stream request sequence is incomplete.");
|
|
251
407
|
}
|
|
252
408
|
requestSeq = readBigUInt64BE(header, offset);
|
|
253
409
|
if (requestSeq === 0n) {
|
|
254
410
|
throw new Error("Request sequence must not be zero.");
|
|
255
411
|
}
|
|
256
|
-
offset +=
|
|
412
|
+
offset += UINT64_BYTES;
|
|
257
413
|
}
|
|
258
414
|
if (header.length - offset < 1) {
|
|
259
415
|
throw new Error("Stream packet name length is missing.");
|
|
260
416
|
}
|
|
261
417
|
const nameLength = header[offset++];
|
|
262
|
-
if (
|
|
418
|
+
if ((isReplyKind(kind) ? nameLength !== 0 : nameLength === 0) || header.length - offset < nameLength) {
|
|
263
419
|
throw new Error("Stream packet name is invalid.");
|
|
264
420
|
}
|
|
265
|
-
const
|
|
266
|
-
const name = isReplyKind(kind) ? "" : decodedName;
|
|
421
|
+
const name = utf8Decode(header.subarray(offset, offset + nameLength));
|
|
267
422
|
offset += nameLength;
|
|
268
423
|
const decodedMetadata = hasMetadata ? decodeStreamWireHeaderMetadata(header, offset) : { metadata: /* @__PURE__ */ new Map(), offset };
|
|
269
424
|
offset = decodedMetadata.offset;
|
|
@@ -282,14 +437,14 @@ function decodeStreamWireHeader(header, flagOverrides, includeFlow = true) {
|
|
|
282
437
|
let flowId;
|
|
283
438
|
let flowOrigin;
|
|
284
439
|
if (hasFlow) {
|
|
285
|
-
if (header.length - offset <
|
|
440
|
+
if (header.length - offset < FLOW_FIELDS_BYTES) {
|
|
286
441
|
throw new Error("Stream flow fields are incomplete.");
|
|
287
442
|
}
|
|
288
443
|
if (includeFlow) {
|
|
289
|
-
flowId = asciiDecode(header.subarray(offset, offset +
|
|
444
|
+
flowId = asciiDecode(header.subarray(offset, offset + FLOW_ID_BYTES));
|
|
290
445
|
validateFlowId(flowId);
|
|
291
446
|
}
|
|
292
|
-
offset +=
|
|
447
|
+
offset += FLOW_ID_BYTES;
|
|
293
448
|
const decodedFlowOrigin = header[offset++];
|
|
294
449
|
if (includeFlow && ![1, 2, 3, 4].includes(decodedFlowOrigin)) {
|
|
295
450
|
throw new Error("Stream flow origin is invalid.");
|
|
@@ -298,14 +453,14 @@ function decodeStreamWireHeader(header, flagOverrides, includeFlow = true) {
|
|
|
298
453
|
}
|
|
299
454
|
let actorSlot;
|
|
300
455
|
if (hasActorSlot) {
|
|
301
|
-
if (header.length - offset <
|
|
456
|
+
if (header.length - offset < UINT16_BYTES) {
|
|
302
457
|
throw new Error("Stream actor slot is incomplete.");
|
|
303
458
|
}
|
|
304
459
|
actorSlot = readUInt16BE(header, offset);
|
|
305
460
|
if (actorSlot === 0) {
|
|
306
461
|
throw new Error("Stream actor slot must not be zero.");
|
|
307
462
|
}
|
|
308
|
-
offset +=
|
|
463
|
+
offset += UINT16_BYTES;
|
|
309
464
|
}
|
|
310
465
|
if (offset !== header.length) {
|
|
311
466
|
throw new Error("Stream header has trailing bytes.");
|
|
@@ -335,47 +490,47 @@ function asciiDecode(value) {
|
|
|
335
490
|
if (value.some((byte) => byte > 127)) throw new Error("Stream flow id must be ASCII.");
|
|
336
491
|
return String.fromCharCode(...value);
|
|
337
492
|
}
|
|
338
|
-
function
|
|
339
|
-
if (metadata.size >
|
|
493
|
+
function streamWireMetadataSize(metadata) {
|
|
494
|
+
if (metadata.size > UINT8_MAX) {
|
|
340
495
|
throw new Error("Metadata entry count must not exceed 255.");
|
|
341
496
|
}
|
|
342
497
|
let size = 1;
|
|
343
|
-
const
|
|
344
|
-
const
|
|
345
|
-
const
|
|
346
|
-
if (
|
|
498
|
+
for (const [key, value] of metadata) {
|
|
499
|
+
const keySize = utf8Size(key);
|
|
500
|
+
const valueSize = utf8Size(value);
|
|
501
|
+
if (keySize === 0 || keySize > UINT8_MAX) {
|
|
347
502
|
throw new Error("Metadata key length is invalid.");
|
|
348
503
|
}
|
|
349
|
-
if (
|
|
504
|
+
if (valueSize > UINT16_MAX) {
|
|
350
505
|
throw new Error("Metadata value is too large.");
|
|
351
506
|
}
|
|
352
|
-
size += 1 +
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
507
|
+
size += 1 + keySize + UINT16_BYTES + valueSize;
|
|
508
|
+
}
|
|
509
|
+
return size;
|
|
510
|
+
}
|
|
511
|
+
function encodeStreamWireMetadata(metadata) {
|
|
512
|
+
const buffer = new Uint8Array(streamWireMetadataSize(metadata));
|
|
356
513
|
let offset = 0;
|
|
357
514
|
buffer[offset++] = metadata.size;
|
|
358
|
-
for (const
|
|
515
|
+
for (const [key, value] of metadata) {
|
|
516
|
+
const keyBytes = utf8Encode(key);
|
|
517
|
+
const valueBytes = utf8Encode(value);
|
|
359
518
|
buffer[offset++] = keyBytes.length;
|
|
360
519
|
buffer.set(keyBytes, offset);
|
|
361
520
|
offset += keyBytes.length;
|
|
362
521
|
writeUInt16BE(buffer, offset, valueBytes.length);
|
|
363
|
-
offset +=
|
|
522
|
+
offset += UINT16_BYTES;
|
|
364
523
|
buffer.set(valueBytes, offset);
|
|
365
524
|
offset += valueBytes.length;
|
|
366
525
|
}
|
|
367
526
|
return buffer;
|
|
368
527
|
}
|
|
369
|
-
function decodeStreamWireMetadata(metadata) {
|
|
370
|
-
const decoded = decodeStreamWireMetadataAt(metadata, 0, metadata.length);
|
|
371
|
-
if (decoded.offset !== metadata.length) {
|
|
372
|
-
throw new Error("Stream metadata payload has trailing bytes.");
|
|
373
|
-
}
|
|
374
|
-
return decoded.metadata;
|
|
375
|
-
}
|
|
376
528
|
function lz4PickleUncompressed2(payload) {
|
|
377
529
|
return lz4PickleUncompressed(payload);
|
|
378
530
|
}
|
|
531
|
+
function lz4PickledLength2(payload) {
|
|
532
|
+
return lz4PickledLength(payload);
|
|
533
|
+
}
|
|
379
534
|
function lz4UnpicklePayload2(payload, maxDecompressedSize) {
|
|
380
535
|
return lz4UnpicklePayload(payload, maxDecompressedSize ?? defaultMaxDecompressedPayloadSize);
|
|
381
536
|
}
|
|
@@ -386,11 +541,11 @@ function utf8Decode(value) {
|
|
|
386
541
|
return new TextDecoder().decode(value);
|
|
387
542
|
}
|
|
388
543
|
function decodeStreamWireHeaderMetadata(header, offset) {
|
|
389
|
-
if (header.length - offset <
|
|
544
|
+
if (header.length - offset < UINT16_BYTES) {
|
|
390
545
|
throw new Error("Stream metadata section is incomplete.");
|
|
391
546
|
}
|
|
392
547
|
const metadataLength = readUInt16BE(header, offset);
|
|
393
|
-
offset +=
|
|
548
|
+
offset += UINT16_BYTES;
|
|
394
549
|
if (header.length - offset < metadataLength) {
|
|
395
550
|
throw new Error("Stream metadata payload is incomplete.");
|
|
396
551
|
}
|
|
@@ -416,7 +571,7 @@ function decodeStreamWireMetadataAt(source, offset, end) {
|
|
|
416
571
|
throw new Error("Stream metadata value length is missing.");
|
|
417
572
|
}
|
|
418
573
|
const valueLength = readUInt16BE(source, offset);
|
|
419
|
-
offset +=
|
|
574
|
+
offset += UINT16_BYTES;
|
|
420
575
|
if (end - offset < valueLength) {
|
|
421
576
|
throw new Error("Stream metadata value is incomplete.");
|
|
422
577
|
}
|
|
@@ -433,16 +588,17 @@ function decodeStreamWireMetadataAt(source, offset, end) {
|
|
|
433
588
|
}
|
|
434
589
|
function validateStreamWirePacketName(name) {
|
|
435
590
|
const nameBytes = utf8Encode(name);
|
|
436
|
-
if (
|
|
591
|
+
if (unicodeWhiteSpaceOnly.test(name) || nameBytes.length > ZLINK_STREAM_MAX_PACKET_NAME_BYTES) {
|
|
437
592
|
throw new Error("Stream packet name is invalid.");
|
|
438
593
|
}
|
|
594
|
+
return nameBytes;
|
|
439
595
|
}
|
|
440
596
|
function isReplyKind(kind) {
|
|
441
|
-
return kind ===
|
|
597
|
+
return kind === 3 /* Response */ || kind === 4 /* Error */;
|
|
442
598
|
}
|
|
443
599
|
function writeUInt16BE(buffer, offset, value) {
|
|
444
|
-
buffer[offset] = value >>> 8 &
|
|
445
|
-
buffer[offset + 1] = value &
|
|
600
|
+
buffer[offset] = value >>> 8 & UINT8_MAX;
|
|
601
|
+
buffer[offset + 1] = value & UINT8_MAX;
|
|
446
602
|
}
|
|
447
603
|
function readUInt16BE(buffer, offset) {
|
|
448
604
|
return buffer[offset] << 8 | buffer[offset + 1];
|
|
@@ -451,10 +607,10 @@ function readUInt32BE(buffer, offset) {
|
|
|
451
607
|
return buffer[offset] * 16777216 + (buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3]);
|
|
452
608
|
}
|
|
453
609
|
function writeUInt32BE(buffer, offset, value) {
|
|
454
|
-
buffer[offset] = value >>> 24 &
|
|
455
|
-
buffer[offset + 1] = value >>> 16 &
|
|
456
|
-
buffer[offset + 2] = value >>> 8 &
|
|
457
|
-
buffer[offset + 3] = value &
|
|
610
|
+
buffer[offset] = value >>> 24 & UINT8_MAX;
|
|
611
|
+
buffer[offset + 1] = value >>> 16 & UINT8_MAX;
|
|
612
|
+
buffer[offset + 2] = value >>> 8 & UINT8_MAX;
|
|
613
|
+
buffer[offset + 3] = value & UINT8_MAX;
|
|
458
614
|
}
|
|
459
615
|
function writeBigUInt64BE(buffer, offset, value) {
|
|
460
616
|
for (let index = 7; index >= 0; index -= 1) {
|
|
@@ -469,6 +625,19 @@ function readBigUInt64BE(buffer, offset) {
|
|
|
469
625
|
}
|
|
470
626
|
return value;
|
|
471
627
|
}
|
|
628
|
+
function utf8Size(value) {
|
|
629
|
+
let size = 0;
|
|
630
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
631
|
+
const code = value.charCodeAt(index);
|
|
632
|
+
if (code < UTF8_TWO_BYTE_MIN) size += 1;
|
|
633
|
+
else if (code < UTF8_THREE_BYTE_MIN) size += 2;
|
|
634
|
+
else if (code >= UTF16_HIGH_SURROGATE_MIN && code <= UTF16_HIGH_SURROGATE_MAX && index + 1 < value.length && value.charCodeAt(index + 1) >= UTF16_LOW_SURROGATE_MIN && value.charCodeAt(index + 1) <= UTF16_LOW_SURROGATE_MAX) {
|
|
635
|
+
size += 4;
|
|
636
|
+
index += 1;
|
|
637
|
+
} else size += 3;
|
|
638
|
+
}
|
|
639
|
+
return size;
|
|
640
|
+
}
|
|
472
641
|
|
|
473
642
|
// packages/stream-connector/src/Contracts/ZlinkStreamEnums.ts
|
|
474
643
|
var ZlinkStreamTransport = /* @__PURE__ */ ((ZlinkStreamTransport2) => {
|
|
@@ -486,24 +655,6 @@ var ZlinkStreamDispatchMode = /* @__PURE__ */ ((ZlinkStreamDispatchMode2) => {
|
|
|
486
655
|
ZlinkStreamDispatchMode2["Immediate"] = "immediate";
|
|
487
656
|
return ZlinkStreamDispatchMode2;
|
|
488
657
|
})(ZlinkStreamDispatchMode || {});
|
|
489
|
-
var ZlinkStreamMessageKind = /* @__PURE__ */ ((ZlinkStreamMessageKind2) => {
|
|
490
|
-
ZlinkStreamMessageKind2[ZlinkStreamMessageKind2["Send"] = 1] = "Send";
|
|
491
|
-
ZlinkStreamMessageKind2[ZlinkStreamMessageKind2["Request"] = 2] = "Request";
|
|
492
|
-
ZlinkStreamMessageKind2[ZlinkStreamMessageKind2["Response"] = 3] = "Response";
|
|
493
|
-
ZlinkStreamMessageKind2[ZlinkStreamMessageKind2["Error"] = 4] = "Error";
|
|
494
|
-
ZlinkStreamMessageKind2[ZlinkStreamMessageKind2["Control"] = 5] = "Control";
|
|
495
|
-
return ZlinkStreamMessageKind2;
|
|
496
|
-
})(ZlinkStreamMessageKind || {});
|
|
497
|
-
var ZlinkStreamHeaderFlags = /* @__PURE__ */ ((ZlinkStreamHeaderFlags2) => {
|
|
498
|
-
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["None"] = 0] = "None";
|
|
499
|
-
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["HasRequestSeq"] = 1] = "HasRequestSeq";
|
|
500
|
-
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["HasMetadata"] = 2] = "HasMetadata";
|
|
501
|
-
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["PayloadCompressed"] = 4] = "PayloadCompressed";
|
|
502
|
-
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["HasCorrelationId"] = 8] = "HasCorrelationId";
|
|
503
|
-
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["HasFlowId"] = 16] = "HasFlowId";
|
|
504
|
-
ZlinkStreamHeaderFlags2[ZlinkStreamHeaderFlags2["HasActorSlot"] = 32] = "HasActorSlot";
|
|
505
|
-
return ZlinkStreamHeaderFlags2;
|
|
506
|
-
})(ZlinkStreamHeaderFlags || {});
|
|
507
658
|
var ZlinkStreamErrorCode = /* @__PURE__ */ ((ZlinkStreamErrorCode2) => {
|
|
508
659
|
ZlinkStreamErrorCode2["Disconnected"] = "disconnected";
|
|
509
660
|
ZlinkStreamErrorCode2["ConfigurationError"] = "configurationError";
|
|
@@ -515,6 +666,7 @@ var ZlinkStreamErrorCode = /* @__PURE__ */ ((ZlinkStreamErrorCode2) => {
|
|
|
515
666
|
ZlinkStreamErrorCode2["SendFailed"] = "sendFailed";
|
|
516
667
|
ZlinkStreamErrorCode2["CompressionFailed"] = "compressionFailed";
|
|
517
668
|
ZlinkStreamErrorCode2["DecompressionFailed"] = "decompressionFailed";
|
|
669
|
+
ZlinkStreamErrorCode2["TlsValidationFailed"] = "tlsValidationFailed";
|
|
518
670
|
ZlinkStreamErrorCode2["UserCallbackFailed"] = "userCallbackFailed";
|
|
519
671
|
ZlinkStreamErrorCode2["RemoteError"] = "remoteError";
|
|
520
672
|
return ZlinkStreamErrorCode2;
|
|
@@ -529,6 +681,15 @@ var ZlinkStreamConnectionState = /* @__PURE__ */ ((ZlinkStreamConnectionState3)
|
|
|
529
681
|
return ZlinkStreamConnectionState3;
|
|
530
682
|
})(ZlinkStreamConnectionState || {});
|
|
531
683
|
|
|
684
|
+
// packages/stream-connector/src/Contracts/ZlinkStreamModels.ts
|
|
685
|
+
var ZlinkStreamException = class extends Error {
|
|
686
|
+
constructor(error) {
|
|
687
|
+
super(error.message);
|
|
688
|
+
this.error = error;
|
|
689
|
+
this.name = "ZlinkStreamException";
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
|
|
532
693
|
// packages/stream-connector/src/Contracts/ZlinkStreamMetadata.ts
|
|
533
694
|
var ZlinkStreamMetadataMap = class _ZlinkStreamMetadataMap {
|
|
534
695
|
constructor(values) {
|
|
@@ -561,25 +722,13 @@ var ZlinkStreamMetadataMap = class _ZlinkStreamMetadataMap {
|
|
|
561
722
|
};
|
|
562
723
|
function validateMetadataKey(key) {
|
|
563
724
|
if (key.length === 0) {
|
|
564
|
-
throw new
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
if (code < 32 || code > 126 || key[index] === "=") {
|
|
569
|
-
throw new Error('Metadata key must contain printable ASCII characters except "=".');
|
|
570
|
-
}
|
|
725
|
+
throw new ZlinkStreamException({
|
|
726
|
+
code: "validationFailed" /* ValidationFailed */,
|
|
727
|
+
message: "Metadata key must not be empty."
|
|
728
|
+
});
|
|
571
729
|
}
|
|
572
730
|
}
|
|
573
731
|
|
|
574
|
-
// packages/stream-connector/src/Contracts/ZlinkStreamModels.ts
|
|
575
|
-
var ZlinkStreamException = class extends Error {
|
|
576
|
-
constructor(error) {
|
|
577
|
-
super(error.message);
|
|
578
|
-
this.error = error;
|
|
579
|
-
this.name = "ZlinkStreamException";
|
|
580
|
-
}
|
|
581
|
-
};
|
|
582
|
-
|
|
583
732
|
// packages/stream-connector/src/Contracts/ZlinkStreamJsonCodec.ts
|
|
584
733
|
var zlinkStreamJsonCodecName = "json";
|
|
585
734
|
var codecOptions = {};
|
|
@@ -631,6 +780,10 @@ function isPrototypeKey(key) {
|
|
|
631
780
|
}
|
|
632
781
|
|
|
633
782
|
// packages/stream-connector/src/Runtime/ZlinkStreamSupport.ts
|
|
783
|
+
var BACKING_ARRAY_COMPACTION_MIN_HEAD = 1024;
|
|
784
|
+
function shouldCompactBackingArray(head, length) {
|
|
785
|
+
return head >= BACKING_ARRAY_COMPACTION_MIN_HEAD && head * 2 >= length;
|
|
786
|
+
}
|
|
634
787
|
function connectorError(code, message, cause) {
|
|
635
788
|
return new ZlinkStreamException({ code, message, cause });
|
|
636
789
|
}
|
|
@@ -644,15 +797,21 @@ function unwrapStreamError(error) {
|
|
|
644
797
|
if (error instanceof ZlinkStreamException) {
|
|
645
798
|
return error.error;
|
|
646
799
|
}
|
|
647
|
-
|
|
648
|
-
code: "remoteError" /* RemoteError */,
|
|
649
|
-
message: error instanceof Error ? error.message : String(error),
|
|
650
|
-
cause: error
|
|
651
|
-
};
|
|
800
|
+
throw error;
|
|
652
801
|
}
|
|
653
802
|
function subscription(dispose) {
|
|
654
803
|
return { dispose };
|
|
655
804
|
}
|
|
805
|
+
function* currentRegistrations(registrations, snapshot = Array.from(registrations)) {
|
|
806
|
+
for (const registration of snapshot) {
|
|
807
|
+
if (registrations.has(registration)) yield registration;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function registerHandler(handlers, handler) {
|
|
811
|
+
const registration = { handler };
|
|
812
|
+
handlers.add(registration);
|
|
813
|
+
return subscription(() => handlers.delete(registration));
|
|
814
|
+
}
|
|
656
815
|
function throwIfAborted(signal) {
|
|
657
816
|
if (signal?.aborted === true) {
|
|
658
817
|
throw connectorError("disconnected" /* Disconnected */, "Operation canceled.");
|
|
@@ -672,9 +831,6 @@ function delay(delayMs, signal) {
|
|
|
672
831
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
673
832
|
});
|
|
674
833
|
}
|
|
675
|
-
function utf8Encode2(value) {
|
|
676
|
-
return new TextEncoder().encode(value);
|
|
677
|
-
}
|
|
678
834
|
function utf8Decode2(value) {
|
|
679
835
|
return new TextDecoder().decode(value);
|
|
680
836
|
}
|
|
@@ -693,59 +849,48 @@ var zlinkStreamAssert = {
|
|
|
693
849
|
}
|
|
694
850
|
},
|
|
695
851
|
async expectFailure(action, errorKind) {
|
|
696
|
-
let failure;
|
|
697
852
|
try {
|
|
698
853
|
await action();
|
|
699
|
-
} catch (
|
|
700
|
-
failure
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
failure
|
|
711
|
-
);
|
|
854
|
+
} catch (failure) {
|
|
855
|
+
if (!(failure instanceof ZlinkStreamException)) throw failure;
|
|
856
|
+
const streamError = failure.error;
|
|
857
|
+
if (errorKind !== void 0 && streamError.code !== errorKind) {
|
|
858
|
+
throw connectorError(
|
|
859
|
+
"validationFailed" /* ValidationFailed */,
|
|
860
|
+
`Expected failure kind '${errorKind}', got '${streamError.code}'.`,
|
|
861
|
+
failure
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
return streamError;
|
|
712
865
|
}
|
|
713
|
-
|
|
866
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "Expected action to fail.");
|
|
714
867
|
},
|
|
715
868
|
async expectTimeout(action) {
|
|
716
|
-
let failure;
|
|
717
869
|
try {
|
|
718
870
|
await action();
|
|
719
|
-
} catch (
|
|
720
|
-
failure
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
}
|
|
725
|
-
const code = unwrapStreamError(failure).code;
|
|
726
|
-
if (code !== "requestTimeout" /* RequestTimeout */ && code !== "connectTimeout" /* ConnectTimeout */) {
|
|
727
|
-
throw failure;
|
|
871
|
+
} catch (failure) {
|
|
872
|
+
if (!(failure instanceof ZlinkStreamException) || failure.error.code !== "requestTimeout" /* RequestTimeout */ && failure.error.code !== "connectTimeout" /* ConnectTimeout */) {
|
|
873
|
+
throw failure;
|
|
874
|
+
}
|
|
875
|
+
return;
|
|
728
876
|
}
|
|
877
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "Expected action to time out.");
|
|
729
878
|
}
|
|
730
879
|
};
|
|
731
880
|
|
|
732
881
|
// packages/stream-connector/src/Runtime/Protocol/ZlinkStreamPacketNameValidator.ts
|
|
733
882
|
function validateName(name, allowReserved = false) {
|
|
734
|
-
|
|
735
|
-
|
|
883
|
+
try {
|
|
884
|
+
validateStreamWirePacketName(name);
|
|
885
|
+
} catch (cause) {
|
|
886
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "Message name is invalid.", cause);
|
|
736
887
|
}
|
|
737
|
-
if (!allowReserved && name.startsWith(
|
|
888
|
+
if (!allowReserved && name.startsWith(ZLINK_STREAM_RESERVED_PACKET_NAME_PREFIX)) {
|
|
738
889
|
throw connectorError(
|
|
739
890
|
"validationFailed" /* ValidationFailed */,
|
|
740
891
|
"Message name uses a reserved zlink prefix."
|
|
741
892
|
);
|
|
742
893
|
}
|
|
743
|
-
if (utf8Encode2(name).length > 255) {
|
|
744
|
-
throw connectorError(
|
|
745
|
-
"validationFailed" /* ValidationFailed */,
|
|
746
|
-
"Message name must not exceed 255 UTF-8 bytes."
|
|
747
|
-
);
|
|
748
|
-
}
|
|
749
894
|
}
|
|
750
895
|
|
|
751
896
|
// packages/stream-connector/src/Runtime/Calls/ZlinkStreamCallBuilders.ts
|
|
@@ -817,9 +962,10 @@ var ZlinkStreamSendBuilder = class {
|
|
|
817
962
|
}
|
|
818
963
|
};
|
|
819
964
|
var ZlinkStreamRequestBuilder = class {
|
|
820
|
-
constructor(connector, name, payload, actorSlot, validateActor) {
|
|
965
|
+
constructor(connector, name, payload, enqueueCallback, actorSlot, validateActor) {
|
|
821
966
|
this.connector = connector;
|
|
822
967
|
this.payload = payload;
|
|
968
|
+
this.enqueueCallback = enqueueCallback;
|
|
823
969
|
this.state = new ZlinkStreamCallBuilderState(name, actorSlot, validateActor);
|
|
824
970
|
}
|
|
825
971
|
state;
|
|
@@ -853,8 +999,10 @@ var ZlinkStreamRequestBuilder = class {
|
|
|
853
999
|
);
|
|
854
1000
|
if (typeof signalOrCallback === "function") {
|
|
855
1001
|
operation.then(
|
|
856
|
-
(value) => signalOrCallback({ isSuccess: true, value }),
|
|
857
|
-
(error) =>
|
|
1002
|
+
(value) => this.enqueueCallback(() => signalOrCallback({ isSuccess: true, value })),
|
|
1003
|
+
(error) => this.enqueueCallback(
|
|
1004
|
+
() => signalOrCallback({ isSuccess: false, error: unwrapStreamError(error) })
|
|
1005
|
+
)
|
|
858
1006
|
);
|
|
859
1007
|
return;
|
|
860
1008
|
}
|
|
@@ -1059,6 +1207,12 @@ var zlinkStreamLz4CompressionCodec = {
|
|
|
1059
1207
|
return lz4PickleUncompressed2(payload);
|
|
1060
1208
|
},
|
|
1061
1209
|
decompress(payload, maxDecompressedSize) {
|
|
1210
|
+
if (lz4PickledLength2(payload) > maxDecompressedSize) {
|
|
1211
|
+
throw connectorError(
|
|
1212
|
+
"frameTooLarge" /* FrameTooLarge */,
|
|
1213
|
+
"LZ4 decoded payload exceeds MaxReceivePayloadSize."
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1062
1216
|
return lz4UnpicklePayload2(payload, maxDecompressedSize);
|
|
1063
1217
|
}
|
|
1064
1218
|
};
|
|
@@ -1070,7 +1224,11 @@ function compressPayload(payload, compression, compressionCodec) {
|
|
|
1070
1224
|
"Compression codec is not configured."
|
|
1071
1225
|
);
|
|
1072
1226
|
}
|
|
1073
|
-
|
|
1227
|
+
try {
|
|
1228
|
+
return codec.compress(payload);
|
|
1229
|
+
} catch (cause) {
|
|
1230
|
+
throw connectorError("compressionFailed" /* CompressionFailed */, "Compression failed.", cause);
|
|
1231
|
+
}
|
|
1074
1232
|
}
|
|
1075
1233
|
function decompressIfNeeded(header, payload, compression, compressionCodec, maxDecompressedSize) {
|
|
1076
1234
|
if ((header.flags & 4 /* PayloadCompressed */) === 0) {
|
|
@@ -1083,15 +1241,20 @@ function decompressIfNeeded(header, payload, compression, compressionCodec, maxD
|
|
|
1083
1241
|
"Compression codec is not configured."
|
|
1084
1242
|
);
|
|
1085
1243
|
}
|
|
1244
|
+
let decompressed;
|
|
1086
1245
|
try {
|
|
1087
|
-
|
|
1088
|
-
if (decompressed.length > maxDecompressedSize) {
|
|
1089
|
-
throw new Error("Decoded payload exceeds MaxReceivePayloadSize.");
|
|
1090
|
-
}
|
|
1091
|
-
return decompressed;
|
|
1246
|
+
decompressed = codec.decompress(payload, maxDecompressedSize);
|
|
1092
1247
|
} catch (cause) {
|
|
1248
|
+
if (cause instanceof ZlinkStreamException) throw cause;
|
|
1093
1249
|
throw connectorError("decompressionFailed" /* DecompressionFailed */, "Decompression failed.", cause);
|
|
1094
1250
|
}
|
|
1251
|
+
if (decompressed.length > maxDecompressedSize) {
|
|
1252
|
+
throw connectorError(
|
|
1253
|
+
"frameTooLarge" /* FrameTooLarge */,
|
|
1254
|
+
"Decompressed payload exceeds MaxReceivePayloadSize."
|
|
1255
|
+
);
|
|
1256
|
+
}
|
|
1257
|
+
return decompressed;
|
|
1095
1258
|
}
|
|
1096
1259
|
function resolveCompressionCodec(compression, compressionCodec) {
|
|
1097
1260
|
if (compression === "none" /* None */) {
|
|
@@ -1100,122 +1263,231 @@ function resolveCompressionCodec(compression, compressionCodec) {
|
|
|
1100
1263
|
return compressionCodec ?? zlinkStreamLz4CompressionCodec;
|
|
1101
1264
|
}
|
|
1102
1265
|
|
|
1103
|
-
// packages/stream-connector/src/
|
|
1104
|
-
var
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
return decodeStreamWireFrame(frame);
|
|
1116
|
-
} catch (cause) {
|
|
1266
|
+
// packages/stream-connector/src/Contracts/ZlinkStreamConnectorOptions.ts
|
|
1267
|
+
var ZLINK_STREAM_DEFAULT_PAYLOAD_BYTES = 64 * 1024;
|
|
1268
|
+
|
|
1269
|
+
// packages/stream-connector/src/Runtime/Transport/ZlinkStreamEndpoint.ts
|
|
1270
|
+
function inferTransport(endpoint) {
|
|
1271
|
+
const url = parseEndpoint(endpoint);
|
|
1272
|
+
switch (url.protocol) {
|
|
1273
|
+
case "ws:":
|
|
1274
|
+
return "webSocket" /* WebSocket */;
|
|
1275
|
+
case "wss:":
|
|
1276
|
+
return "webSocketSecure" /* WebSocketSecure */;
|
|
1277
|
+
default:
|
|
1117
1278
|
throw connectorError(
|
|
1118
|
-
"
|
|
1119
|
-
"
|
|
1120
|
-
cause
|
|
1279
|
+
"configurationError" /* ConfigurationError */,
|
|
1280
|
+
"The TypeScript Stream Connector supports only ws:// and wss:// endpoints."
|
|
1121
1281
|
);
|
|
1122
|
-
}
|
|
1123
1282
|
}
|
|
1124
|
-
}
|
|
1125
|
-
function
|
|
1126
|
-
|
|
1283
|
+
}
|
|
1284
|
+
function parseEndpoint(endpoint) {
|
|
1285
|
+
try {
|
|
1286
|
+
return new URL(endpoint);
|
|
1287
|
+
} catch (cause) {
|
|
1288
|
+
throw connectorError("configurationError" /* ConfigurationError */, "Endpoint is invalid.", cause);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
// packages/stream-connector/src/Runtime/ZlinkStreamConnectorOptions.ts
|
|
1293
|
+
function normalizeOptions(options, defaultTransportFactory) {
|
|
1294
|
+
const endpoint = options.endpoint;
|
|
1295
|
+
if (endpoint.trim().length === 0) {
|
|
1296
|
+
throw connectorError("configurationError" /* ConfigurationError */, "Endpoint must not be empty.");
|
|
1297
|
+
}
|
|
1298
|
+
const inferredTransport = inferTransport(endpoint);
|
|
1299
|
+
if (options.transport !== void 0 && options.transport !== inferredTransport) {
|
|
1127
1300
|
throw connectorError(
|
|
1128
|
-
"
|
|
1129
|
-
"
|
|
1301
|
+
"configurationError" /* ConfigurationError */,
|
|
1302
|
+
"Configured transport conflicts with endpoint scheme."
|
|
1130
1303
|
);
|
|
1131
1304
|
}
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1305
|
+
const normalized = {
|
|
1306
|
+
endpoint,
|
|
1307
|
+
transport: inferredTransport,
|
|
1308
|
+
connectTimeoutMs: options.connectTimeoutMs ?? 5e3,
|
|
1309
|
+
requestTimeoutMs: options.requestTimeoutMs ?? 3e4,
|
|
1310
|
+
waitTimeoutMs: options.waitTimeoutMs ?? 5e3,
|
|
1311
|
+
heartbeat: {
|
|
1312
|
+
enabled: options.heartbeat?.enabled ?? true,
|
|
1313
|
+
intervalMs: options.heartbeat?.intervalMs ?? 1e3,
|
|
1314
|
+
timeoutMs: options.heartbeat?.timeoutMs ?? 5e3
|
|
1315
|
+
},
|
|
1316
|
+
reconnect: {
|
|
1317
|
+
enabled: options.reconnect?.enabled ?? true,
|
|
1318
|
+
initialDelayMs: options.reconnect?.initialDelayMs ?? 250,
|
|
1319
|
+
maxDelayMs: options.reconnect?.maxDelayMs ?? 5e3,
|
|
1320
|
+
backoffFactor: options.reconnect?.backoffFactor ?? 2,
|
|
1321
|
+
maxAttempts: options.reconnect?.maxAttempts === void 0 ? 3 : options.reconnect.maxAttempts
|
|
1322
|
+
},
|
|
1323
|
+
maxSendPayloadSize: options.maxSendPayloadSize ?? ZLINK_STREAM_DEFAULT_PAYLOAD_BYTES,
|
|
1324
|
+
maxReceivePayloadSize: options.maxReceivePayloadSize ?? ZLINK_STREAM_DEFAULT_PAYLOAD_BYTES,
|
|
1325
|
+
dispatchMode: options.dispatchMode ?? "manual" /* Manual */,
|
|
1326
|
+
compression: options.compression ?? "lz4" /* Lz4 */,
|
|
1327
|
+
compressionCodec: options.compressionCodec,
|
|
1328
|
+
nameResolver: options.nameResolver ?? defaultPacketNameResolver,
|
|
1329
|
+
transportFactory: options.transportFactory ?? defaultTransportFactory,
|
|
1330
|
+
codec: options.codec
|
|
1331
|
+
};
|
|
1332
|
+
validatePositive(normalized.connectTimeoutMs, "ConnectTimeout");
|
|
1333
|
+
validatePositive(normalized.requestTimeoutMs, "RequestTimeout");
|
|
1334
|
+
validatePositive(normalized.waitTimeoutMs, "WaitTimeout");
|
|
1335
|
+
validatePositive(normalized.maxSendPayloadSize, "MaxSendPayloadSize");
|
|
1336
|
+
validatePositive(normalized.maxReceivePayloadSize, "MaxReceivePayloadSize");
|
|
1337
|
+
validateHeartbeat(normalized.heartbeat);
|
|
1338
|
+
validateReconnect(normalized.reconnect);
|
|
1339
|
+
if (options.heartbeat?.enabled !== void 0 && typeof options.heartbeat.enabled !== "boolean") {
|
|
1340
|
+
throw connectorError(
|
|
1341
|
+
"validationFailed" /* ValidationFailed */,
|
|
1342
|
+
"Heartbeat enabled must be boolean."
|
|
1343
|
+
);
|
|
1153
1344
|
}
|
|
1154
|
-
|
|
1155
|
-
}
|
|
1156
|
-
function validatePayload(payloadLength, maxPayloadSize) {
|
|
1157
|
-
if (payloadLength > maxPayloadSize) {
|
|
1345
|
+
if (options.reconnect?.enabled !== void 0 && typeof options.reconnect.enabled !== "boolean") {
|
|
1158
1346
|
throw connectorError(
|
|
1159
1347
|
"validationFailed" /* ValidationFailed */,
|
|
1160
|
-
"
|
|
1348
|
+
"Reconnect enabled must be boolean."
|
|
1161
1349
|
);
|
|
1162
1350
|
}
|
|
1351
|
+
if (options.compressionCodec !== void 0 && !hasMethods(options.compressionCodec, "compress", "decompress")) {
|
|
1352
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "Compression codec is invalid.");
|
|
1353
|
+
}
|
|
1354
|
+
if (options.codec !== void 0 && !hasMethods(options.codec, "encode", "decode")) {
|
|
1355
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "Payload codec is invalid.");
|
|
1356
|
+
}
|
|
1357
|
+
if (options.nameResolver !== void 0 && !hasMethods(options.nameResolver, "resolve")) {
|
|
1358
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "Packet name resolver is invalid.");
|
|
1359
|
+
}
|
|
1360
|
+
if (options.transportFactory !== void 0 && !hasMethods(options.transportFactory, "connect")) {
|
|
1361
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "Transport factory is invalid.");
|
|
1362
|
+
}
|
|
1363
|
+
if (!Object.values(ZlinkStreamDispatchMode).includes(normalized.dispatchMode)) {
|
|
1364
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "DispatchMode is invalid.");
|
|
1365
|
+
}
|
|
1366
|
+
if (!Object.values(ZlinkStreamCompression).includes(normalized.compression)) {
|
|
1367
|
+
throw connectorError("validationFailed" /* ValidationFailed */, "Compression is invalid.");
|
|
1368
|
+
}
|
|
1369
|
+
validateCompressionCodec(normalized);
|
|
1370
|
+
return normalized;
|
|
1163
1371
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
return size;
|
|
1178
|
-
} catch (cause) {
|
|
1372
|
+
var defaultPacketNameResolver = {
|
|
1373
|
+
resolve(payloadType) {
|
|
1374
|
+
const declared = payloadType.packetName;
|
|
1375
|
+
if (typeof declared === "string" && declared.length > 0) {
|
|
1376
|
+
return declared;
|
|
1377
|
+
}
|
|
1378
|
+
return payloadType.name;
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
function validateCompressionCodec(options) {
|
|
1382
|
+
const compression = options.compression;
|
|
1383
|
+
if (compression === "none" /* None */) {
|
|
1384
|
+
if (options.compressionCodec !== void 0) {
|
|
1179
1385
|
throw connectorError(
|
|
1180
|
-
"
|
|
1181
|
-
|
|
1182
|
-
cause
|
|
1386
|
+
"configurationError" /* ConfigurationError */,
|
|
1387
|
+
"compressionCodec cannot be set when compression is none."
|
|
1183
1388
|
);
|
|
1184
1389
|
}
|
|
1185
1390
|
}
|
|
1186
|
-
|
|
1391
|
+
}
|
|
1392
|
+
function validatePositive(value, name) {
|
|
1393
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
1394
|
+
throw connectorError("validationFailed" /* ValidationFailed */, `${name} must be positive.`);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
function hasMethods(value, ...names) {
|
|
1398
|
+
if (value === null || typeof value !== "object") return false;
|
|
1399
|
+
const candidate = value;
|
|
1400
|
+
return names.every((name) => typeof candidate[name] === "function");
|
|
1401
|
+
}
|
|
1402
|
+
function validateHeartbeat(options) {
|
|
1403
|
+
const intervalMs = options.intervalMs;
|
|
1404
|
+
const timeoutMs = options.timeoutMs;
|
|
1405
|
+
validatePositive(intervalMs, "Heartbeat interval");
|
|
1406
|
+
validatePositive(timeoutMs, "Heartbeat timeout");
|
|
1407
|
+
}
|
|
1408
|
+
function validateReconnect(options) {
|
|
1409
|
+
validatePositive(options.initialDelayMs, "Reconnect InitialDelay");
|
|
1410
|
+
validatePositive(options.maxDelayMs, "Reconnect MaxDelay");
|
|
1411
|
+
validatePositive(options.backoffFactor, "Reconnect BackoffFactor");
|
|
1412
|
+
const maxAttempts = options.maxAttempts;
|
|
1413
|
+
if (maxAttempts !== null && !(Number.isFinite(maxAttempts) && maxAttempts > 0)) {
|
|
1414
|
+
throw connectorError(
|
|
1415
|
+
"validationFailed" /* ValidationFailed */,
|
|
1416
|
+
"Reconnect MaxAttempts must be null or positive."
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
// packages/stream-connector/src/Runtime/Protocol/ZlinkStreamFrameCodec.ts
|
|
1422
|
+
var ZlinkStreamFrameCodec = class {
|
|
1423
|
+
static encode(header, payload, maxPayloadSize = ZLINK_STREAM_DEFAULT_PAYLOAD_BYTES) {
|
|
1424
|
+
validatePayload(payload.length, maxPayloadSize);
|
|
1425
|
+
try {
|
|
1426
|
+
return encodeStreamWireFrame(header, payload);
|
|
1427
|
+
} catch (cause) {
|
|
1428
|
+
throw connectorError("frameTooLarge" /* FrameTooLarge */, "Frame is too large.", cause);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
static decode(frame) {
|
|
1187
1432
|
try {
|
|
1188
|
-
|
|
1433
|
+
return decodeStreamWireFrame(frame);
|
|
1189
1434
|
} catch (cause) {
|
|
1190
1435
|
throw connectorError(
|
|
1191
|
-
"
|
|
1192
|
-
|
|
1436
|
+
"frameDecodeFailed" /* FrameDecodeFailed */,
|
|
1437
|
+
"Frame length does not match prefix.",
|
|
1193
1438
|
cause
|
|
1194
1439
|
);
|
|
1195
1440
|
}
|
|
1196
1441
|
}
|
|
1197
|
-
|
|
1442
|
+
};
|
|
1443
|
+
function splitZlinkStreamFrames(chunk) {
|
|
1444
|
+
try {
|
|
1445
|
+
return splitStreamWireFrames(chunk);
|
|
1446
|
+
} catch (cause) {
|
|
1447
|
+
throw connectorError(
|
|
1448
|
+
"frameDecodeFailed" /* FrameDecodeFailed */,
|
|
1449
|
+
cause instanceof Error ? cause.message : "Frame length does not match prefix."
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
function validatePayload(payloadLength, maxPayloadSize) {
|
|
1454
|
+
if (payloadLength > maxPayloadSize) {
|
|
1455
|
+
throw connectorError(
|
|
1456
|
+
"validationFailed" /* ValidationFailed */,
|
|
1457
|
+
"Payload exceeds MaxSendPayloadSize."
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
// packages/stream-connector/src/Runtime/Protocol/ZlinkStreamMetadataCodec.ts
|
|
1463
|
+
var ZlinkStreamMetadataCodec = class {
|
|
1464
|
+
static size(metadata) {
|
|
1465
|
+
let size;
|
|
1198
1466
|
try {
|
|
1199
|
-
|
|
1200
|
-
return values.size === 0 ? ZlinkStreamMetadataMap.empty : ZlinkStreamMetadataMap.from(values);
|
|
1467
|
+
size = metadata.count === 0 ? 0 : streamWireMetadataSize(metadata.values);
|
|
1201
1468
|
} catch (cause) {
|
|
1202
1469
|
throw connectorError(
|
|
1203
|
-
"
|
|
1204
|
-
|
|
1470
|
+
"validationFailed" /* ValidationFailed */,
|
|
1471
|
+
cause instanceof Error ? cause.message : "Metadata is invalid.",
|
|
1205
1472
|
cause
|
|
1206
1473
|
);
|
|
1207
1474
|
}
|
|
1475
|
+
if (size > ZLINK_STREAM_MAX_METADATA_BYTES) {
|
|
1476
|
+
throw connectorError(
|
|
1477
|
+
"validationFailed" /* ValidationFailed */,
|
|
1478
|
+
`Metadata must not exceed ${ZLINK_STREAM_MAX_METADATA_BYTES} bytes.`
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1481
|
+
return size;
|
|
1208
1482
|
}
|
|
1209
1483
|
};
|
|
1210
|
-
function streamWireErrorMessage(cause) {
|
|
1211
|
-
return cause instanceof Error ? cause.message : "Metadata is invalid.";
|
|
1212
|
-
}
|
|
1213
1484
|
|
|
1214
1485
|
// packages/stream-connector/src/Runtime/Protocol/ZlinkStreamHeaderCodec.ts
|
|
1215
1486
|
var ZlinkStreamHeaderCodec = class {
|
|
1216
1487
|
static encode(header) {
|
|
1217
1488
|
const reply = isReplyKind2(header.kind);
|
|
1218
1489
|
if (!reply) validateName(header.name, header.kind === 5 /* Control */);
|
|
1490
|
+
validateEncodeFlags(header.flags);
|
|
1219
1491
|
validateHeaderSemantics(header);
|
|
1220
1492
|
ZlinkStreamMetadataCodec.size(header.metadata);
|
|
1221
1493
|
try {
|
|
@@ -1232,7 +1504,7 @@ var ZlinkStreamHeaderCodec = class {
|
|
|
1232
1504
|
} catch (cause) {
|
|
1233
1505
|
throw connectorError(
|
|
1234
1506
|
"validationFailed" /* ValidationFailed */,
|
|
1235
|
-
|
|
1507
|
+
streamWireErrorMessage(cause),
|
|
1236
1508
|
cause
|
|
1237
1509
|
);
|
|
1238
1510
|
}
|
|
@@ -1252,25 +1524,24 @@ var ZlinkStreamHeaderCodec = class {
|
|
|
1252
1524
|
correlationId: wire.correlationId,
|
|
1253
1525
|
actorSlot: wire.actorSlot
|
|
1254
1526
|
};
|
|
1527
|
+
if (!isReplyKind2(decoded.kind)) {
|
|
1528
|
+
validateName(decoded.name, decoded.kind === 5 /* Control */);
|
|
1529
|
+
}
|
|
1530
|
+
validateHeaderSemantics(decoded);
|
|
1255
1531
|
} catch (cause) {
|
|
1256
1532
|
throw connectorError(
|
|
1257
1533
|
"frameDecodeFailed" /* FrameDecodeFailed */,
|
|
1258
|
-
|
|
1534
|
+
streamWireErrorMessage(cause),
|
|
1259
1535
|
cause
|
|
1260
1536
|
);
|
|
1261
1537
|
}
|
|
1262
|
-
validateEnum(decoded.kind, decoded.codec, decoded.flags);
|
|
1263
|
-
if (!isReplyKind2(decoded.kind)) {
|
|
1264
|
-
validateName(decoded.name, decoded.kind === 5 /* Control */);
|
|
1265
|
-
}
|
|
1266
|
-
validateHeaderSemantics(decoded);
|
|
1267
1538
|
return decoded;
|
|
1268
1539
|
}
|
|
1269
1540
|
};
|
|
1270
1541
|
function isReplyKind2(kind) {
|
|
1271
1542
|
return kind === 3 /* Response */ || kind === 4 /* Error */;
|
|
1272
1543
|
}
|
|
1273
|
-
function
|
|
1544
|
+
function streamWireErrorMessage(cause) {
|
|
1274
1545
|
return cause instanceof Error ? cause.message : "Stream header is invalid.";
|
|
1275
1546
|
}
|
|
1276
1547
|
function buildHeader(kind, name, codec, metadata, compress, requestSeq, correlationId, actorSlot) {
|
|
@@ -1300,7 +1571,7 @@ function buildHeader(kind, name, codec, metadata, compress, requestSeq, correlat
|
|
|
1300
1571
|
};
|
|
1301
1572
|
}
|
|
1302
1573
|
function validateHeaderSemantics(header) {
|
|
1303
|
-
validateEnum(header.kind, header.codec
|
|
1574
|
+
validateEnum(header.kind, header.codec);
|
|
1304
1575
|
const hasRequestSeq = header.requestSeq !== void 0 || (header.flags & 1 /* HasRequestSeq */) !== 0;
|
|
1305
1576
|
const hasMetadata = header.metadata.count > 0 || (header.flags & 2 /* HasMetadata */) !== 0;
|
|
1306
1577
|
if (header.kind === 1 /* Send */ && hasRequestSeq) {
|
|
@@ -1333,13 +1604,15 @@ function validateHeaderSemantics(header) {
|
|
|
1333
1604
|
}
|
|
1334
1605
|
}
|
|
1335
1606
|
}
|
|
1336
|
-
function validateEnum(kind, codec
|
|
1337
|
-
if (!
|
|
1607
|
+
function validateEnum(kind, codec) {
|
|
1608
|
+
if (!isStreamWireMessageKind(kind)) {
|
|
1338
1609
|
throw connectorError("frameDecodeFailed" /* FrameDecodeFailed */, "Unknown stream message kind.");
|
|
1339
1610
|
}
|
|
1340
|
-
if (!
|
|
1611
|
+
if (!isStreamWireCodec(codec)) {
|
|
1341
1612
|
throw connectorError("frameDecodeFailed" /* FrameDecodeFailed */, "Unknown stream codec.");
|
|
1342
1613
|
}
|
|
1614
|
+
}
|
|
1615
|
+
function validateEncodeFlags(flags) {
|
|
1343
1616
|
const known = 1 /* HasRequestSeq */ | 2 /* HasMetadata */ | 4 /* PayloadCompressed */ | 8 /* HasCorrelationId */ | 16 /* HasFlowId */ | 32 /* HasActorSlot */;
|
|
1344
1617
|
if ((flags & ~known) !== 0) {
|
|
1345
1618
|
throw connectorError("frameDecodeFailed" /* FrameDecodeFailed */, "Unknown stream header flag.");
|
|
@@ -1347,8 +1620,8 @@ function validateEnum(kind, codec, flags) {
|
|
|
1347
1620
|
}
|
|
1348
1621
|
|
|
1349
1622
|
// packages/stream-connector/src/Runtime/Protocol/ZlinkStreamFrameProtocol.ts
|
|
1350
|
-
var ZLINK_STREAM_HEARTBEAT_PING =
|
|
1351
|
-
var ZLINK_STREAM_HEARTBEAT_PONG =
|
|
1623
|
+
var ZLINK_STREAM_HEARTBEAT_PING = ZlinkStreamControlPacket.HeartbeatPing;
|
|
1624
|
+
var ZLINK_STREAM_HEARTBEAT_PONG = ZlinkStreamControlPacket.HeartbeatPong;
|
|
1352
1625
|
var ZlinkStreamFrameProtocol = class {
|
|
1353
1626
|
constructor(options) {
|
|
1354
1627
|
this.options = options;
|
|
@@ -1416,172 +1689,37 @@ var ZlinkStreamFrameProtocol = class {
|
|
|
1416
1689
|
}
|
|
1417
1690
|
};
|
|
1418
1691
|
|
|
1419
|
-
// packages/stream-connector/src/Runtime/Transport/ZlinkStreamEndpoint.ts
|
|
1420
|
-
function inferTransport(endpoint) {
|
|
1421
|
-
const url = parseEndpoint(endpoint);
|
|
1422
|
-
switch (url.protocol) {
|
|
1423
|
-
case "ws:":
|
|
1424
|
-
return "webSocket" /* WebSocket */;
|
|
1425
|
-
case "wss:":
|
|
1426
|
-
return "webSocketSecure" /* WebSocketSecure */;
|
|
1427
|
-
default:
|
|
1428
|
-
throw connectorError(
|
|
1429
|
-
"configurationError" /* ConfigurationError */,
|
|
1430
|
-
"The TypeScript Stream Connector supports only ws:// and wss:// endpoints."
|
|
1431
|
-
);
|
|
1432
|
-
}
|
|
1433
|
-
}
|
|
1434
|
-
function parseEndpoint(endpoint) {
|
|
1435
|
-
try {
|
|
1436
|
-
return new URL(endpoint);
|
|
1437
|
-
} catch (cause) {
|
|
1438
|
-
throw connectorError("configurationError" /* ConfigurationError */, "Endpoint is invalid.", cause);
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
// packages/stream-connector/src/Runtime/ZlinkStreamConnectorOptions.ts
|
|
1443
|
-
function normalizeOptions(options, defaultTransportFactory) {
|
|
1444
|
-
const endpoint = options.endpoint;
|
|
1445
|
-
if (endpoint.trim().length === 0) {
|
|
1446
|
-
throw connectorError("configurationError" /* ConfigurationError */, "Endpoint must not be empty.");
|
|
1447
|
-
}
|
|
1448
|
-
const inferredTransport = inferTransport(endpoint);
|
|
1449
|
-
if (options.transport !== void 0 && options.transport !== inferredTransport) {
|
|
1450
|
-
throw connectorError(
|
|
1451
|
-
"configurationError" /* ConfigurationError */,
|
|
1452
|
-
"Configured transport conflicts with endpoint scheme."
|
|
1453
|
-
);
|
|
1454
|
-
}
|
|
1455
|
-
validatePositive(options.connectTimeoutMs ?? 5e3, "ConnectTimeout");
|
|
1456
|
-
validatePositive(options.requestTimeoutMs ?? 3e4, "RequestTimeout");
|
|
1457
|
-
validatePositive(options.waitTimeoutMs ?? 5e3, "WaitTimeout");
|
|
1458
|
-
validatePositive(options.maxSendPayloadSize ?? 64 * 1024, "MaxSendPayloadSize");
|
|
1459
|
-
validatePositive(options.maxReceivePayloadSize ?? 64 * 1024, "MaxReceivePayloadSize");
|
|
1460
|
-
validateHeartbeat(options.heartbeat);
|
|
1461
|
-
validateReconnect(options.reconnect);
|
|
1462
|
-
return {
|
|
1463
|
-
endpoint,
|
|
1464
|
-
transport: inferredTransport,
|
|
1465
|
-
connectTimeoutMs: options.connectTimeoutMs ?? 5e3,
|
|
1466
|
-
requestTimeoutMs: options.requestTimeoutMs ?? 3e4,
|
|
1467
|
-
waitTimeoutMs: options.waitTimeoutMs ?? 5e3,
|
|
1468
|
-
heartbeat: {
|
|
1469
|
-
enabled: options.heartbeat?.enabled ?? true,
|
|
1470
|
-
intervalMs: options.heartbeat?.intervalMs ?? 1e3,
|
|
1471
|
-
timeoutMs: options.heartbeat?.timeoutMs ?? 5e3
|
|
1472
|
-
},
|
|
1473
|
-
reconnect: {
|
|
1474
|
-
enabled: options.reconnect?.enabled ?? true,
|
|
1475
|
-
initialDelayMs: options.reconnect?.initialDelayMs ?? 250,
|
|
1476
|
-
maxDelayMs: options.reconnect?.maxDelayMs ?? 5e3,
|
|
1477
|
-
backoffFactor: options.reconnect?.backoffFactor ?? 2,
|
|
1478
|
-
maxAttempts: options.reconnect?.maxAttempts === void 0 ? 3 : options.reconnect.maxAttempts
|
|
1479
|
-
},
|
|
1480
|
-
maxSendPayloadSize: options.maxSendPayloadSize ?? 64 * 1024,
|
|
1481
|
-
maxReceivePayloadSize: options.maxReceivePayloadSize ?? 64 * 1024,
|
|
1482
|
-
dispatchMode: options.dispatchMode ?? "manual" /* Manual */,
|
|
1483
|
-
compression: options.compression ?? "lz4" /* Lz4 */,
|
|
1484
|
-
compressionCodec: resolveCompressionCodec2(options),
|
|
1485
|
-
nameResolver: options.nameResolver ?? defaultPacketNameResolver,
|
|
1486
|
-
transportFactory: options.transportFactory ?? defaultTransportFactory,
|
|
1487
|
-
codec: options.codec
|
|
1488
|
-
};
|
|
1489
|
-
}
|
|
1490
|
-
var defaultPacketNameResolver = {
|
|
1491
|
-
resolve(payloadType) {
|
|
1492
|
-
const declared = payloadType.packetName;
|
|
1493
|
-
if (typeof declared === "string" && declared.length > 0) {
|
|
1494
|
-
return declared;
|
|
1495
|
-
}
|
|
1496
|
-
return payloadType.name;
|
|
1497
|
-
}
|
|
1498
|
-
};
|
|
1499
|
-
function resolveCompressionCodec2(options) {
|
|
1500
|
-
const compression = options.compression ?? "lz4" /* Lz4 */;
|
|
1501
|
-
if (compression === "none" /* None */) {
|
|
1502
|
-
if (options.compressionCodec !== void 0) {
|
|
1503
|
-
throw connectorError(
|
|
1504
|
-
"configurationError" /* ConfigurationError */,
|
|
1505
|
-
"compressionCodec cannot be set when compression is none."
|
|
1506
|
-
);
|
|
1507
|
-
}
|
|
1508
|
-
return void 0;
|
|
1509
|
-
}
|
|
1510
|
-
return options.compressionCodec;
|
|
1511
|
-
}
|
|
1512
|
-
function validatePositive(value, name) {
|
|
1513
|
-
if (value <= 0) {
|
|
1514
|
-
throw connectorError("validationFailed" /* ValidationFailed */, `${name} must be positive.`);
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
function validateHeartbeat(options) {
|
|
1518
|
-
const enabled = options?.enabled ?? true;
|
|
1519
|
-
const intervalMs = options?.intervalMs ?? 1e3;
|
|
1520
|
-
const timeoutMs = options?.timeoutMs ?? 5e3;
|
|
1521
|
-
if (!enabled) {
|
|
1522
|
-
return;
|
|
1523
|
-
}
|
|
1524
|
-
validatePositive(intervalMs, "Heartbeat interval");
|
|
1525
|
-
validatePositive(timeoutMs, "Heartbeat timeout");
|
|
1526
|
-
if (timeoutMs <= intervalMs) {
|
|
1527
|
-
throw connectorError(
|
|
1528
|
-
"validationFailed" /* ValidationFailed */,
|
|
1529
|
-
"Heartbeat timeout must be greater than the heartbeat interval."
|
|
1530
|
-
);
|
|
1531
|
-
}
|
|
1532
|
-
}
|
|
1533
|
-
function validateReconnect(options) {
|
|
1534
|
-
const enabled = options?.enabled ?? true;
|
|
1535
|
-
if (!enabled) {
|
|
1536
|
-
return;
|
|
1537
|
-
}
|
|
1538
|
-
validatePositive(options?.initialDelayMs ?? 250, "Reconnect InitialDelay");
|
|
1539
|
-
validatePositive(options?.maxDelayMs ?? 5e3, "Reconnect MaxDelay");
|
|
1540
|
-
if ((options?.backoffFactor ?? 2) < 1) {
|
|
1541
|
-
throw connectorError(
|
|
1542
|
-
"validationFailed" /* ValidationFailed */,
|
|
1543
|
-
"Reconnect BackoffFactor must be at least 1.0."
|
|
1544
|
-
);
|
|
1545
|
-
}
|
|
1546
|
-
const maxAttempts = options?.maxAttempts === void 0 ? 3 : options.maxAttempts;
|
|
1547
|
-
if (maxAttempts !== null && !(Number.isFinite(maxAttempts) && maxAttempts > 0)) {
|
|
1548
|
-
throw connectorError(
|
|
1549
|
-
"validationFailed" /* ValidationFailed */,
|
|
1550
|
-
"Reconnect MaxAttempts must be null or positive."
|
|
1551
|
-
);
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
1692
|
// packages/stream-connector/src/Runtime/ZlinkStreamPendingRequests.ts
|
|
1556
1693
|
var ZlinkStreamPendingRequests = class {
|
|
1557
1694
|
nextRequestSeq = 1n;
|
|
1558
1695
|
active = /* @__PURE__ */ new Map();
|
|
1559
|
-
get count() {
|
|
1560
|
-
return this.active.size;
|
|
1561
|
-
}
|
|
1562
1696
|
create(packetName, timeoutMs) {
|
|
1697
|
+
if (this.nextRequestSeq > UINT64_MAX) {
|
|
1698
|
+
throw connectorError("sendFailed" /* SendFailed */, "Request sequence is exhausted.");
|
|
1699
|
+
}
|
|
1563
1700
|
const requestSeq = this.nextRequestSeq++;
|
|
1564
1701
|
let timeout;
|
|
1565
1702
|
let resolvePending;
|
|
1566
1703
|
let rejectPending;
|
|
1567
1704
|
const promise = new Promise(
|
|
1568
1705
|
(resolve, reject) => {
|
|
1569
|
-
timeout = setTimeout(() => {
|
|
1570
|
-
this.active.delete(requestSeq);
|
|
1571
|
-
reject(
|
|
1572
|
-
connectorError(
|
|
1573
|
-
"requestTimeout" /* RequestTimeout */,
|
|
1574
|
-
`Request '${packetName}' timed out.`
|
|
1575
|
-
)
|
|
1576
|
-
);
|
|
1577
|
-
}, timeoutMs);
|
|
1578
1706
|
resolvePending = resolve;
|
|
1579
1707
|
rejectPending = (error) => reject(connectorError(error.code, error.message, error.cause));
|
|
1580
1708
|
}
|
|
1581
1709
|
);
|
|
1582
|
-
|
|
1710
|
+
const tracked = {
|
|
1583
1711
|
packetName,
|
|
1584
1712
|
promise,
|
|
1713
|
+
startTimeout: () => {
|
|
1714
|
+
if (timeout !== void 0 || this.active.get(requestSeq) !== tracked) return;
|
|
1715
|
+
timeout = setTimeout(() => {
|
|
1716
|
+
this.active.delete(requestSeq);
|
|
1717
|
+
rejectPending({
|
|
1718
|
+
code: "requestTimeout" /* RequestTimeout */,
|
|
1719
|
+
message: `Request '${packetName}' timed out.`
|
|
1720
|
+
});
|
|
1721
|
+
}, timeoutMs);
|
|
1722
|
+
},
|
|
1585
1723
|
resolve: (value) => {
|
|
1586
1724
|
if (timeout !== void 0) {
|
|
1587
1725
|
clearTimeout(timeout);
|
|
@@ -1599,8 +1737,9 @@ var ZlinkStreamPendingRequests = class {
|
|
|
1599
1737
|
clearTimeout(timeout);
|
|
1600
1738
|
}
|
|
1601
1739
|
}
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1740
|
+
};
|
|
1741
|
+
this.active.set(requestSeq, tracked);
|
|
1742
|
+
return { requestSeq, promise, startTimeout: tracked.startTimeout };
|
|
1604
1743
|
}
|
|
1605
1744
|
/* stream connector spec §5.2: a pending request is matched by request_seq alone. */
|
|
1606
1745
|
resolve(requestSeq, value, metadata) {
|
|
@@ -1608,8 +1747,9 @@ var ZlinkStreamPendingRequests = class {
|
|
|
1608
1747
|
if (pending === void 0) {
|
|
1609
1748
|
return false;
|
|
1610
1749
|
}
|
|
1750
|
+
const payload = value();
|
|
1611
1751
|
this.active.delete(requestSeq);
|
|
1612
|
-
pending.resolve({ name: pending.packetName, metadata, payload
|
|
1752
|
+
pending.resolve({ name: pending.packetName, metadata, payload });
|
|
1613
1753
|
return true;
|
|
1614
1754
|
}
|
|
1615
1755
|
reject(requestSeq, error) {
|
|
@@ -1637,50 +1777,212 @@ var ZlinkStreamPendingRequests = class {
|
|
|
1637
1777
|
}
|
|
1638
1778
|
};
|
|
1639
1779
|
|
|
1640
|
-
// packages/stream-connector/src/Runtime/
|
|
1641
|
-
var
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
this.events = events;
|
|
1650
|
-
this.deliverOnArrival = deliverOnArrival;
|
|
1780
|
+
// packages/stream-connector/src/Runtime/ZlinkStreamActors.ts
|
|
1781
|
+
var ACTOR_BOUND = ZlinkStreamControlPacket.ActorBound;
|
|
1782
|
+
var ACTOR_UNBOUND = ZlinkStreamControlPacket.ActorUnbound;
|
|
1783
|
+
var zlinkStreamActorBinding = Symbol("zlink.stream.actorBinding");
|
|
1784
|
+
var DefaultZlinkStreamActor = class {
|
|
1785
|
+
constructor(connector, actorId, slot) {
|
|
1786
|
+
this.connector = connector;
|
|
1787
|
+
this.actorId = actorId;
|
|
1788
|
+
this.slot = slot;
|
|
1651
1789
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1790
|
+
bound = true;
|
|
1791
|
+
get isBound() {
|
|
1792
|
+
return this.bound;
|
|
1793
|
+
}
|
|
1794
|
+
send(payload, messageType) {
|
|
1795
|
+
this.ensureBound();
|
|
1796
|
+
return this.connector.sendForActor(this, payload, messageType);
|
|
1797
|
+
}
|
|
1798
|
+
request(payload, messageType) {
|
|
1799
|
+
this.ensureBound();
|
|
1800
|
+
return this.connector.requestForActor(this, payload, messageType);
|
|
1801
|
+
}
|
|
1802
|
+
on(nameOrType, handler, messageType) {
|
|
1803
|
+
return this.connector.onActorMessage(this, nameOrType, handler, messageType);
|
|
1804
|
+
}
|
|
1805
|
+
ensureBound() {
|
|
1806
|
+
if (!this.bound) {
|
|
1807
|
+
throw connectorError(
|
|
1808
|
+
"validationFailed" /* ValidationFailed */,
|
|
1809
|
+
`Actor '${this.actorId}' is no longer bound.`
|
|
1810
|
+
);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
close() {
|
|
1814
|
+
this.bound = false;
|
|
1815
|
+
}
|
|
1816
|
+
};
|
|
1817
|
+
var ZlinkStreamActors = class {
|
|
1818
|
+
constructor(connector, receivedMessages, events) {
|
|
1819
|
+
this.connector = connector;
|
|
1820
|
+
this.receivedMessages = receivedMessages;
|
|
1821
|
+
this.events = events;
|
|
1822
|
+
}
|
|
1823
|
+
bySlot = /* @__PURE__ */ new Map();
|
|
1824
|
+
byId = /* @__PURE__ */ new Map();
|
|
1825
|
+
boundHandlers = /* @__PURE__ */ new Set();
|
|
1826
|
+
unboundHandlers = /* @__PURE__ */ new Set();
|
|
1827
|
+
get snapshot() {
|
|
1828
|
+
return Object.freeze(Array.from(this.bySlot.values()));
|
|
1829
|
+
}
|
|
1830
|
+
find(actorId) {
|
|
1831
|
+
return this.byId.get(actorId);
|
|
1832
|
+
}
|
|
1833
|
+
onBound(handler) {
|
|
1834
|
+
return registerHandler(this.boundHandlers, handler);
|
|
1835
|
+
}
|
|
1836
|
+
onUnbound(handler) {
|
|
1837
|
+
return registerHandler(this.unboundHandlers, handler);
|
|
1838
|
+
}
|
|
1839
|
+
processControl(name, payload, signal) {
|
|
1840
|
+
if (name === ACTOR_BOUND) {
|
|
1841
|
+
this.bind(payload, signal);
|
|
1842
|
+
return true;
|
|
1843
|
+
}
|
|
1844
|
+
if (name === ACTOR_UNBOUND) {
|
|
1845
|
+
this.unbind(payload, signal);
|
|
1846
|
+
return true;
|
|
1847
|
+
}
|
|
1848
|
+
return false;
|
|
1849
|
+
}
|
|
1850
|
+
resolve(slot) {
|
|
1851
|
+
const actor = this.bySlot.get(slot);
|
|
1852
|
+
if (actor === void 0) {
|
|
1853
|
+
throw connectorError(
|
|
1854
|
+
"frameDecodeFailed" /* FrameDecodeFailed */,
|
|
1855
|
+
`Actor slot '${slot}' is not bound.`
|
|
1856
|
+
);
|
|
1857
|
+
}
|
|
1858
|
+
return actor;
|
|
1859
|
+
}
|
|
1860
|
+
closeAll(signal) {
|
|
1861
|
+
for (const actor of this.bySlot.values()) {
|
|
1862
|
+
this.bySlot.delete(actor.slot);
|
|
1863
|
+
this.byId.delete(actor.actorId);
|
|
1864
|
+
actor.close();
|
|
1865
|
+
this.queue(this.unboundHandlers, actor, signal);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
bind(payload, signal) {
|
|
1869
|
+
let binding;
|
|
1870
|
+
try {
|
|
1871
|
+
binding = decodeStreamWireActorBoundPayload(payload);
|
|
1872
|
+
} catch (cause) {
|
|
1873
|
+
throw connectorError(
|
|
1874
|
+
"frameDecodeFailed" /* FrameDecodeFailed */,
|
|
1875
|
+
cause instanceof Error ? cause.message : "Actor bound payload is invalid.",
|
|
1876
|
+
cause instanceof Error ? cause.cause : void 0
|
|
1877
|
+
);
|
|
1878
|
+
}
|
|
1879
|
+
const { slot, actorId } = binding;
|
|
1880
|
+
if (actorId.length === 0 || this.bySlot.has(slot) || this.byId.has(actorId)) {
|
|
1881
|
+
throw invalidControl("Actor bound identity is already in use.");
|
|
1882
|
+
}
|
|
1883
|
+
const actor = new DefaultZlinkStreamActor(this.connector, actorId, slot);
|
|
1884
|
+
this.bySlot.set(slot, actor);
|
|
1885
|
+
this.byId.set(actorId, actor);
|
|
1886
|
+
this.queue(this.boundHandlers, actor, signal);
|
|
1887
|
+
}
|
|
1888
|
+
unbind(payload, signal) {
|
|
1889
|
+
let slot;
|
|
1890
|
+
try {
|
|
1891
|
+
slot = decodeStreamWireActorUnboundPayload(payload);
|
|
1892
|
+
} catch (cause) {
|
|
1893
|
+
throw connectorError(
|
|
1894
|
+
"frameDecodeFailed" /* FrameDecodeFailed */,
|
|
1895
|
+
cause instanceof Error ? cause.message : "Actor unbound payload is invalid."
|
|
1896
|
+
);
|
|
1897
|
+
}
|
|
1898
|
+
const actor = this.bySlot.get(slot);
|
|
1899
|
+
if (slot === 0 || actor === void 0) {
|
|
1900
|
+
throw invalidControl(`Actor slot '${slot}' is not bound.`);
|
|
1901
|
+
}
|
|
1902
|
+
this.bySlot.delete(slot);
|
|
1903
|
+
this.byId.delete(actor.actorId);
|
|
1904
|
+
actor.close();
|
|
1905
|
+
this.queue(this.unboundHandlers, actor, signal);
|
|
1906
|
+
}
|
|
1907
|
+
queue(handlers, actor, signal) {
|
|
1908
|
+
this.receivedMessages.enqueueCallback(
|
|
1909
|
+
() => {
|
|
1910
|
+
for (const { handler } of currentRegistrations(handlers)) {
|
|
1911
|
+
this.events.runUserCallback(
|
|
1912
|
+
() => handler(actor, signal),
|
|
1913
|
+
"Actor lifecycle handler failed.",
|
|
1914
|
+
signal
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
},
|
|
1918
|
+
() => handlers.size
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
};
|
|
1922
|
+
function invalidControl(message) {
|
|
1923
|
+
return connectorError("frameDecodeFailed" /* FrameDecodeFailed */, message);
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
// packages/stream-connector/src/Runtime/ZlinkStreamReceivedMessages.ts
|
|
1927
|
+
function receives(registration, message) {
|
|
1928
|
+
return registration.actor === void 0 || registration.actor.isBound && registration.actor === message[zlinkStreamActorBinding];
|
|
1929
|
+
}
|
|
1930
|
+
var ZlinkStreamReceivedMessages = class {
|
|
1931
|
+
/**
|
|
1932
|
+
* @param deliverOnArrival `Immediate` runs registered handlers on the receive
|
|
1933
|
+
* path; `Manual` leaves them queued until {@link pump} runs them on the
|
|
1934
|
+
* caller's thread (spec stream-connector 32 §7). Wait surfaces observe the
|
|
1935
|
+
* queue in both modes, so they never depend on this flag.
|
|
1936
|
+
*/
|
|
1937
|
+
constructor(events, deliverOnArrival) {
|
|
1938
|
+
this.events = events;
|
|
1939
|
+
this.deliverOnArrival = deliverOnArrival;
|
|
1940
|
+
}
|
|
1941
|
+
handlers = /* @__PURE__ */ new Map();
|
|
1942
|
+
observers = /* @__PURE__ */ new Map();
|
|
1943
|
+
// A handler can be registered after messages for another name arrive, so the
|
|
1944
|
+
// queue is not a simple FIFO. Tombstones let us remove a deliverable entry
|
|
1945
|
+
// without shifting every later message on the hot receive path.
|
|
1946
|
+
queue = [];
|
|
1947
|
+
deliverable = [];
|
|
1948
|
+
queueHead = 0;
|
|
1949
|
+
queuedCount = 0;
|
|
1950
|
+
// True for as long as `pump` is on the stack. It
|
|
1951
|
+
// marks the execution context a registered handler runs in, so a `dispatch`
|
|
1952
|
+
// made from inside a handler is recognised as re-entry rather than a fresh
|
|
1953
|
+
// pump. It is not a lock: a single event loop admits no second thread, and
|
|
1954
|
+
// nothing ever waits for this flag to fall.
|
|
1955
|
+
draining = false;
|
|
1956
|
+
// Spec stream-connector 32 §10: arrivals per packet name on the current
|
|
1957
|
+
// connection. It is raised where a packet arrives, never where one is taken,
|
|
1958
|
+
// so consuming does not lower it and the dispatch mode does not change it.
|
|
1959
|
+
receivedCounts = /* @__PURE__ */ new Map();
|
|
1960
|
+
/**
|
|
1961
|
+
* @param actor The Actor handle that registers the handler, if any. Its
|
|
1962
|
+
* handler receives only that Actor's packets.
|
|
1963
|
+
*/
|
|
1964
|
+
on(name, handler, actor) {
|
|
1965
|
+
validateName(name);
|
|
1966
|
+
let set = this.handlers.get(name);
|
|
1967
|
+
if (set === void 0) {
|
|
1675
1968
|
set = /* @__PURE__ */ new Set();
|
|
1676
1969
|
this.handlers.set(name, set);
|
|
1677
1970
|
}
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1971
|
+
const registration = { handle: handler, actor };
|
|
1972
|
+
set.add(registration);
|
|
1973
|
+
let newlyDeliverable = false;
|
|
1974
|
+
for (let index = this.queueHead; index < this.queue.length; index += 1) {
|
|
1975
|
+
const queued = this.queue[index];
|
|
1976
|
+
if (queued?.kind === "message" && queued.indexed !== true && queued.message.name === name && receives(registration, queued.message)) {
|
|
1977
|
+
this.indexDeliverable(index);
|
|
1978
|
+
newlyDeliverable = true;
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
if (this.deliverOnArrival && newlyDeliverable) {
|
|
1982
|
+
queueMicrotask(() => this.pump());
|
|
1681
1983
|
}
|
|
1682
1984
|
return subscription(() => {
|
|
1683
|
-
set.delete(
|
|
1985
|
+
set.delete(registration);
|
|
1684
1986
|
if (set.size === 0 && this.handlers.get(name) === set) {
|
|
1685
1987
|
this.handlers.delete(name);
|
|
1686
1988
|
}
|
|
@@ -1750,6 +2052,11 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1750
2052
|
this.queue.push(...submittedCallbacks);
|
|
1751
2053
|
this.queueHead = 0;
|
|
1752
2054
|
this.queuedCount = submittedCallbacks.length;
|
|
2055
|
+
this.deliverable.length = 0;
|
|
2056
|
+
for (let index = 0; index < this.queue.length; index += 1) {
|
|
2057
|
+
this.queue[index].indexed = false;
|
|
2058
|
+
this.indexDeliverable(index);
|
|
2059
|
+
}
|
|
1753
2060
|
}
|
|
1754
2061
|
/**
|
|
1755
2062
|
* Releases every registered wait surface because the connection it was
|
|
@@ -1780,60 +2087,38 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1780
2087
|
}
|
|
1781
2088
|
this.queue.push({ kind: "message", message, signal });
|
|
1782
2089
|
this.queuedCount += 1;
|
|
2090
|
+
this.indexDeliverable(this.queue.length - 1);
|
|
1783
2091
|
if (this.deliverOnArrival) {
|
|
1784
|
-
this.
|
|
2092
|
+
this.pump();
|
|
1785
2093
|
}
|
|
1786
2094
|
}
|
|
1787
|
-
|
|
1788
|
-
|
|
2095
|
+
/**
|
|
2096
|
+
* @param callbacks The number of handler calls `callback` makes when it runs
|
|
2097
|
+
* with the handlers registered at that moment; a callback that runs a set
|
|
2098
|
+
* of handlers reads the set's size. One callback by default.
|
|
2099
|
+
*/
|
|
2100
|
+
enqueueCallback(callback, callbacks = () => 1) {
|
|
2101
|
+
this.queue.push({ kind: "callback", callback, callbacks });
|
|
1789
2102
|
this.queuedCount += 1;
|
|
2103
|
+
this.indexDeliverable(this.queue.length - 1);
|
|
1790
2104
|
if (this.deliverOnArrival) {
|
|
1791
|
-
this.
|
|
2105
|
+
this.pump();
|
|
1792
2106
|
}
|
|
1793
2107
|
}
|
|
1794
2108
|
/**
|
|
1795
2109
|
* Runs the registered handlers the receive path left queued. `Manual` calls
|
|
1796
|
-
* this from `dispatch`; `Immediate`
|
|
2110
|
+
* this from `dispatch`; `Immediate` drains on arrival.
|
|
1797
2111
|
*
|
|
1798
|
-
*
|
|
1799
|
-
*
|
|
1800
|
-
*
|
|
1801
|
-
*
|
|
1802
|
-
*
|
|
1803
|
-
* would deliver and returning is the whole of the correct behaviour.
|
|
2112
|
+
* The drain is synchronous: the connector does not wait for a handler
|
|
2113
|
+
* (spec stream-connector 32 §7), so nothing inside it awaits. A handler that
|
|
2114
|
+
* calls `dispatch` arrives back here from inside the drain it was started by;
|
|
2115
|
+
* the running loop already takes every deliverable entry, so returning is the
|
|
2116
|
+
* whole of the correct behaviour.
|
|
1804
2117
|
*/
|
|
1805
|
-
|
|
2118
|
+
pump() {
|
|
1806
2119
|
if (this.draining) {
|
|
1807
2120
|
return;
|
|
1808
2121
|
}
|
|
1809
|
-
this.scheduleDrain();
|
|
1810
|
-
await this.drainTask;
|
|
1811
|
-
}
|
|
1812
|
-
offerQueued(name, registration) {
|
|
1813
|
-
for (let index = this.queueHead; index < this.queue.length; index += 1) {
|
|
1814
|
-
const queued = this.queue[index];
|
|
1815
|
-
if (queued === void 0 || queued.kind !== "message" || queued.message.name !== name) {
|
|
1816
|
-
continue;
|
|
1817
|
-
}
|
|
1818
|
-
if (!registration.consume(queued.message)) {
|
|
1819
|
-
continue;
|
|
1820
|
-
}
|
|
1821
|
-
this.removeAt(index);
|
|
1822
|
-
return;
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
scheduleDrain() {
|
|
1826
|
-
if (this.drainTask !== void 0) {
|
|
1827
|
-
return;
|
|
1828
|
-
}
|
|
1829
|
-
this.drainTask = this.drain().finally(() => {
|
|
1830
|
-
this.drainTask = void 0;
|
|
1831
|
-
if (this.deliverOnArrival && this.findDeliverableIndex() >= 0) {
|
|
1832
|
-
this.scheduleDrain();
|
|
1833
|
-
}
|
|
1834
|
-
});
|
|
1835
|
-
}
|
|
1836
|
-
async drain() {
|
|
1837
2122
|
this.draining = true;
|
|
1838
2123
|
try {
|
|
1839
2124
|
for (let index = this.findDeliverableIndex(); index >= 0; index = this.findDeliverableIndex()) {
|
|
@@ -1841,30 +2126,53 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1841
2126
|
if (queued === void 0) continue;
|
|
1842
2127
|
this.removeAt(index);
|
|
1843
2128
|
if (queued.kind === "callback") {
|
|
1844
|
-
|
|
2129
|
+
this.events.runUserCallback(queued.callback, "Connector callback failed.");
|
|
1845
2130
|
continue;
|
|
1846
2131
|
}
|
|
1847
2132
|
const { message, signal } = queued;
|
|
1848
|
-
const handlers =
|
|
1849
|
-
for (const handler of handlers) {
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
message: "Typed message handler failed.",
|
|
1857
|
-
cause
|
|
1858
|
-
},
|
|
1859
|
-
signal
|
|
1860
|
-
);
|
|
1861
|
-
}
|
|
2133
|
+
const handlers = this.receiversOf(message);
|
|
2134
|
+
for (const handler of currentRegistrations(this.handlers.get(message.name), handlers)) {
|
|
2135
|
+
if (!receives(handler, message)) continue;
|
|
2136
|
+
this.events.runUserCallback(
|
|
2137
|
+
() => handler.handle(message, signal),
|
|
2138
|
+
"Typed message handler failed.",
|
|
2139
|
+
signal
|
|
2140
|
+
);
|
|
1862
2141
|
}
|
|
1863
2142
|
}
|
|
1864
2143
|
} finally {
|
|
1865
2144
|
this.draining = false;
|
|
1866
2145
|
}
|
|
1867
2146
|
}
|
|
2147
|
+
/**
|
|
2148
|
+
* True while a registered callback runs from {@link pump}: the execution
|
|
2149
|
+
* context spec stream-connector 32 §7 calls "inside a handler".
|
|
2150
|
+
*/
|
|
2151
|
+
get dispatching() {
|
|
2152
|
+
return this.draining;
|
|
2153
|
+
}
|
|
2154
|
+
get pendingCallbacks() {
|
|
2155
|
+
let count = 0;
|
|
2156
|
+
for (let index = this.queueHead; index < this.queue.length; index += 1) {
|
|
2157
|
+
const queued = this.queue[index];
|
|
2158
|
+
if (queued?.kind === "callback" && queued.callbacks() > 0) count += 1;
|
|
2159
|
+
if (queued?.kind === "message" && this.receiversOf(queued.message).length > 0) count += 1;
|
|
2160
|
+
}
|
|
2161
|
+
return count;
|
|
2162
|
+
}
|
|
2163
|
+
offerQueued(name, registration) {
|
|
2164
|
+
for (let index = this.queueHead; index < this.queue.length; index += 1) {
|
|
2165
|
+
const queued = this.queue[index];
|
|
2166
|
+
if (queued === void 0 || queued.kind !== "message" || queued.message.name !== name) {
|
|
2167
|
+
continue;
|
|
2168
|
+
}
|
|
2169
|
+
if (!registration.consume(queued.message)) {
|
|
2170
|
+
continue;
|
|
2171
|
+
}
|
|
2172
|
+
this.removeAt(index);
|
|
2173
|
+
return;
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
1868
2176
|
removeAt(index) {
|
|
1869
2177
|
this.queue[index] = void 0;
|
|
1870
2178
|
this.queuedCount -= 1;
|
|
@@ -1872,20 +2180,60 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1872
2180
|
this.compactQueue();
|
|
1873
2181
|
}
|
|
1874
2182
|
findDeliverableIndex() {
|
|
1875
|
-
|
|
2183
|
+
while (this.deliverable.length > 0) {
|
|
2184
|
+
const index = this.deliverable[0];
|
|
1876
2185
|
const queued = this.queue[index];
|
|
1877
|
-
if (
|
|
2186
|
+
if (this.isDeliverable(queued)) {
|
|
1878
2187
|
return index;
|
|
1879
2188
|
}
|
|
2189
|
+
if (queued !== void 0) queued.indexed = false;
|
|
2190
|
+
this.removeDeliverable();
|
|
1880
2191
|
}
|
|
1881
2192
|
return -1;
|
|
1882
2193
|
}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
2194
|
+
indexDeliverable(index) {
|
|
2195
|
+
const queued = this.queue[index];
|
|
2196
|
+
if (queued === void 0 || !this.isDeliverable(queued) || queued.indexed === true) return;
|
|
2197
|
+
queued.indexed = true;
|
|
2198
|
+
let position = this.deliverable.length;
|
|
2199
|
+
this.deliverable.push(index);
|
|
2200
|
+
while (position > 0) {
|
|
2201
|
+
const parent = Math.floor((position - 1) / 2);
|
|
2202
|
+
if (this.deliverable[parent] <= index) break;
|
|
2203
|
+
this.deliverable[position] = this.deliverable[parent];
|
|
2204
|
+
position = parent;
|
|
1887
2205
|
}
|
|
1888
|
-
|
|
2206
|
+
this.deliverable[position] = index;
|
|
2207
|
+
}
|
|
2208
|
+
isDeliverable(queued) {
|
|
2209
|
+
return queued?.kind === "callback" || queued?.kind === "message" && this.receiversOf(queued.message).length > 0;
|
|
2210
|
+
}
|
|
2211
|
+
/**
|
|
2212
|
+
* Spec stream-connector 32 §7 and §10: the handlers that receive `message`
|
|
2213
|
+
* are the connector handlers for its name and the handle handlers of the
|
|
2214
|
+
* Actor it carries. A packet no handler receives stays in the queue for the
|
|
2215
|
+
* wait surfaces. This is the one place that decides it.
|
|
2216
|
+
*/
|
|
2217
|
+
receiversOf(message) {
|
|
2218
|
+
const set = this.handlers.get(message.name);
|
|
2219
|
+
if (set === void 0) return [];
|
|
2220
|
+
return Array.from(set).filter((registration) => receives(registration, message));
|
|
2221
|
+
}
|
|
2222
|
+
removeDeliverable() {
|
|
2223
|
+
if (this.deliverable.length === 0) return;
|
|
2224
|
+
const last = this.deliverable.pop();
|
|
2225
|
+
if (this.deliverable.length === 0) return;
|
|
2226
|
+
let position = 0;
|
|
2227
|
+
for (; ; ) {
|
|
2228
|
+
const left = position * 2 + 1;
|
|
2229
|
+
if (left >= this.deliverable.length) break;
|
|
2230
|
+
const right = left + 1;
|
|
2231
|
+
const child = right < this.deliverable.length && this.deliverable[right] < this.deliverable[left] ? right : left;
|
|
2232
|
+
if (this.deliverable[child] >= last) break;
|
|
2233
|
+
this.deliverable[position] = this.deliverable[child];
|
|
2234
|
+
position = child;
|
|
2235
|
+
}
|
|
2236
|
+
this.deliverable[position] = last;
|
|
1889
2237
|
}
|
|
1890
2238
|
advanceHead() {
|
|
1891
2239
|
while (this.queueHead < this.queue.length && this.queue[this.queueHead] === void 0) {
|
|
@@ -1896,251 +2244,207 @@ var ZlinkStreamReceivedMessages = class {
|
|
|
1896
2244
|
if (this.queuedCount === 0) {
|
|
1897
2245
|
this.queue.length = 0;
|
|
1898
2246
|
this.queueHead = 0;
|
|
2247
|
+
this.deliverable.length = 0;
|
|
1899
2248
|
return;
|
|
1900
2249
|
}
|
|
1901
|
-
|
|
1902
|
-
|
|
2250
|
+
const removed = this.queue.length - this.queuedCount;
|
|
2251
|
+
if (shouldCompactBackingArray(removed, this.queue.length)) {
|
|
2252
|
+
let destination = 0;
|
|
2253
|
+
for (let source = this.queueHead; source < this.queue.length; source += 1) {
|
|
2254
|
+
const queued = this.queue[source];
|
|
2255
|
+
if (queued !== void 0) this.queue[destination++] = queued;
|
|
2256
|
+
}
|
|
2257
|
+
this.queue.length = destination;
|
|
1903
2258
|
this.queueHead = 0;
|
|
2259
|
+
this.deliverable.length = 0;
|
|
2260
|
+
for (let index = 0; index < this.queue.length; index += 1) {
|
|
2261
|
+
const queued = this.queue[index];
|
|
2262
|
+
if (queued === void 0) continue;
|
|
2263
|
+
queued.indexed = false;
|
|
2264
|
+
this.indexDeliverable(index);
|
|
2265
|
+
}
|
|
1904
2266
|
}
|
|
1905
2267
|
}
|
|
1906
2268
|
};
|
|
1907
2269
|
|
|
1908
2270
|
// packages/stream-connector/src/Runtime/ZlinkStreamFrameSender.ts
|
|
2271
|
+
var ZlinkStreamConnectionWriteQueue = class {
|
|
2272
|
+
/**
|
|
2273
|
+
* @param writeFailed Ends the connection whose transport write failed (spec
|
|
2274
|
+
* stream-connector 32 §9: a transport write failure ends the connection as
|
|
2275
|
+
* `TransportError`).
|
|
2276
|
+
*/
|
|
2277
|
+
constructor(connection, writeFailed) {
|
|
2278
|
+
this.connection = connection;
|
|
2279
|
+
this.writeFailed = writeFailed;
|
|
2280
|
+
}
|
|
2281
|
+
queue = /* @__PURE__ */ new Set();
|
|
2282
|
+
active;
|
|
2283
|
+
enqueue(operation) {
|
|
2284
|
+
this.queue.add(operation);
|
|
2285
|
+
this.advance();
|
|
2286
|
+
}
|
|
2287
|
+
cancel(operation, error) {
|
|
2288
|
+
if (this.queue.delete(operation) || this.active === operation) operation.fail(error);
|
|
2289
|
+
}
|
|
2290
|
+
/**
|
|
2291
|
+
* Spec stream-connector 32 §7: when the connection ends, frames that have not
|
|
2292
|
+
* reached the transport are not written and the frame being written is not
|
|
2293
|
+
* waited for. The operations of both fail with `error`.
|
|
2294
|
+
*/
|
|
2295
|
+
failAll(error) {
|
|
2296
|
+
const operations = Array.from(this.queue);
|
|
2297
|
+
if (this.active !== void 0) operations.unshift(this.active);
|
|
2298
|
+
this.queue.clear();
|
|
2299
|
+
this.active = void 0;
|
|
2300
|
+
for (const operation of operations) operation.fail(error);
|
|
2301
|
+
}
|
|
2302
|
+
advance() {
|
|
2303
|
+
if (this.active !== void 0) return;
|
|
2304
|
+
const next = this.queue.values().next().value;
|
|
2305
|
+
if (next === void 0) return;
|
|
2306
|
+
this.queue.delete(next);
|
|
2307
|
+
this.active = next;
|
|
2308
|
+
let write;
|
|
2309
|
+
try {
|
|
2310
|
+
write = Promise.resolve(this.connection.write(next.frame));
|
|
2311
|
+
} catch (error) {
|
|
2312
|
+
write = Promise.reject(error);
|
|
2313
|
+
}
|
|
2314
|
+
void write.then(
|
|
2315
|
+
() => next.complete(),
|
|
2316
|
+
(cause) => this.failWrite(next, cause)
|
|
2317
|
+
).finally(() => {
|
|
2318
|
+
if (this.active !== next) return;
|
|
2319
|
+
this.active = void 0;
|
|
2320
|
+
this.advance();
|
|
2321
|
+
});
|
|
2322
|
+
}
|
|
2323
|
+
/**
|
|
2324
|
+
* Spec stream-connector 32 §9: a transport write failure is `SendFailed` for
|
|
2325
|
+
* the operation of that write, and it ends the connection. The ending fails
|
|
2326
|
+
* every other operation of the connection with `Disconnected`. A write that
|
|
2327
|
+
* fails after the connection already ended belongs to that ending, which has
|
|
2328
|
+
* already failed its operation with `Disconnected`.
|
|
2329
|
+
*/
|
|
2330
|
+
failWrite(operation, cause) {
|
|
2331
|
+
if (this.active !== operation) return;
|
|
2332
|
+
const error = {
|
|
2333
|
+
code: "sendFailed" /* SendFailed */,
|
|
2334
|
+
message: `Frame write failed: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
2335
|
+
cause
|
|
2336
|
+
};
|
|
2337
|
+
operation.fail(new ZlinkStreamException(error));
|
|
2338
|
+
this.writeFailed(error);
|
|
2339
|
+
}
|
|
2340
|
+
};
|
|
1909
2341
|
var ZlinkStreamFrameSender = class {
|
|
1910
2342
|
constructor(protocol) {
|
|
1911
2343
|
this.protocol = protocol;
|
|
1912
2344
|
}
|
|
1913
|
-
|
|
1914
|
-
async send(connection, kind, name, payload, metadata, compress, requestSeq, signal, correlationId, actorSlot) {
|
|
2345
|
+
queues = /* @__PURE__ */ new Map();
|
|
2346
|
+
async send(connection, kind, name, payload, metadata, compress, requestSeq, signal, correlationId, actorSlot, expiry, onAccepted) {
|
|
1915
2347
|
throwIfAborted(signal);
|
|
1916
2348
|
await this.write(
|
|
1917
2349
|
connection,
|
|
1918
2350
|
this.protocol.encode(
|
|
1919
2351
|
kind,
|
|
1920
2352
|
name,
|
|
1921
|
-
payload,
|
|
1922
|
-
metadata,
|
|
1923
|
-
compress,
|
|
1924
|
-
requestSeq,
|
|
1925
|
-
correlationId,
|
|
1926
|
-
actorSlot
|
|
1927
|
-
),
|
|
1928
|
-
signal
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
await this.write(connection, this.protocol.encodeControl(name), signal);
|
|
1933
|
-
}
|
|
1934
|
-
async drain(signal) {
|
|
1935
|
-
while (this.pendingWrites.size > 0) {
|
|
1936
|
-
throwIfAborted(signal);
|
|
1937
|
-
await Promise.allSettled(Array.from(this.pendingWrites));
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
async write(connection, frame, signal) {
|
|
1941
|
-
const write = connection.write(frame, signal);
|
|
1942
|
-
this.pendingWrites.add(write);
|
|
1943
|
-
try {
|
|
1944
|
-
await write;
|
|
1945
|
-
} finally {
|
|
1946
|
-
this.pendingWrites.delete(write);
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
};
|
|
1950
|
-
|
|
1951
|
-
// packages/stream-connector/src/Runtime/Protocol/ZlinkSessionClosing.ts
|
|
1952
|
-
var ZLINK_SESSION_CLOSING = "session-closing";
|
|
1953
|
-
var reasons = {
|
|
1954
|
-
1: "ClientClose",
|
|
1955
|
-
2: "IdleTimeout",
|
|
1956
|
-
3: "HeartbeatTimeout",
|
|
1957
|
-
4: "ServerDrain",
|
|
1958
|
-
5: "ProtocolError",
|
|
1959
|
-
6: "TransportError"
|
|
1960
|
-
};
|
|
1961
|
-
function decodeSessionClosing(payload) {
|
|
1962
|
-
if (payload.length < 4 || payload[0] !== 1)
|
|
1963
|
-
throw new Error("Unsupported session-closing version.");
|
|
1964
|
-
const closeReason = reasons[payload[1]];
|
|
1965
|
-
if (closeReason === void 0) throw new Error("Unknown session-closing reason.");
|
|
1966
|
-
const length = payload[2] << 8 | payload[3];
|
|
1967
|
-
if (length > 512 || payload.length !== 4 + length)
|
|
1968
|
-
throw new Error("Invalid session-closing diagnostic length.");
|
|
1969
|
-
const diagnostic = length === 0 ? void 0 : new TextDecoder("utf-8", { fatal: true }).decode(payload.subarray(4));
|
|
1970
|
-
return { closeReason, diagnostic };
|
|
1971
|
-
}
|
|
1972
|
-
|
|
1973
|
-
// packages/stream-connector/src/Runtime/ZlinkStreamActors.ts
|
|
1974
|
-
var ACTOR_BOUND = "$zlink.actor.bound";
|
|
1975
|
-
var ACTOR_UNBOUND = "$zlink.actor.unbound";
|
|
1976
|
-
var zlinkStreamActorBinding = Symbol("zlink.stream.actorBinding");
|
|
1977
|
-
var DefaultZlinkStreamActor = class {
|
|
1978
|
-
constructor(connector, actorId, slot) {
|
|
1979
|
-
this.connector = connector;
|
|
1980
|
-
this.actorId = actorId;
|
|
1981
|
-
this.slot = slot;
|
|
1982
|
-
}
|
|
1983
|
-
bound = true;
|
|
1984
|
-
get isBound() {
|
|
1985
|
-
return this.bound;
|
|
1986
|
-
}
|
|
1987
|
-
send(payload, messageType) {
|
|
1988
|
-
this.ensureBound();
|
|
1989
|
-
return this.connector.sendForActor(this, payload, messageType);
|
|
1990
|
-
}
|
|
1991
|
-
request(payload, messageType) {
|
|
1992
|
-
this.ensureBound();
|
|
1993
|
-
return this.connector.requestForActor(this, payload, messageType);
|
|
1994
|
-
}
|
|
1995
|
-
on(nameOrType, handler, messageType) {
|
|
1996
|
-
return this.connector.onActorMessage(this, nameOrType, handler, messageType);
|
|
1997
|
-
}
|
|
1998
|
-
ensureBound() {
|
|
1999
|
-
if (!this.bound) {
|
|
2000
|
-
throw connectorError(
|
|
2001
|
-
"validationFailed" /* ValidationFailed */,
|
|
2002
|
-
`Actor '${this.actorId}' is no longer bound.`
|
|
2003
|
-
);
|
|
2004
|
-
}
|
|
2005
|
-
}
|
|
2006
|
-
close() {
|
|
2007
|
-
this.bound = false;
|
|
2008
|
-
}
|
|
2009
|
-
};
|
|
2010
|
-
var ZlinkStreamActors = class {
|
|
2011
|
-
constructor(connector, receivedMessages, events) {
|
|
2012
|
-
this.connector = connector;
|
|
2013
|
-
this.receivedMessages = receivedMessages;
|
|
2014
|
-
this.events = events;
|
|
2015
|
-
}
|
|
2016
|
-
bySlot = /* @__PURE__ */ new Map();
|
|
2017
|
-
byId = /* @__PURE__ */ new Map();
|
|
2018
|
-
issued = [];
|
|
2019
|
-
boundHandlers = /* @__PURE__ */ new Set();
|
|
2020
|
-
unboundHandlers = /* @__PURE__ */ new Set();
|
|
2021
|
-
get snapshot() {
|
|
2022
|
-
return Object.freeze(Array.from(this.bySlot.values()));
|
|
2023
|
-
}
|
|
2024
|
-
find(actorId) {
|
|
2025
|
-
return this.byId.get(actorId);
|
|
2026
|
-
}
|
|
2027
|
-
onBound(handler) {
|
|
2028
|
-
this.boundHandlers.add(handler);
|
|
2029
|
-
return subscription(() => this.boundHandlers.delete(handler));
|
|
2030
|
-
}
|
|
2031
|
-
onUnbound(handler) {
|
|
2032
|
-
this.unboundHandlers.add(handler);
|
|
2033
|
-
return subscription(() => this.unboundHandlers.delete(handler));
|
|
2034
|
-
}
|
|
2035
|
-
processControl(name, payload, signal) {
|
|
2036
|
-
if (name === ACTOR_BOUND) {
|
|
2037
|
-
this.bind(payload, signal);
|
|
2038
|
-
return true;
|
|
2039
|
-
}
|
|
2040
|
-
if (name === ACTOR_UNBOUND) {
|
|
2041
|
-
this.unbind(payload, signal);
|
|
2042
|
-
return true;
|
|
2043
|
-
}
|
|
2044
|
-
return false;
|
|
2045
|
-
}
|
|
2046
|
-
resolve(slot) {
|
|
2047
|
-
const actor = this.bySlot.get(slot);
|
|
2048
|
-
if (actor === void 0) {
|
|
2049
|
-
throw connectorError(
|
|
2050
|
-
"frameDecodeFailed" /* FrameDecodeFailed */,
|
|
2051
|
-
`Actor slot '${slot}' is not bound.`
|
|
2052
|
-
);
|
|
2053
|
-
}
|
|
2054
|
-
return actor;
|
|
2055
|
-
}
|
|
2056
|
-
closeAll(signal) {
|
|
2057
|
-
for (const actor of this.issued) {
|
|
2058
|
-
if (!actor.isBound) continue;
|
|
2059
|
-
this.bySlot.delete(actor.slot);
|
|
2060
|
-
this.byId.delete(actor.actorId);
|
|
2061
|
-
actor.close();
|
|
2062
|
-
this.queue(this.unboundHandlers, actor, signal);
|
|
2063
|
-
}
|
|
2064
|
-
this.issued.length = 0;
|
|
2065
|
-
}
|
|
2066
|
-
bind(payload, signal) {
|
|
2067
|
-
if (payload.length < 5 || payload[0] !== 1) {
|
|
2068
|
-
throw invalidControl("Actor bound payload is invalid.");
|
|
2069
|
-
}
|
|
2070
|
-
const slot = payload[1] << 8 | payload[2];
|
|
2071
|
-
const idLength = payload[3];
|
|
2072
|
-
if (slot === 0 || idLength === 0 || payload.length !== 4 + idLength) {
|
|
2073
|
-
throw invalidControl("Actor bound payload is invalid.");
|
|
2074
|
-
}
|
|
2075
|
-
let actorId;
|
|
2076
|
-
try {
|
|
2077
|
-
actorId = new TextDecoder("utf-8", { fatal: true }).decode(payload.subarray(4));
|
|
2078
|
-
} catch (cause) {
|
|
2079
|
-
throw connectorError(
|
|
2080
|
-
"frameDecodeFailed" /* FrameDecodeFailed */,
|
|
2081
|
-
"Actor id is not valid UTF-8.",
|
|
2082
|
-
cause
|
|
2083
|
-
);
|
|
2084
|
-
}
|
|
2085
|
-
if (actorId.length === 0 || this.bySlot.has(slot) || this.byId.has(actorId)) {
|
|
2086
|
-
throw invalidControl("Actor bound identity is already in use.");
|
|
2087
|
-
}
|
|
2088
|
-
const actor = new DefaultZlinkStreamActor(this.connector, actorId, slot);
|
|
2089
|
-
this.bySlot.set(slot, actor);
|
|
2090
|
-
this.byId.set(actorId, actor);
|
|
2091
|
-
this.issued.push(actor);
|
|
2092
|
-
this.queue(this.boundHandlers, actor, signal);
|
|
2093
|
-
}
|
|
2094
|
-
unbind(payload, signal) {
|
|
2095
|
-
if (payload.length !== 3 || payload[0] !== 1) {
|
|
2096
|
-
throw invalidControl("Actor unbound payload is invalid.");
|
|
2097
|
-
}
|
|
2098
|
-
const slot = payload[1] << 8 | payload[2];
|
|
2099
|
-
const actor = this.bySlot.get(slot);
|
|
2100
|
-
if (slot === 0 || actor === void 0) {
|
|
2101
|
-
throw invalidControl(`Actor slot '${slot}' is not bound.`);
|
|
2102
|
-
}
|
|
2103
|
-
this.bySlot.delete(slot);
|
|
2104
|
-
this.byId.delete(actor.actorId);
|
|
2105
|
-
actor.close();
|
|
2106
|
-
this.queue(this.unboundHandlers, actor, signal);
|
|
2353
|
+
payload,
|
|
2354
|
+
metadata,
|
|
2355
|
+
compress,
|
|
2356
|
+
requestSeq,
|
|
2357
|
+
correlationId,
|
|
2358
|
+
actorSlot
|
|
2359
|
+
),
|
|
2360
|
+
signal,
|
|
2361
|
+
expiry,
|
|
2362
|
+
onAccepted
|
|
2363
|
+
);
|
|
2107
2364
|
}
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2365
|
+
/**
|
|
2366
|
+
* Writes a control frame (heartbeat ping or pong). A control frame belongs to
|
|
2367
|
+
* no operation: its write failure ends the connection through the write
|
|
2368
|
+
* queue like any other write (spec stream-connector 32 §9), and a connection
|
|
2369
|
+
* that has already ended does not write it. The promise settles when the
|
|
2370
|
+
* write has ended either way.
|
|
2371
|
+
*/
|
|
2372
|
+
sendControl(connection, name) {
|
|
2373
|
+
const queue = this.queues.get(connection);
|
|
2374
|
+
if (queue === void 0) return Promise.resolve();
|
|
2375
|
+
const frame = this.protocol.encodeControl(name);
|
|
2376
|
+
return new Promise((resolve) => {
|
|
2377
|
+
queue.enqueue({ frame, complete: resolve, fail: () => resolve() });
|
|
2113
2378
|
});
|
|
2114
2379
|
}
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2380
|
+
/**
|
|
2381
|
+
* Gives a connection that has just been established its own write queue.
|
|
2382
|
+
* `writeFailed` ends that connection when one of its transport writes fails.
|
|
2383
|
+
*/
|
|
2384
|
+
open(connection, writeFailed) {
|
|
2385
|
+
this.queues.set(connection, new ZlinkStreamConnectionWriteQueue(connection, writeFailed));
|
|
2386
|
+
}
|
|
2387
|
+
/**
|
|
2388
|
+
* Ends the write queue of a connection that has ended, by close and by
|
|
2389
|
+
* transport loss alike. Every operation still in it fails with `error`.
|
|
2390
|
+
*/
|
|
2391
|
+
failUnwritten(connection, error) {
|
|
2392
|
+
const queue = this.queues.get(connection);
|
|
2393
|
+
if (queue === void 0) return;
|
|
2394
|
+
this.queues.delete(connection);
|
|
2395
|
+
queue.failAll(error);
|
|
2396
|
+
}
|
|
2397
|
+
write(connection, frame, signal, expiry, onAccepted) {
|
|
2398
|
+
throwIfAborted(signal);
|
|
2399
|
+
const queue = this.queues.get(connection);
|
|
2400
|
+
if (queue === void 0) {
|
|
2401
|
+
throw connectorError("disconnected" /* Disconnected */, "Connector is not connected.");
|
|
2136
2402
|
}
|
|
2403
|
+
let operation;
|
|
2404
|
+
let settled = false;
|
|
2405
|
+
const promise = new Promise((resolve, reject) => {
|
|
2406
|
+
const cleanup = () => signal?.removeEventListener("abort", onAbort);
|
|
2407
|
+
const onAbort = () => queue.cancel(
|
|
2408
|
+
operation,
|
|
2409
|
+
connectorError("disconnected" /* Disconnected */, "Operation canceled.")
|
|
2410
|
+
);
|
|
2411
|
+
operation = {
|
|
2412
|
+
frame,
|
|
2413
|
+
complete: () => {
|
|
2414
|
+
if (settled) return;
|
|
2415
|
+
settled = true;
|
|
2416
|
+
cleanup();
|
|
2417
|
+
resolve();
|
|
2418
|
+
},
|
|
2419
|
+
fail: (error) => {
|
|
2420
|
+
if (settled) return;
|
|
2421
|
+
settled = true;
|
|
2422
|
+
cleanup();
|
|
2423
|
+
reject(error);
|
|
2424
|
+
}
|
|
2425
|
+
};
|
|
2426
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2427
|
+
onAccepted?.();
|
|
2428
|
+
expiry?.then(void 0, (error) => queue.cancel(operation, error));
|
|
2429
|
+
queue.enqueue(operation);
|
|
2430
|
+
});
|
|
2431
|
+
return promise;
|
|
2137
2432
|
}
|
|
2138
2433
|
};
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2434
|
+
|
|
2435
|
+
// packages/stream-connector/src/Runtime/Protocol/ZlinkSessionClosing.ts
|
|
2436
|
+
var ZLINK_SESSION_CLOSING = ZlinkStreamControlPacket.SessionClosing;
|
|
2142
2437
|
|
|
2143
2438
|
// packages/stream-connector/src/Runtime/ZlinkStreamReceiveDispatcher.ts
|
|
2439
|
+
function closeReasonFor(error) {
|
|
2440
|
+
return error.code === "frameDecodeFailed" /* FrameDecodeFailed */ || error.code === "frameTooLarge" /* FrameTooLarge */ ? "ProtocolError" : "TransportError";
|
|
2441
|
+
}
|
|
2442
|
+
var ZlinkStreamConnectionEnd = class extends Error {
|
|
2443
|
+
constructor(error) {
|
|
2444
|
+
super(error.message);
|
|
2445
|
+
this.error = error;
|
|
2446
|
+
}
|
|
2447
|
+
};
|
|
2144
2448
|
var ZlinkStreamReceiveDispatcher = class {
|
|
2145
2449
|
constructor(protocol, pendingRequests, receivedMessages, frameSender, events, actors, serverClosing) {
|
|
2146
2450
|
this.protocol = protocol;
|
|
@@ -2160,15 +2464,20 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
2160
2464
|
* the recheck the rest of a dead connection's batch lands on the new
|
|
2161
2465
|
* connection's state, and the §10 arrival counts, which start at 0 when a
|
|
2162
2466
|
* connection is established, take the stale frames on top.
|
|
2163
|
-
* @param connectionForSend Resolves the connection a reply belongs on at the
|
|
2164
|
-
* moment it is written. The captured `connection` is the one the batch was
|
|
2165
|
-
* read from, which a reconnect may already have replaced.
|
|
2166
2467
|
*/
|
|
2167
|
-
async readAndDispatch(connection, signal, isCurrent
|
|
2468
|
+
async readAndDispatch(connection, signal, isCurrent) {
|
|
2168
2469
|
if (connection?.read === void 0) {
|
|
2169
2470
|
return { available: false, inbound: false };
|
|
2170
2471
|
}
|
|
2171
|
-
|
|
2472
|
+
let frameBytes;
|
|
2473
|
+
try {
|
|
2474
|
+
frameBytes = await connection.read(signal);
|
|
2475
|
+
} catch (cause) {
|
|
2476
|
+
if (signal?.aborted === true) throw cause;
|
|
2477
|
+
throw new ZlinkStreamConnectionEnd(
|
|
2478
|
+
toStreamError(cause, "disconnected" /* Disconnected */, "Transport read failed.")
|
|
2479
|
+
);
|
|
2480
|
+
}
|
|
2172
2481
|
if (isCurrent !== void 0 && !isCurrent()) {
|
|
2173
2482
|
return { available: false, inbound: false };
|
|
2174
2483
|
}
|
|
@@ -2179,87 +2488,96 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
2179
2488
|
try {
|
|
2180
2489
|
frames = this.protocol.decodeFrames(frameBytes);
|
|
2181
2490
|
} catch (cause) {
|
|
2182
|
-
throw
|
|
2491
|
+
throw new ZlinkStreamConnectionEnd(
|
|
2492
|
+
toStreamError(cause, "frameDecodeFailed" /* FrameDecodeFailed */, "Frame decode failed.")
|
|
2493
|
+
);
|
|
2183
2494
|
}
|
|
2184
2495
|
for (const frame of frames) {
|
|
2185
2496
|
if (isCurrent !== void 0 && !isCurrent()) {
|
|
2186
2497
|
break;
|
|
2187
2498
|
}
|
|
2188
2499
|
try {
|
|
2189
|
-
await this.dispatch(connection, frame.header, frame.payload, signal
|
|
2500
|
+
await this.dispatch(connection, frame.header, frame.payload, signal);
|
|
2190
2501
|
} catch (cause) {
|
|
2191
|
-
if (
|
|
2192
|
-
throw cause;
|
|
2193
|
-
}
|
|
2502
|
+
if (cause instanceof ZlinkStreamConnectionEnd) throw cause;
|
|
2194
2503
|
const error = toStreamError(
|
|
2195
2504
|
cause,
|
|
2196
2505
|
"frameDecodeFailed" /* FrameDecodeFailed */,
|
|
2197
2506
|
"Frame dispatch failed."
|
|
2198
2507
|
);
|
|
2199
2508
|
if (error.code === "frameDecodeFailed" /* FrameDecodeFailed */) {
|
|
2200
|
-
throw
|
|
2509
|
+
throw new ZlinkStreamConnectionEnd(error);
|
|
2201
2510
|
}
|
|
2202
|
-
|
|
2511
|
+
this.events.publishError(error, signal);
|
|
2203
2512
|
}
|
|
2204
2513
|
}
|
|
2205
2514
|
return { available: true, inbound: true };
|
|
2206
2515
|
}
|
|
2207
|
-
|
|
2516
|
+
/**
|
|
2517
|
+
* Spec stream-connector 32 §4.7 and §9: a decompressed payload over the
|
|
2518
|
+
* receive limit is `FrameTooLarge` and ends the connection as a protocol
|
|
2519
|
+
* error, like a wire payload over the limit. A payload that does not
|
|
2520
|
+
* decompress fails only its packet.
|
|
2521
|
+
*/
|
|
2522
|
+
decodePayload(header, payload) {
|
|
2523
|
+
try {
|
|
2524
|
+
return this.protocol.decodePayload(header, payload);
|
|
2525
|
+
} catch (cause) {
|
|
2526
|
+
const error = toStreamError(
|
|
2527
|
+
cause,
|
|
2528
|
+
"decompressionFailed" /* DecompressionFailed */,
|
|
2529
|
+
"Decompression failed."
|
|
2530
|
+
);
|
|
2531
|
+
if (error.code === "frameTooLarge" /* FrameTooLarge */) {
|
|
2532
|
+
throw new ZlinkStreamConnectionEnd(error);
|
|
2533
|
+
}
|
|
2534
|
+
throw cause;
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
async dispatch(connection, header, payload, signal) {
|
|
2208
2538
|
const actor = header.actorSlot === void 0 ? void 0 : this.actors.resolve(header.actorSlot);
|
|
2209
2539
|
if (header.kind === 3 /* Response */ && header.requestSeq !== void 0) {
|
|
2210
2540
|
try {
|
|
2211
|
-
|
|
2541
|
+
this.pendingRequests.resolve(
|
|
2212
2542
|
header.requestSeq,
|
|
2213
|
-
{
|
|
2543
|
+
() => ({
|
|
2214
2544
|
codec: header.codec,
|
|
2215
|
-
payload: this.
|
|
2216
|
-
},
|
|
2545
|
+
payload: this.decodePayload(header, payload)
|
|
2546
|
+
}),
|
|
2217
2547
|
header.metadata
|
|
2218
|
-
)
|
|
2219
|
-
await this.events.publishError(
|
|
2220
|
-
{
|
|
2221
|
-
code: "frameDecodeFailed" /* FrameDecodeFailed */,
|
|
2222
|
-
message: `Response request sequence '${header.requestSeq}' has no pending request.`
|
|
2223
|
-
},
|
|
2224
|
-
signal
|
|
2225
|
-
);
|
|
2226
|
-
}
|
|
2548
|
+
);
|
|
2227
2549
|
} catch (cause) {
|
|
2550
|
+
if (cause instanceof ZlinkStreamConnectionEnd) throw cause;
|
|
2228
2551
|
const decodeError = toStreamError(
|
|
2229
2552
|
cause,
|
|
2230
2553
|
"decompressionFailed" /* DecompressionFailed */,
|
|
2231
2554
|
"Decompression failed."
|
|
2232
2555
|
);
|
|
2233
2556
|
if (!this.pendingRequests.reject(header.requestSeq, decodeError)) {
|
|
2234
|
-
|
|
2557
|
+
this.events.publishError(decodeError, signal);
|
|
2235
2558
|
}
|
|
2236
2559
|
}
|
|
2237
2560
|
return;
|
|
2238
2561
|
}
|
|
2239
|
-
if (header.kind === 4 /* Error */
|
|
2562
|
+
if (header.kind === 4 /* Error */) {
|
|
2563
|
+
let error;
|
|
2240
2564
|
try {
|
|
2241
|
-
|
|
2242
|
-
if (!this.pendingRequests.reject(header.requestSeq, remoteError)) {
|
|
2243
|
-
await this.events.publishError(remoteError, signal);
|
|
2244
|
-
}
|
|
2565
|
+
error = decodeRemoteError(this.decodePayload(header, payload));
|
|
2245
2566
|
} catch (cause) {
|
|
2246
|
-
|
|
2567
|
+
if (cause instanceof ZlinkStreamConnectionEnd) throw cause;
|
|
2568
|
+
error = toStreamError(
|
|
2247
2569
|
cause,
|
|
2248
2570
|
"frameDecodeFailed" /* FrameDecodeFailed */,
|
|
2249
2571
|
"Remote error payload is invalid."
|
|
2250
2572
|
);
|
|
2251
|
-
if (!this.pendingRequests.reject(header.requestSeq, decodeError)) {
|
|
2252
|
-
await this.events.publishError(decodeError, signal);
|
|
2253
|
-
}
|
|
2254
2573
|
}
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
await this.events.publishError(decodeRemoteError(this.protocol, header, payload), signal);
|
|
2574
|
+
if (header.requestSeq === void 0 || !this.pendingRequests.reject(header.requestSeq, error)) {
|
|
2575
|
+
this.events.publishError(error, signal);
|
|
2576
|
+
}
|
|
2259
2577
|
return;
|
|
2260
2578
|
}
|
|
2261
2579
|
if (header.kind === 5 /* Control */) {
|
|
2262
|
-
await this.dispatchControl(connection, header, payload, signal
|
|
2580
|
+
await this.dispatchControl(connection, header, payload, signal);
|
|
2263
2581
|
return;
|
|
2264
2582
|
}
|
|
2265
2583
|
if (header.kind === 1 /* Send */) {
|
|
@@ -2267,7 +2585,7 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
2267
2585
|
{
|
|
2268
2586
|
name: header.name,
|
|
2269
2587
|
metadata: header.metadata,
|
|
2270
|
-
payload: { codec: header.codec, payload: this.
|
|
2588
|
+
payload: { codec: header.codec, payload: this.decodePayload(header, payload) },
|
|
2271
2589
|
actorId: actor?.actorId,
|
|
2272
2590
|
[zlinkStreamActorBinding]: actor
|
|
2273
2591
|
},
|
|
@@ -2275,12 +2593,12 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
2275
2593
|
);
|
|
2276
2594
|
}
|
|
2277
2595
|
}
|
|
2278
|
-
async dispatchControl(connection, header, payload, signal
|
|
2596
|
+
async dispatchControl(connection, header, payload, signal) {
|
|
2279
2597
|
if (this.actors.processControl(header.name, payload, signal)) {
|
|
2280
2598
|
return;
|
|
2281
2599
|
}
|
|
2282
2600
|
if (header.name === ZLINK_SESSION_CLOSING) {
|
|
2283
|
-
const closing =
|
|
2601
|
+
const closing = decodeStreamWireSessionClosing(payload);
|
|
2284
2602
|
await this.serverClosing?.(closing.closeReason);
|
|
2285
2603
|
return;
|
|
2286
2604
|
}
|
|
@@ -2291,18 +2609,7 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
2291
2609
|
);
|
|
2292
2610
|
}
|
|
2293
2611
|
if (header.name === ZLINK_STREAM_HEARTBEAT_PING) {
|
|
2294
|
-
|
|
2295
|
-
await this.frameSender.sendControl(
|
|
2296
|
-
connectionForSend?.() ?? connection,
|
|
2297
|
-
ZLINK_STREAM_HEARTBEAT_PONG,
|
|
2298
|
-
signal
|
|
2299
|
-
);
|
|
2300
|
-
} catch (cause) {
|
|
2301
|
-
throw connectorError(
|
|
2302
|
-
"sendFailed" /* SendFailed */,
|
|
2303
|
-
cause instanceof Error ? cause.message : "Heartbeat pong send failed."
|
|
2304
|
-
);
|
|
2305
|
-
}
|
|
2612
|
+
await this.frameSender.sendControl(connection, ZLINK_STREAM_HEARTBEAT_PONG);
|
|
2306
2613
|
return;
|
|
2307
2614
|
}
|
|
2308
2615
|
if (header.name !== ZLINK_STREAM_HEARTBEAT_PONG) {
|
|
@@ -2310,8 +2617,7 @@ var ZlinkStreamReceiveDispatcher = class {
|
|
|
2310
2617
|
}
|
|
2311
2618
|
}
|
|
2312
2619
|
};
|
|
2313
|
-
function decodeRemoteError(
|
|
2314
|
-
const decodedPayload = protocol.decodePayload(header, payload);
|
|
2620
|
+
function decodeRemoteError(decodedPayload) {
|
|
2315
2621
|
let decoded;
|
|
2316
2622
|
try {
|
|
2317
2623
|
decoded = JSON.parse(utf8Decode2(decodedPayload));
|
|
@@ -2333,8 +2639,11 @@ function decodeRemoteError(protocol, header, payload) {
|
|
|
2333
2639
|
}
|
|
2334
2640
|
|
|
2335
2641
|
// packages/stream-connector/src/Runtime/ZlinkStreamConnectorLifecycle.ts
|
|
2642
|
+
var RECONNECT_JITTER_FLOOR = 0.5;
|
|
2336
2643
|
function randomizedDelay(baseDelayMs) {
|
|
2337
|
-
return Math.round(
|
|
2644
|
+
return Math.round(
|
|
2645
|
+
baseDelayMs * (RECONNECT_JITTER_FLOOR + Math.random() * (1 - RECONNECT_JITTER_FLOOR))
|
|
2646
|
+
);
|
|
2338
2647
|
}
|
|
2339
2648
|
var ZlinkStreamConnectorLifecycle = class {
|
|
2340
2649
|
constructor(options, pendingRequests, frameSender, receiveDispatcher, receivedMessages, actors, events) {
|
|
@@ -2358,11 +2667,11 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2358
2667
|
lastInboundAt = 0;
|
|
2359
2668
|
closeTask;
|
|
2360
2669
|
connectTask;
|
|
2670
|
+
connectAbort;
|
|
2361
2671
|
disconnectTask;
|
|
2362
2672
|
closeRequested = false;
|
|
2363
2673
|
disconnectedPublished = false;
|
|
2364
2674
|
closeReasonValue;
|
|
2365
|
-
lateConnectCleanupError;
|
|
2366
2675
|
get isConnected() {
|
|
2367
2676
|
return this.currentState === "connected" /* Connected */;
|
|
2368
2677
|
}
|
|
@@ -2389,17 +2698,22 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2389
2698
|
});
|
|
2390
2699
|
return await this.connectTask;
|
|
2391
2700
|
}
|
|
2701
|
+
/**
|
|
2702
|
+
* Spec stream-connector 32 §7: close does not wait for a connect attempt or a
|
|
2703
|
+
* reconnect delay to run its course. The attempts listen to one controller
|
|
2704
|
+
* the lifecycle owns; close aborts it, and the caller's signal is forwarded
|
|
2705
|
+
* to it.
|
|
2706
|
+
*/
|
|
2392
2707
|
async connectOnce(signal) {
|
|
2393
|
-
|
|
2708
|
+
const attempts = new AbortController();
|
|
2709
|
+
const forwardAbort = () => attempts.abort();
|
|
2710
|
+
signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
2711
|
+
this.connectAbort = attempts;
|
|
2712
|
+
this.setState("connecting" /* Connecting */, void 0, signal);
|
|
2394
2713
|
try {
|
|
2395
|
-
const connection = await this.connectWithReconnect(signal);
|
|
2714
|
+
const connection = await this.connectWithReconnect(attempts.signal);
|
|
2396
2715
|
if (this.closeRequested) {
|
|
2397
|
-
|
|
2398
|
-
await connection.close(signal);
|
|
2399
|
-
} catch (error) {
|
|
2400
|
-
this.lateConnectCleanupError = error;
|
|
2401
|
-
throw error;
|
|
2402
|
-
}
|
|
2716
|
+
await this.closeTransport(connection, void 0, signal);
|
|
2403
2717
|
throw connectorError(
|
|
2404
2718
|
"disconnected" /* Disconnected */,
|
|
2405
2719
|
"Connector closed while connecting."
|
|
@@ -2407,41 +2721,61 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2407
2721
|
}
|
|
2408
2722
|
this.currentConnection = connection;
|
|
2409
2723
|
this.connectionGeneration += 1;
|
|
2724
|
+
const generation = this.connectionGeneration;
|
|
2725
|
+
this.frameSender.open(connection, (error) => {
|
|
2726
|
+
void this.disconnectForTransportFailure(error, connection, generation);
|
|
2727
|
+
});
|
|
2410
2728
|
this.disconnectedPublished = false;
|
|
2411
2729
|
this.receivedMessages.resetForNewConnection();
|
|
2412
2730
|
this.lastInboundAt = Date.now();
|
|
2413
|
-
|
|
2731
|
+
this.setState("connected" /* Connected */, void 0, signal);
|
|
2414
2732
|
this.startHeartbeat();
|
|
2415
2733
|
this.startReceiveLoop();
|
|
2416
2734
|
} catch (cause) {
|
|
2417
2735
|
if (this.closeRequested) {
|
|
2418
2736
|
const message = cause instanceof Error ? cause.message : "Connector closed while connecting.";
|
|
2419
|
-
|
|
2420
|
-
|
|
2737
|
+
throw connectorError("disconnected" /* Disconnected */, message, cause);
|
|
2738
|
+
}
|
|
2739
|
+
if (signal?.aborted === true) {
|
|
2740
|
+
this.setState("disconnected" /* Disconnected */, void 0, signal);
|
|
2741
|
+
throwIfAborted(signal);
|
|
2421
2742
|
}
|
|
2422
2743
|
const error = toStreamError(cause, "connectTimeout" /* ConnectTimeout */, "Connect failed.");
|
|
2423
|
-
this.closeReasonValue
|
|
2424
|
-
|
|
2425
|
-
this.
|
|
2744
|
+
this.closeReasonValue = "TransportError";
|
|
2745
|
+
this.setState("disconnected" /* Disconnected */, error, signal);
|
|
2746
|
+
this.disconnectedPublished = false;
|
|
2747
|
+
this.publishDisconnected(signal);
|
|
2426
2748
|
throw new ZlinkStreamException(error);
|
|
2749
|
+
} finally {
|
|
2750
|
+
signal?.removeEventListener("abort", forwardAbort);
|
|
2751
|
+
this.connectAbort = void 0;
|
|
2427
2752
|
}
|
|
2428
2753
|
}
|
|
2754
|
+
/**
|
|
2755
|
+
* Spec stream-connector 32 §7: a `close` called outside the registered
|
|
2756
|
+
* handlers and callbacks returns once the close work has ended. One called
|
|
2757
|
+
* from inside them returns right after starting it; the result goes to the
|
|
2758
|
+
* outside caller, so a callback never waits for the close of the path that
|
|
2759
|
+
* is running it.
|
|
2760
|
+
*/
|
|
2429
2761
|
async close(signal) {
|
|
2430
|
-
this.closeReasonValue
|
|
2431
|
-
|
|
2432
|
-
if (this.closeTask !== void 0) {
|
|
2433
|
-
return await this.closeTask;
|
|
2762
|
+
if (this.currentConnection !== void 0 || this.closeReasonValue === void 0) {
|
|
2763
|
+
this.closeReasonValue = "ClientClose";
|
|
2434
2764
|
}
|
|
2435
|
-
|
|
2765
|
+
this.closeRequested = true;
|
|
2766
|
+
if (this.closeTask === void 0 && this.currentState === "closed" /* Closed */) {
|
|
2436
2767
|
return;
|
|
2437
2768
|
}
|
|
2438
|
-
this.closeTask
|
|
2769
|
+
const closeTask = this.closeTask ??= this.closeOnce(signal).finally(() => {
|
|
2439
2770
|
this.closeTask = void 0;
|
|
2440
2771
|
});
|
|
2441
|
-
|
|
2772
|
+
if (this.receivedMessages.dispatching) {
|
|
2773
|
+
void closeTask.catch(() => void 0);
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
return await closeTask;
|
|
2442
2777
|
}
|
|
2443
2778
|
async serverClosing(reason) {
|
|
2444
|
-
this.closeReasonValue = reason;
|
|
2445
2779
|
const error = {
|
|
2446
2780
|
code: "disconnected" /* Disconnected */,
|
|
2447
2781
|
message: `Server closed the session: ${reason}.`
|
|
@@ -2449,41 +2783,18 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2449
2783
|
await this.disconnectForTransportFailure(
|
|
2450
2784
|
error,
|
|
2451
2785
|
this.currentConnection,
|
|
2452
|
-
this.connectionGeneration
|
|
2786
|
+
this.connectionGeneration,
|
|
2787
|
+
reason
|
|
2453
2788
|
);
|
|
2454
2789
|
}
|
|
2455
2790
|
async closeOnce(signal) {
|
|
2791
|
+
this.connectAbort?.abort();
|
|
2456
2792
|
await this.connectTask?.catch(() => void 0);
|
|
2457
2793
|
await this.disconnectTask?.catch(() => void 0);
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
const errors = [];
|
|
2463
|
-
if (this.lateConnectCleanupError !== void 0) {
|
|
2464
|
-
errors.push(this.lateConnectCleanupError);
|
|
2465
|
-
this.lateConnectCleanupError = void 0;
|
|
2466
|
-
}
|
|
2467
|
-
try {
|
|
2468
|
-
await this.frameSender.drain(signal);
|
|
2469
|
-
} catch (error) {
|
|
2470
|
-
errors.push(error);
|
|
2471
|
-
}
|
|
2472
|
-
try {
|
|
2473
|
-
await connection?.close(signal);
|
|
2474
|
-
} catch (error) {
|
|
2475
|
-
errors.push(error);
|
|
2476
|
-
}
|
|
2477
|
-
this.pendingRequests.failAll({
|
|
2478
|
-
code: "disconnected" /* Disconnected */,
|
|
2479
|
-
message: "Connector closed."
|
|
2480
|
-
});
|
|
2481
|
-
this.receivedMessages.connectionEnded();
|
|
2482
|
-
this.actors.closeAll(signal);
|
|
2483
|
-
this.queueState("closed" /* Closed */, void 0, signal);
|
|
2484
|
-
this.queueDisconnected(signal);
|
|
2485
|
-
if (errors.length === 1) throw errors[0];
|
|
2486
|
-
if (errors.length > 1) throw new AggregateError(errors, "Stream connector close failed.");
|
|
2794
|
+
await this.tearDownConnection(
|
|
2795
|
+
{ code: "disconnected" /* Disconnected */, message: "Connector closed." },
|
|
2796
|
+
signal
|
|
2797
|
+
);
|
|
2487
2798
|
}
|
|
2488
2799
|
/**
|
|
2489
2800
|
* Spec stream-connector 32 §7: `dispatch` runs the callbacks the receive loop
|
|
@@ -2499,7 +2810,7 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2499
2810
|
if (this.options.dispatchMode !== "immediate" /* Immediate */) {
|
|
2500
2811
|
await this.settleReceiveLoop();
|
|
2501
2812
|
}
|
|
2502
|
-
|
|
2813
|
+
this.receivedMessages.pump();
|
|
2503
2814
|
}
|
|
2504
2815
|
connectionForSend() {
|
|
2505
2816
|
if (this.currentConnection === void 0 || this.currentState !== "connected" /* Connected */) {
|
|
@@ -2512,8 +2823,7 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2512
2823
|
const result = await this.receiveDispatcher.readAndDispatch(
|
|
2513
2824
|
connection,
|
|
2514
2825
|
signal,
|
|
2515
|
-
() => this.isCurrentConnection(connection, generation)
|
|
2516
|
-
() => this.connectionForSend()
|
|
2826
|
+
() => this.isCurrentConnection(connection, generation)
|
|
2517
2827
|
);
|
|
2518
2828
|
if (result.inbound && this.isCurrentConnection(connection, generation)) {
|
|
2519
2829
|
this.lastInboundAt = Date.now();
|
|
@@ -2522,7 +2832,10 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2522
2832
|
}
|
|
2523
2833
|
async connectWithReconnect(signal) {
|
|
2524
2834
|
let attempt = 0;
|
|
2525
|
-
let delayMs =
|
|
2835
|
+
let delayMs = Math.min(
|
|
2836
|
+
this.options.reconnect.initialDelayMs,
|
|
2837
|
+
this.options.reconnect.maxDelayMs
|
|
2838
|
+
);
|
|
2526
2839
|
let lastError;
|
|
2527
2840
|
const maxAttempts = this.options.reconnect.enabled ? this.options.reconnect.maxAttempts : 1;
|
|
2528
2841
|
const unlimited = maxAttempts === null;
|
|
@@ -2532,10 +2845,10 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2532
2845
|
return await this.options.transportFactory.connect(this.options, signal);
|
|
2533
2846
|
} catch (cause) {
|
|
2534
2847
|
lastError = toStreamError(cause, "connectTimeout" /* ConnectTimeout */, "Connect failed.");
|
|
2535
|
-
if (!this.options.reconnect.enabled || !unlimited && attempt >= maxAttempts) {
|
|
2848
|
+
if (this.closeRequested || !this.options.reconnect.enabled || !unlimited && attempt >= maxAttempts) {
|
|
2536
2849
|
break;
|
|
2537
2850
|
}
|
|
2538
|
-
|
|
2851
|
+
this.setState("reconnecting" /* Reconnecting */, lastError, signal);
|
|
2539
2852
|
await delay(randomizedDelay(delayMs), signal);
|
|
2540
2853
|
delayMs = Math.min(
|
|
2541
2854
|
this.options.reconnect.maxDelayMs,
|
|
@@ -2603,12 +2916,8 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2603
2916
|
}
|
|
2604
2917
|
} catch (cause) {
|
|
2605
2918
|
if (signal.aborted) return;
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
"frameDecodeFailed" /* FrameDecodeFailed */,
|
|
2609
|
-
"Receive loop failed."
|
|
2610
|
-
);
|
|
2611
|
-
await this.disconnectForTransportFailure(error, connection, generation);
|
|
2919
|
+
if (!(cause instanceof ZlinkStreamConnectionEnd)) throw cause;
|
|
2920
|
+
await this.disconnectForTransportFailure(cause.error, connection, generation);
|
|
2612
2921
|
} finally {
|
|
2613
2922
|
this.receiveLoopSleeping = false;
|
|
2614
2923
|
this.releaseReceiveLoopSettled();
|
|
@@ -2664,56 +2973,97 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2664
2973
|
return;
|
|
2665
2974
|
}
|
|
2666
2975
|
if (Date.now() - this.lastInboundAt > this.options.heartbeat.timeoutMs) {
|
|
2667
|
-
this.closeReasonValue = "HeartbeatTimeout";
|
|
2668
2976
|
const error = { code: "disconnected" /* Disconnected */, message: "Heartbeat timed out." };
|
|
2669
2977
|
await this.disconnectForTransportFailure(
|
|
2670
2978
|
error,
|
|
2671
2979
|
this.currentConnection,
|
|
2672
|
-
this.connectionGeneration
|
|
2980
|
+
this.connectionGeneration,
|
|
2981
|
+
"HeartbeatTimeout"
|
|
2673
2982
|
);
|
|
2674
2983
|
return;
|
|
2675
2984
|
}
|
|
2676
|
-
|
|
2677
|
-
const generation = this.connectionGeneration;
|
|
2678
|
-
try {
|
|
2679
|
-
await this.frameSender.sendControl(this.connectionForSend(), ZLINK_STREAM_HEARTBEAT_PING);
|
|
2680
|
-
} catch (cause) {
|
|
2681
|
-
const error = toStreamError(cause, "sendFailed" /* SendFailed */, "Heartbeat send failed.");
|
|
2682
|
-
await this.disconnectForTransportFailure(error, connection, generation);
|
|
2683
|
-
}
|
|
2985
|
+
await this.frameSender.sendControl(this.connectionForSend(), ZLINK_STREAM_HEARTBEAT_PING);
|
|
2684
2986
|
}
|
|
2685
|
-
|
|
2686
|
-
|
|
2987
|
+
/**
|
|
2988
|
+
* @param reason Given only where the reason is not read from `error`: a
|
|
2989
|
+
* server `session-closing` and the heartbeat timeout. Every other ending
|
|
2990
|
+
* takes it from {@link closeReasonFor}.
|
|
2991
|
+
*/
|
|
2992
|
+
async disconnectForTransportFailure(error, origin, generation, reason = closeReasonFor(error)) {
|
|
2687
2993
|
if (this.closeRequested || this.currentState === "closed" /* Closed */) {
|
|
2688
2994
|
return;
|
|
2689
2995
|
}
|
|
2690
2996
|
if (origin !== void 0 && !this.isCurrentConnection(origin, generation)) {
|
|
2691
2997
|
return;
|
|
2692
2998
|
}
|
|
2999
|
+
this.closeReasonValue = reason;
|
|
2693
3000
|
if (this.disconnectTask !== void 0) {
|
|
2694
3001
|
return await this.disconnectTask;
|
|
2695
3002
|
}
|
|
2696
|
-
|
|
2697
|
-
|
|
3003
|
+
let resolve;
|
|
3004
|
+
let reject;
|
|
3005
|
+
const pending = new Promise((complete, fail) => {
|
|
3006
|
+
resolve = complete;
|
|
3007
|
+
reject = fail;
|
|
3008
|
+
});
|
|
3009
|
+
const task = pending.finally(() => {
|
|
3010
|
+
if (this.disconnectTask === task) this.disconnectTask = void 0;
|
|
2698
3011
|
});
|
|
2699
|
-
|
|
2700
|
-
|
|
3012
|
+
this.disconnectTask = task;
|
|
3013
|
+
void this.tearDownConnection(error).then(resolve, reject);
|
|
3014
|
+
await task;
|
|
3015
|
+
this.announceDisconnect();
|
|
2701
3016
|
}
|
|
2702
3017
|
isCurrentConnection(connection, generation) {
|
|
2703
3018
|
return !this.closeRequested && this.currentConnection === connection && this.connectionGeneration === generation;
|
|
2704
3019
|
}
|
|
2705
|
-
/**
|
|
2706
|
-
|
|
3020
|
+
/**
|
|
3021
|
+
* Ends the current connection, for close and for transport loss alike. No
|
|
3022
|
+
* application callback runs before terminal operations settle. Spec stream-connector 32 §7 and §9:
|
|
3023
|
+
* every operation the ending connection fails (the frames it has not
|
|
3024
|
+
* written, the one it is writing and the pending requests) fails with
|
|
3025
|
+
* `Disconnected`, whatever ended it; the cause stays in the close reason.
|
|
3026
|
+
* The transport is closed without waiting for the frames.
|
|
3027
|
+
*/
|
|
3028
|
+
async tearDownConnection(error, signal) {
|
|
2707
3029
|
this.stopHeartbeat();
|
|
2708
3030
|
this.stopReceiveLoop();
|
|
2709
3031
|
const connection = this.currentConnection;
|
|
2710
3032
|
this.currentConnection = void 0;
|
|
2711
|
-
|
|
3033
|
+
const disconnected = { code: "disconnected" /* Disconnected */, message: error.message };
|
|
3034
|
+
if (connection !== void 0) {
|
|
3035
|
+
this.frameSender.failUnwritten(
|
|
3036
|
+
connection,
|
|
3037
|
+
connectorError(disconnected.code, disconnected.message)
|
|
3038
|
+
);
|
|
3039
|
+
}
|
|
3040
|
+
this.pendingRequests.failAll(disconnected);
|
|
2712
3041
|
this.receivedMessages.connectionEnded();
|
|
2713
|
-
this.actors.closeAll();
|
|
3042
|
+
this.actors.closeAll(signal);
|
|
3043
|
+
await this.closeTransport(connection, error, signal);
|
|
3044
|
+
}
|
|
3045
|
+
async closeTransport(connection, error, signal) {
|
|
2714
3046
|
try {
|
|
2715
|
-
|
|
2716
|
-
|
|
3047
|
+
try {
|
|
3048
|
+
await connection?.close(signal);
|
|
3049
|
+
} finally {
|
|
3050
|
+
this.setState(
|
|
3051
|
+
this.closeRequested ? "closed" /* Closed */ : "disconnected" /* Disconnected */,
|
|
3052
|
+
this.closeRequested ? void 0 : error,
|
|
3053
|
+
signal
|
|
3054
|
+
);
|
|
3055
|
+
}
|
|
3056
|
+
} catch (cause) {
|
|
3057
|
+
this.events.publishError(
|
|
3058
|
+
{
|
|
3059
|
+
code: "disconnected" /* Disconnected */,
|
|
3060
|
+
message: `Transport close failed: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
3061
|
+
cause
|
|
3062
|
+
},
|
|
3063
|
+
signal
|
|
3064
|
+
);
|
|
3065
|
+
} finally {
|
|
3066
|
+
this.publishDisconnected(signal);
|
|
2717
3067
|
}
|
|
2718
3068
|
}
|
|
2719
3069
|
/**
|
|
@@ -2724,10 +3074,7 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2724
3074
|
* handler that is slow — or whose promise never settles at all — must not
|
|
2725
3075
|
* cost the connector the attempt.
|
|
2726
3076
|
*/
|
|
2727
|
-
|
|
2728
|
-
if (this.closeRequested) return;
|
|
2729
|
-
this.queueState("disconnected" /* Disconnected */, error);
|
|
2730
|
-
this.queueDisconnected();
|
|
3077
|
+
announceDisconnect() {
|
|
2731
3078
|
if (this.shouldReconnect()) {
|
|
2732
3079
|
queueMicrotask(() => {
|
|
2733
3080
|
void this.connect().catch(() => void 0);
|
|
@@ -2737,110 +3084,127 @@ var ZlinkStreamConnectorLifecycle = class {
|
|
|
2737
3084
|
shouldReconnect() {
|
|
2738
3085
|
return this.options.reconnect.enabled && !this.closeRequested;
|
|
2739
3086
|
}
|
|
2740
|
-
// Synchronous test-and-set, taken before any await, so the single disconnect
|
|
2741
|
-
// notification the spec promises is claimed by exactly one caller even when
|
|
2742
|
-
// the publishing itself is deferred.
|
|
2743
|
-
claimDisconnectedPublish() {
|
|
2744
|
-
if (this.disconnectedPublished) return false;
|
|
2745
|
-
this.disconnectedPublished = true;
|
|
2746
|
-
return true;
|
|
2747
|
-
}
|
|
2748
|
-
async publishDisconnectedOnce(signal) {
|
|
2749
|
-
if (!this.claimDisconnectedPublish()) return;
|
|
2750
|
-
await this.events.publishDisconnected(signal);
|
|
2751
|
-
}
|
|
2752
3087
|
/**
|
|
2753
|
-
* Spec stream-connector 32 §7:
|
|
2754
|
-
*
|
|
2755
|
-
*
|
|
2756
|
-
*
|
|
2757
|
-
*
|
|
2758
|
-
* Starting the publish without awaiting it still runs every handler right
|
|
2759
|
-
* here: an `async` function body runs synchronously up to its first `await`,
|
|
2760
|
-
* and `publishDisconnected` reaches each handler before that point. So the
|
|
2761
|
-
* handler has run by the time `close` returns, which is what the spec asks
|
|
2762
|
-
* for, while a handler that calls `close` no longer waits for the very
|
|
2763
|
-
* `closeTask` it is running inside. Java and C++ hand the handler to a queue
|
|
2764
|
-
* and return the same way.
|
|
2765
|
-
*
|
|
2766
|
-
* `claimDisconnectedPublish` is taken by the same call and before any await,
|
|
2767
|
-
* so deferring the completion never turns the one notification into two.
|
|
2768
|
-
*
|
|
2769
|
-
* Nothing awaits the promise, so a rejection would reach the process as an
|
|
2770
|
-
* unhandled rejection and kill it. Handler failures are already contained
|
|
2771
|
-
* where they were before this call stopped waiting — the `Promise.allSettled`
|
|
2772
|
-
* in `publishDisconnected` — and this `catch` covers what is left rather than
|
|
2773
|
-
* reporting the same failure a second time on the error surface.
|
|
3088
|
+
* Spec stream-connector 32 §6 and §7: one disconnect notification per
|
|
3089
|
+
* lifecycle event, handed to the dispatch queue. The connector does not wait
|
|
3090
|
+
* for the handler; `Manual` runs it when the application pumps dispatch.
|
|
3091
|
+
* The flag is tested and set synchronously, so concurrent callers for the
|
|
3092
|
+
* same event claim it once.
|
|
2774
3093
|
*/
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
if (!this.claimDisconnectedPublish()) return;
|
|
2780
|
-
this.receivedMessages.enqueueCallback(() => {
|
|
2781
|
-
void this.events.publishDisconnected(signal).catch(() => void 0);
|
|
2782
|
-
});
|
|
2783
|
-
}
|
|
2784
|
-
queueState(current, error, signal) {
|
|
2785
|
-
const change = this.transitionState(current, error);
|
|
2786
|
-
if (change === void 0) return;
|
|
2787
|
-
this.receivedMessages.enqueueCallback(() => {
|
|
2788
|
-
void this.publishStateChange(change, signal).catch(() => void 0);
|
|
2789
|
-
});
|
|
2790
|
-
}
|
|
2791
|
-
async setState(current, error, signal) {
|
|
2792
|
-
const change = this.transitionState(current, error);
|
|
2793
|
-
if (change === void 0) return;
|
|
2794
|
-
await this.publishStateChange(change, signal);
|
|
3094
|
+
publishDisconnected(signal) {
|
|
3095
|
+
if (this.disconnectedPublished) return;
|
|
3096
|
+
this.disconnectedPublished = true;
|
|
3097
|
+
this.events.publishDisconnected(signal);
|
|
2795
3098
|
}
|
|
2796
|
-
|
|
3099
|
+
setState(current, error, signal) {
|
|
2797
3100
|
const previous = this.currentState;
|
|
2798
3101
|
this.currentState = current;
|
|
2799
|
-
if (previous === current && error === void 0)
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
return { previous, current, error };
|
|
2803
|
-
}
|
|
2804
|
-
async publishStateChange(change, signal) {
|
|
2805
|
-
await this.events.publishStateChanged(change, signal);
|
|
2806
|
-
if (change.error !== void 0) await this.events.publishError(change.error, signal);
|
|
3102
|
+
if (previous === current && error === void 0) return;
|
|
3103
|
+
this.events.publishStateChanged({ previous, current, error }, signal);
|
|
3104
|
+
if (error !== void 0) this.events.publishError(error, signal);
|
|
2807
3105
|
}
|
|
2808
3106
|
};
|
|
2809
3107
|
|
|
2810
3108
|
// packages/stream-connector/src/Runtime/ZlinkStreamConnectorEvents.ts
|
|
2811
3109
|
var ZlinkStreamConnectorEvents = class {
|
|
3110
|
+
/**
|
|
3111
|
+
* @param enqueueCallback Queues a callback that calls the number of handlers
|
|
3112
|
+
* `callbacks` reports for the handlers registered at that moment.
|
|
3113
|
+
*/
|
|
3114
|
+
constructor(enqueueCallback) {
|
|
3115
|
+
this.enqueueCallback = enqueueCallback;
|
|
3116
|
+
}
|
|
2812
3117
|
errorHandlers = /* @__PURE__ */ new Set();
|
|
2813
3118
|
disconnectedHandlers = /* @__PURE__ */ new Set();
|
|
2814
3119
|
stateHandlers = /* @__PURE__ */ new Set();
|
|
2815
3120
|
onError(handler) {
|
|
2816
|
-
this.errorHandlers
|
|
2817
|
-
return subscription(() => this.errorHandlers.delete(handler));
|
|
3121
|
+
return registerHandler(this.errorHandlers, handler);
|
|
2818
3122
|
}
|
|
2819
3123
|
onDisconnected(handler) {
|
|
2820
|
-
this.disconnectedHandlers
|
|
2821
|
-
return subscription(() => this.disconnectedHandlers.delete(handler));
|
|
3124
|
+
return registerHandler(this.disconnectedHandlers, handler);
|
|
2822
3125
|
}
|
|
2823
3126
|
onStateChanged(handler) {
|
|
2824
|
-
this.stateHandlers
|
|
2825
|
-
|
|
3127
|
+
return registerHandler(this.stateHandlers, handler);
|
|
3128
|
+
}
|
|
3129
|
+
publishError(error, signal) {
|
|
3130
|
+
this.publish(this.errorHandlers, (handler) => handler(error, signal), signal);
|
|
3131
|
+
}
|
|
3132
|
+
publishDisconnected(signal) {
|
|
3133
|
+
this.publish(this.disconnectedHandlers, (handler) => handler(signal), signal);
|
|
2826
3134
|
}
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
3135
|
+
publishStateChanged(change, signal) {
|
|
3136
|
+
this.publish(this.stateHandlers, (handler) => handler(change, signal), signal);
|
|
3137
|
+
}
|
|
3138
|
+
/**
|
|
3139
|
+
* Spec stream-connector 32 §7: the connector runs a registered callback and
|
|
3140
|
+
* does not wait for it to finish. A thrown or rejected failure reaches the
|
|
3141
|
+
* error handlers as `UserCallbackFailed`.
|
|
3142
|
+
*/
|
|
3143
|
+
runUserCallback(run, failureMessage, signal) {
|
|
3144
|
+
this.invoke(
|
|
3145
|
+
run,
|
|
3146
|
+
(cause) => this.publishError(
|
|
3147
|
+
{ code: "userCallbackFailed" /* UserCallbackFailed */, message: failureMessage, cause },
|
|
3148
|
+
signal
|
|
3149
|
+
)
|
|
2830
3150
|
);
|
|
2831
3151
|
}
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
3152
|
+
publish(handlers, invoke, signal) {
|
|
3153
|
+
this.enqueueCallback(
|
|
3154
|
+
() => {
|
|
3155
|
+
for (const handler of currentRegistrations(handlers)) {
|
|
3156
|
+
const report = (cause) => this.reportFailure(
|
|
3157
|
+
cause,
|
|
3158
|
+
signal,
|
|
3159
|
+
handlers === this.errorHandlers ? handler : void 0
|
|
3160
|
+
);
|
|
3161
|
+
this.invoke(() => invoke(handler.handler), report);
|
|
3162
|
+
}
|
|
3163
|
+
},
|
|
3164
|
+
() => handlers.size
|
|
2835
3165
|
);
|
|
2836
3166
|
}
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
3167
|
+
reportFailure(cause, signal, failedHandler) {
|
|
3168
|
+
const error = {
|
|
3169
|
+
code: "userCallbackFailed" /* UserCallbackFailed */,
|
|
3170
|
+
message: "Connector event handler failed.",
|
|
3171
|
+
cause
|
|
3172
|
+
};
|
|
3173
|
+
if (failedHandler === void 0) {
|
|
3174
|
+
this.publishError(error, signal);
|
|
3175
|
+
return;
|
|
3176
|
+
}
|
|
3177
|
+
this.reportToRemaining(error, signal, /* @__PURE__ */ new Set([failedHandler]));
|
|
3178
|
+
}
|
|
3179
|
+
reportToRemaining(error, signal, attempted) {
|
|
3180
|
+
const remaining = Array.from(this.errorHandlers).filter(
|
|
3181
|
+
(candidate) => !attempted.has(candidate)
|
|
3182
|
+
);
|
|
3183
|
+
if (remaining.length === 0) return;
|
|
3184
|
+
const nextAttempted = new Set(attempted);
|
|
3185
|
+
for (const handler of remaining) nextAttempted.add(handler);
|
|
3186
|
+
this.enqueueCallback(
|
|
3187
|
+
() => {
|
|
3188
|
+
for (const handler of currentRegistrations(this.errorHandlers, remaining)) {
|
|
3189
|
+
const report = (cause) => this.reportToRemaining({ ...error, cause }, signal, nextAttempted);
|
|
3190
|
+
const callback = handler.handler;
|
|
3191
|
+
this.invoke(() => callback(error, signal), report);
|
|
3192
|
+
}
|
|
3193
|
+
},
|
|
3194
|
+
() => {
|
|
3195
|
+
let count = 0;
|
|
3196
|
+
const registrations = currentRegistrations(this.errorHandlers, remaining);
|
|
3197
|
+
while (registrations.next().done !== true) count += 1;
|
|
3198
|
+
return count;
|
|
3199
|
+
}
|
|
2840
3200
|
);
|
|
2841
3201
|
}
|
|
2842
|
-
|
|
2843
|
-
|
|
3202
|
+
invoke(run, report) {
|
|
3203
|
+
try {
|
|
3204
|
+
Promise.resolve(run()).catch(report);
|
|
3205
|
+
} catch (cause) {
|
|
3206
|
+
report(cause);
|
|
3207
|
+
}
|
|
2844
3208
|
}
|
|
2845
3209
|
};
|
|
2846
3210
|
|
|
@@ -2878,11 +3242,7 @@ var BrowserWebSocketConnection = class {
|
|
|
2878
3242
|
if (this.closed || this.socket.readyState !== 1) {
|
|
2879
3243
|
throw connectorError("disconnected" /* Disconnected */, "Remote stream is not connected.");
|
|
2880
3244
|
}
|
|
2881
|
-
|
|
2882
|
-
this.socket.send(frame);
|
|
2883
|
-
} catch (cause) {
|
|
2884
|
-
throw connectorError("sendFailed" /* SendFailed */, "Send failed.", cause);
|
|
2885
|
-
}
|
|
3245
|
+
this.socket.send(frame);
|
|
2886
3246
|
}
|
|
2887
3247
|
async read(signal) {
|
|
2888
3248
|
throwIfAborted(signal);
|
|
@@ -2900,18 +3260,19 @@ var BrowserWebSocketConnection = class {
|
|
|
2900
3260
|
await this.waitForMessage(signal);
|
|
2901
3261
|
}
|
|
2902
3262
|
}
|
|
3263
|
+
/**
|
|
3264
|
+
* Spec stream-connector 32 §7: closing does not wait for the peer to read or
|
|
3265
|
+
* answer. `WebSocket.close` sends the close frame after the data already
|
|
3266
|
+
* handed to `send`, so a frame whose write completed is not torn.
|
|
3267
|
+
*/
|
|
2903
3268
|
async close(signal) {
|
|
2904
3269
|
throwIfAborted(signal);
|
|
3270
|
+
this.removeListeners();
|
|
2905
3271
|
if (!this.closed) {
|
|
2906
3272
|
this.closed = true;
|
|
2907
3273
|
this.socket.close();
|
|
2908
|
-
this.wakeReader();
|
|
2909
|
-
}
|
|
2910
|
-
try {
|
|
2911
|
-
await waitForClose(this.socket, signal);
|
|
2912
|
-
} finally {
|
|
2913
|
-
this.removeListeners();
|
|
2914
3274
|
}
|
|
3275
|
+
this.wakeReader();
|
|
2915
3276
|
}
|
|
2916
3277
|
onMessage = (event) => {
|
|
2917
3278
|
try {
|
|
@@ -2982,28 +3343,13 @@ var BrowserWebSocketConnection = class {
|
|
|
2982
3343
|
const message = this.messages[this.messageHead];
|
|
2983
3344
|
this.messages[this.messageHead] = void 0;
|
|
2984
3345
|
this.messageHead += 1;
|
|
2985
|
-
if (this.messageHead
|
|
3346
|
+
if (shouldCompactBackingArray(this.messageHead, this.messages.length)) {
|
|
2986
3347
|
this.messages.splice(0, this.messageHead);
|
|
2987
3348
|
this.messageHead = 0;
|
|
2988
3349
|
}
|
|
2989
3350
|
return message;
|
|
2990
3351
|
}
|
|
2991
3352
|
};
|
|
2992
|
-
function waitForClose(socket, signal) {
|
|
2993
|
-
if (socket.readyState === 3) return Promise.resolve();
|
|
2994
|
-
return new Promise((resolve, reject) => {
|
|
2995
|
-
const onClose = () => finish();
|
|
2996
|
-
const onAbort = () => finish(connectorError("disconnected" /* Disconnected */, "Close canceled."));
|
|
2997
|
-
const finish = (error) => {
|
|
2998
|
-
socket.removeEventListener("close", onClose);
|
|
2999
|
-
signal?.removeEventListener("abort", onAbort);
|
|
3000
|
-
if (error === void 0) resolve();
|
|
3001
|
-
else reject(error);
|
|
3002
|
-
};
|
|
3003
|
-
socket.addEventListener("close", onClose, { once: true });
|
|
3004
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3005
|
-
});
|
|
3006
|
-
}
|
|
3007
3353
|
async function waitForOpen(socket, connectTimeoutMs, signal) {
|
|
3008
3354
|
throwIfAborted(signal);
|
|
3009
3355
|
await new Promise((resolve, reject) => {
|
|
@@ -3012,8 +3358,8 @@ async function waitForOpen(socket, connectTimeoutMs, signal) {
|
|
|
3012
3358
|
connectTimeoutMs
|
|
3013
3359
|
);
|
|
3014
3360
|
const onOpen = () => finish();
|
|
3015
|
-
const onClose = () => finish(connectorError("
|
|
3016
|
-
const onError = () => finish(connectorError("
|
|
3361
|
+
const onClose = () => finish(connectorError("disconnected" /* Disconnected */, "Connect closed before opening."));
|
|
3362
|
+
const onError = () => finish(connectorError("disconnected" /* Disconnected */, "Connect failed."));
|
|
3017
3363
|
const onAbort = () => finish(connectorError("disconnected" /* Disconnected */, "Connect canceled."));
|
|
3018
3364
|
const finish = (error) => {
|
|
3019
3365
|
clearTimeout(timeout);
|
|
@@ -3053,7 +3399,9 @@ var DefaultZlinkStreamConnector = class {
|
|
|
3053
3399
|
static heartbeatPongName = ZLINK_STREAM_HEARTBEAT_PONG;
|
|
3054
3400
|
receivedMessages;
|
|
3055
3401
|
lifecycle;
|
|
3056
|
-
events = new ZlinkStreamConnectorEvents(
|
|
3402
|
+
events = new ZlinkStreamConnectorEvents(
|
|
3403
|
+
(callback, callbacks) => this.receivedMessages.enqueueCallback(callback, callbacks)
|
|
3404
|
+
);
|
|
3057
3405
|
correlationCounter = 0n;
|
|
3058
3406
|
pendingRequests = new ZlinkStreamPendingRequests();
|
|
3059
3407
|
frameSender;
|
|
@@ -3100,7 +3448,7 @@ var DefaultZlinkStreamConnector = class {
|
|
|
3100
3448
|
return this.lifecycle.state;
|
|
3101
3449
|
}
|
|
3102
3450
|
get pendingDispatchCount() {
|
|
3103
|
-
return this.
|
|
3451
|
+
return this.receivedMessages.pendingCallbacks;
|
|
3104
3452
|
}
|
|
3105
3453
|
get actors() {
|
|
3106
3454
|
return this.boundActors.snapshot;
|
|
@@ -3135,12 +3483,10 @@ var DefaultZlinkStreamConnector = class {
|
|
|
3135
3483
|
return this.events.onStateChanged(handler);
|
|
3136
3484
|
}
|
|
3137
3485
|
onRequestSending(handler) {
|
|
3138
|
-
this.requestSendingHandlers
|
|
3139
|
-
return subscription(() => this.requestSendingHandlers.delete(handler));
|
|
3486
|
+
return registerHandler(this.requestSendingHandlers, handler);
|
|
3140
3487
|
}
|
|
3141
3488
|
onReplyReceived(handler) {
|
|
3142
|
-
this.replyReceivedHandlers
|
|
3143
|
-
return subscription(() => this.replyReceivedHandlers.delete(handler));
|
|
3489
|
+
return registerHandler(this.replyReceivedHandlers, handler);
|
|
3144
3490
|
}
|
|
3145
3491
|
async connect(signal) {
|
|
3146
3492
|
await this.lifecycle.connect(signal);
|
|
@@ -3157,7 +3503,12 @@ var DefaultZlinkStreamConnector = class {
|
|
|
3157
3503
|
}
|
|
3158
3504
|
request(payload, messageType) {
|
|
3159
3505
|
const encoded = this.encodePayload(payload, messageType);
|
|
3160
|
-
return new ZlinkStreamRequestBuilder(
|
|
3506
|
+
return new ZlinkStreamRequestBuilder(
|
|
3507
|
+
this,
|
|
3508
|
+
this.resolveNameOrDefault(encoded),
|
|
3509
|
+
encoded,
|
|
3510
|
+
(callback) => this.receivedMessages.enqueueCallback(callback)
|
|
3511
|
+
);
|
|
3161
3512
|
}
|
|
3162
3513
|
on(nameOrType, handler, messageType) {
|
|
3163
3514
|
const encodedHandler = (message, signal) => handler(
|
|
@@ -3190,29 +3541,25 @@ var DefaultZlinkStreamConnector = class {
|
|
|
3190
3541
|
this,
|
|
3191
3542
|
this.resolveNameOrDefault(encoded),
|
|
3192
3543
|
encoded,
|
|
3544
|
+
(callback) => this.receivedMessages.enqueueCallback(callback),
|
|
3193
3545
|
actor.slot,
|
|
3194
3546
|
() => actor.ensureBound()
|
|
3195
3547
|
);
|
|
3196
3548
|
}
|
|
3197
3549
|
onActorMessage(actor, nameOrType, handler, messageType) {
|
|
3198
|
-
const encodedHandler = (message, signal) =>
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
},
|
|
3212
|
-
signal
|
|
3213
|
-
);
|
|
3214
|
-
};
|
|
3215
|
-
return this.receivedMessages.on(this.observedName(nameOrType), encodedHandler);
|
|
3550
|
+
const encodedHandler = (message, signal) => handler(
|
|
3551
|
+
{
|
|
3552
|
+
name: message.name,
|
|
3553
|
+
metadata: message.metadata,
|
|
3554
|
+
payload: this.decodePayload(
|
|
3555
|
+
message.payload,
|
|
3556
|
+
messageType ?? (typeof nameOrType === "function" ? nameOrType : void 0)
|
|
3557
|
+
),
|
|
3558
|
+
actorId: message.actorId
|
|
3559
|
+
},
|
|
3560
|
+
signal
|
|
3561
|
+
);
|
|
3562
|
+
return this.receivedMessages.on(this.observedName(nameOrType), encodedHandler, actor);
|
|
3216
3563
|
}
|
|
3217
3564
|
waitFor(nameOrType) {
|
|
3218
3565
|
return new ZlinkStreamWaitBuilder(this, this.observedName(nameOrType));
|
|
@@ -3334,7 +3681,7 @@ var DefaultZlinkStreamConnector = class {
|
|
|
3334
3681
|
}
|
|
3335
3682
|
return payload;
|
|
3336
3683
|
}
|
|
3337
|
-
async sendEncoded(kind, name, payload, metadata, compress, requestSeq, signal, correlationId, actorSlot) {
|
|
3684
|
+
async sendEncoded(kind, name, payload, metadata, compress, requestSeq, signal, correlationId, actorSlot, expiry, onAccepted) {
|
|
3338
3685
|
await this.frameSender.send(
|
|
3339
3686
|
this.lifecycle.connectionForSend(),
|
|
3340
3687
|
kind,
|
|
@@ -3345,7 +3692,9 @@ var DefaultZlinkStreamConnector = class {
|
|
|
3345
3692
|
requestSeq,
|
|
3346
3693
|
signal,
|
|
3347
3694
|
correlationId,
|
|
3348
|
-
actorSlot
|
|
3695
|
+
actorSlot,
|
|
3696
|
+
expiry,
|
|
3697
|
+
onAccepted
|
|
3349
3698
|
);
|
|
3350
3699
|
}
|
|
3351
3700
|
/**
|
|
@@ -3368,37 +3717,48 @@ var DefaultZlinkStreamConnector = class {
|
|
|
3368
3717
|
}
|
|
3369
3718
|
};
|
|
3370
3719
|
let pending;
|
|
3720
|
+
let stopCancellation;
|
|
3371
3721
|
try {
|
|
3372
3722
|
throwIfAborted(signal);
|
|
3373
|
-
for (const handler of
|
|
3723
|
+
for (const { handler } of currentRegistrations(this.requestSendingHandlers)) {
|
|
3374
3724
|
try {
|
|
3375
3725
|
handler(sendingContext);
|
|
3376
3726
|
} catch (cause) {
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
);
|
|
3386
|
-
});
|
|
3727
|
+
this.events.publishError(
|
|
3728
|
+
{
|
|
3729
|
+
code: "userCallbackFailed" /* UserCallbackFailed */,
|
|
3730
|
+
message: "Request sending hook failed.",
|
|
3731
|
+
cause
|
|
3732
|
+
},
|
|
3733
|
+
signal
|
|
3734
|
+
);
|
|
3387
3735
|
}
|
|
3388
3736
|
}
|
|
3389
3737
|
pending = this.pendingRequests.create(name, timeoutMs);
|
|
3390
|
-
|
|
3738
|
+
const accepted = pending;
|
|
3739
|
+
const write = this.sendEncoded(
|
|
3391
3740
|
2 /* Request */,
|
|
3392
3741
|
name,
|
|
3393
3742
|
payload,
|
|
3394
3743
|
requestMetadata,
|
|
3395
3744
|
compress,
|
|
3396
|
-
|
|
3745
|
+
accepted.requestSeq,
|
|
3397
3746
|
signal,
|
|
3398
3747
|
this.nextCorrelationId(),
|
|
3399
|
-
actorSlot
|
|
3748
|
+
actorSlot,
|
|
3749
|
+
accepted.promise,
|
|
3750
|
+
accepted.startTimeout
|
|
3400
3751
|
);
|
|
3401
|
-
const
|
|
3752
|
+
const canceled = new Promise((_, reject) => {
|
|
3753
|
+
const onAbort = () => reject(connectorError("disconnected" /* Disconnected */, "Operation canceled."));
|
|
3754
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3755
|
+
stopCancellation = () => signal?.removeEventListener("abort", onAbort);
|
|
3756
|
+
});
|
|
3757
|
+
const reply = await Promise.race([
|
|
3758
|
+
write.then(() => accepted.promise),
|
|
3759
|
+
accepted.promise,
|
|
3760
|
+
canceled
|
|
3761
|
+
]);
|
|
3402
3762
|
this.publishReplyReceived(
|
|
3403
3763
|
{
|
|
3404
3764
|
requestPacketName: name,
|
|
@@ -3412,6 +3772,7 @@ var DefaultZlinkStreamConnector = class {
|
|
|
3412
3772
|
return reply.payload;
|
|
3413
3773
|
} catch (error) {
|
|
3414
3774
|
if (pending !== void 0) this.pendingRequests.cancel(pending.requestSeq);
|
|
3775
|
+
if (signal?.aborted === true) throw error;
|
|
3415
3776
|
this.publishReplyReceived(
|
|
3416
3777
|
{
|
|
3417
3778
|
requestPacketName: name,
|
|
@@ -3423,26 +3784,24 @@ var DefaultZlinkStreamConnector = class {
|
|
|
3423
3784
|
signal
|
|
3424
3785
|
);
|
|
3425
3786
|
throw error;
|
|
3787
|
+
} finally {
|
|
3788
|
+
stopCancellation?.();
|
|
3426
3789
|
}
|
|
3427
3790
|
}
|
|
3428
3791
|
publishReplyReceived(context, signal) {
|
|
3429
3792
|
if (this.replyReceivedHandlers.size === 0) return;
|
|
3430
|
-
this.receivedMessages.enqueueCallback(
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
toStreamError(
|
|
3437
|
-
cause,
|
|
3438
|
-
"userCallbackFailed" /* UserCallbackFailed */,
|
|
3439
|
-
"Reply received hook failed."
|
|
3440
|
-
),
|
|
3793
|
+
this.receivedMessages.enqueueCallback(
|
|
3794
|
+
() => {
|
|
3795
|
+
for (const { handler } of currentRegistrations(this.replyReceivedHandlers)) {
|
|
3796
|
+
this.events.runUserCallback(
|
|
3797
|
+
() => handler(context, signal),
|
|
3798
|
+
"Reply received hook failed.",
|
|
3441
3799
|
signal
|
|
3442
3800
|
);
|
|
3443
3801
|
}
|
|
3444
|
-
}
|
|
3445
|
-
|
|
3802
|
+
},
|
|
3803
|
+
() => this.replyReceivedHandlers.size
|
|
3804
|
+
);
|
|
3446
3805
|
}
|
|
3447
3806
|
resolveNameOrDefault(payload) {
|
|
3448
3807
|
if (payload.messageType === void 0) {
|