@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.
@@ -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 lz4UnpicklePayload(payload, maxSize) {
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
- const resultLength = data.length + resultDiff;
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 < 2) {
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 += 2;
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++) + 4;
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 !== 15) {
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 !== 255) {
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 > 65535) {
284
+ if (header.length > UINT16_MAX) {
131
285
  throw new Error("Stream header is too large.");
132
286
  }
133
- if (payload.length > 4294967295) {
287
+ if (payload.length > UINT32_MAX) {
134
288
  throw new Error("Stream payload is too large.");
135
289
  }
136
- const frame = new Uint8Array(6 + header.length + payload.length);
290
+ const frame = new Uint8Array(ZLINK_STREAM_FRAME_PREFIX_BYTES + header.length + payload.length);
137
291
  writeUInt16BE(frame, 0, header.length);
138
- writeUInt32BE(frame, 2, payload.length);
139
- frame.set(header, 6);
140
- frame.set(payload, 6 + header.length);
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 < 6) {
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, 2);
149
- if (frame.length !== 6 + headerLength + payloadLength) {
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(6, 6 + headerLength),
154
- payload: frame.slice(6 + headerLength)
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
- if (!reply) validateStreamWirePacketName(packetName);
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 > 255) {
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 > 65535)) {
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 ? 8 : 0) + 1 + nameBytes.length + (hasMetadata ? 2 + metadataBytes.length : 0) + (hasCorrelation ? 1 + correlationBytes.length : 0) + (hasFlow ? 37 : 0) + (hasActorSlot ? 2 : 0);
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 += 8;
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 += 2;
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 += 36;
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 < 5) {
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 & ~63) !== 0) {
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 < 8) {
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 += 8;
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 (!isReplyKind(kind) && nameLength === 0 || header.length - offset < nameLength) {
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 decodedName = utf8Decode(header.subarray(offset, offset + nameLength));
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 < 37) {
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 + 36));
444
+ flowId = asciiDecode(header.subarray(offset, offset + FLOW_ID_BYTES));
290
445
  validateFlowId(flowId);
291
446
  }
292
- offset += 36;
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 < 2) {
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 += 2;
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 encodeStreamWireMetadata(metadata) {
339
- if (metadata.size > 255) {
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 encoded = [...metadata].map(([key, value]) => {
344
- const keyBytes = utf8Encode(key);
345
- const valueBytes = utf8Encode(value);
346
- if (keyBytes.length === 0 || keyBytes.length > 255) {
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 (valueBytes.length > 65535) {
504
+ if (valueSize > UINT16_MAX) {
350
505
  throw new Error("Metadata value is too large.");
351
506
  }
352
- size += 1 + keyBytes.length + 2 + valueBytes.length;
353
- return { keyBytes, valueBytes };
354
- });
355
- const buffer = new Uint8Array(size);
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 { keyBytes, valueBytes } of encoded) {
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 += 2;
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 < 2) {
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 += 2;
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 += 2;
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 (name.trim().length === 0 || nameBytes.length > 255) {
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 === ZLINK_STREAM_RESPONSE_KIND || kind === ZLINK_STREAM_ERROR_KIND;
597
+ return kind === 3 /* Response */ || kind === 4 /* Error */;
442
598
  }
443
599
  function writeUInt16BE(buffer, offset, value) {
444
- buffer[offset] = value >>> 8 & 255;
445
- buffer[offset + 1] = value & 255;
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 & 255;
455
- buffer[offset + 1] = value >>> 16 & 255;
456
- buffer[offset + 2] = value >>> 8 & 255;
457
- buffer[offset + 3] = value & 255;
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 Error("Metadata key must not be empty.");
565
- }
566
- for (let index = 0; index < key.length; index++) {
567
- const code = key.charCodeAt(index);
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
- return {
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 (error) {
700
- failure = error;
701
- }
702
- if (failure === void 0) {
703
- throw connectorError("validationFailed" /* ValidationFailed */, "Expected action to fail.");
704
- }
705
- const streamError = unwrapStreamError(failure);
706
- if (errorKind !== void 0 && streamError.code !== errorKind) {
707
- throw connectorError(
708
- "validationFailed" /* ValidationFailed */,
709
- `Expected failure kind '${errorKind}', got '${streamError.code}'.`,
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
- return streamError;
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 (error) {
720
- failure = error;
721
- }
722
- if (failure === void 0) {
723
- throw connectorError("validationFailed" /* ValidationFailed */, "Expected action to time out.");
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
- if (name.length === 0) {
735
- throw connectorError("validationFailed" /* ValidationFailed */, "Message name must not be empty.");
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("$zlink.")) {
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) => signalOrCallback({ isSuccess: false, error: unwrapStreamError(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
- return codec.compress(payload);
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
- const decompressed = codec.decompress(payload, maxDecompressedSize);
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/Runtime/Protocol/ZlinkStreamFrameCodec.ts
1104
- var ZlinkStreamFrameCodec = class {
1105
- static encode(header, payload, maxPayloadSize = 64 * 1024) {
1106
- validatePayload(payload.length, maxPayloadSize);
1107
- try {
1108
- return encodeStreamWireFrame(header, payload);
1109
- } catch (cause) {
1110
- throw connectorError("frameTooLarge" /* FrameTooLarge */, "Frame is too large.", cause);
1111
- }
1112
- }
1113
- static decode(frame) {
1114
- try {
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
- "frameDecodeFailed" /* FrameDecodeFailed */,
1119
- "Frame length does not match prefix.",
1120
- cause
1279
+ "configurationError" /* ConfigurationError */,
1280
+ "The TypeScript Stream Connector supports only ws:// and wss:// endpoints."
1121
1281
  );
1122
- }
1123
1282
  }
1124
- };
1125
- function splitZlinkStreamFrames(chunk) {
1126
- if (chunk.length === 0) {
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
- "frameDecodeFailed" /* FrameDecodeFailed */,
1129
- "Stream frame prefix is incomplete."
1301
+ "configurationError" /* ConfigurationError */,
1302
+ "Configured transport conflicts with endpoint scheme."
1130
1303
  );
1131
1304
  }
1132
- const frames = [];
1133
- let offset = 0;
1134
- while (offset < chunk.length) {
1135
- const remaining = chunk.length - offset;
1136
- if (remaining < 6) {
1137
- throw connectorError(
1138
- "frameDecodeFailed" /* FrameDecodeFailed */,
1139
- "Stream frame prefix is incomplete."
1140
- );
1141
- }
1142
- const headerLength = chunk[offset] << 8 | chunk[offset + 1];
1143
- const payloadLength = chunk[offset + 2] * 16777216 + (chunk[offset + 3] << 16) + (chunk[offset + 4] << 8) + chunk[offset + 5];
1144
- const frameLength = 6 + headerLength + payloadLength;
1145
- if (frameLength > remaining) {
1146
- throw connectorError(
1147
- "frameDecodeFailed" /* FrameDecodeFailed */,
1148
- "Frame length does not match prefix."
1149
- );
1150
- }
1151
- frames.push(chunk.subarray(offset, offset + frameLength));
1152
- offset += frameLength;
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
- return frames;
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
- "Payload exceeds MaxSendPayloadSize."
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
- // packages/stream-connector/src/Runtime/Protocol/ZlinkStreamMetadataCodec.ts
1166
- var ZLINK_STREAM_MAX_METADATA_BYTES = 1024;
1167
- var ZlinkStreamMetadataCodec = class {
1168
- static size(metadata) {
1169
- try {
1170
- const size = metadata.count === 0 ? 0 : encodeStreamWireMetadata(metadata.values).length;
1171
- if (size > ZLINK_STREAM_MAX_METADATA_BYTES) {
1172
- throw connectorError(
1173
- "validationFailed" /* ValidationFailed */,
1174
- `Metadata must not exceed ${ZLINK_STREAM_MAX_METADATA_BYTES} bytes.`
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
- "validationFailed" /* ValidationFailed */,
1181
- streamWireErrorMessage(cause),
1182
- cause
1386
+ "configurationError" /* ConfigurationError */,
1387
+ "compressionCodec cannot be set when compression is none."
1183
1388
  );
1184
1389
  }
1185
1390
  }
1186
- static write(metadata, destination) {
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
- destination.set(encodeStreamWireMetadata(metadata.values));
1433
+ return decodeStreamWireFrame(frame);
1189
1434
  } catch (cause) {
1190
1435
  throw connectorError(
1191
- "validationFailed" /* ValidationFailed */,
1192
- streamWireErrorMessage(cause),
1436
+ "frameDecodeFailed" /* FrameDecodeFailed */,
1437
+ "Frame length does not match prefix.",
1193
1438
  cause
1194
1439
  );
1195
1440
  }
1196
1441
  }
1197
- static decode(metadata) {
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
- const values = decodeStreamWireMetadata(metadata);
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
- "frameDecodeFailed" /* FrameDecodeFailed */,
1204
- streamWireErrorMessage(cause),
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
- streamWireErrorMessage2(cause),
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
- streamWireErrorMessage2(cause),
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 streamWireErrorMessage2(cause) {
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, header.flags);
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, flags) {
1337
- if (![1, 2, 3, 4, 5].includes(kind)) {
1607
+ function validateEnum(kind, codec) {
1608
+ if (!isStreamWireMessageKind(kind)) {
1338
1609
  throw connectorError("frameDecodeFailed" /* FrameDecodeFailed */, "Unknown stream message kind.");
1339
1610
  }
1340
- if (![0, 1, 2, 3].includes(codec)) {
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 = "$zlink.heartbeat.ping";
1351
- var ZLINK_STREAM_HEARTBEAT_PONG = "$zlink.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
- this.active.set(requestSeq, {
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
- return { requestSeq, promise };
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: value });
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/ZlinkStreamReceivedMessages.ts
1641
- var ZlinkStreamReceivedMessages = class {
1642
- /**
1643
- * @param deliverOnArrival `Immediate` runs registered handlers on the receive
1644
- * path; `Manual` leaves them queued until {@link pump} runs them on the
1645
- * caller's thread (spec stream-connector 32 §7). Wait surfaces observe the
1646
- * queue in both modes, so they never depend on this flag.
1647
- */
1648
- constructor(events, deliverOnArrival) {
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
- handlers = /* @__PURE__ */ new Map();
1653
- observers = /* @__PURE__ */ new Map();
1654
- // A handler can be registered after messages for another name arrive, so the
1655
- // queue is not a simple FIFO. Tombstones let us remove a deliverable entry
1656
- // without shifting every later message on the hot receive path.
1657
- queue = [];
1658
- queueHead = 0;
1659
- queuedCount = 0;
1660
- drainTask;
1661
- // True for as long as `drain` is on the stack, handler awaits included. It
1662
- // marks the execution context a registered handler runs in, so a `dispatch`
1663
- // made from inside a handler is recognised as re-entry rather than a fresh
1664
- // pump. It is not a lock: a single event loop admits no second thread, and
1665
- // nothing ever waits for this flag to fall.
1666
- draining = false;
1667
- // Spec stream-connector 32 §10: arrivals per packet name on the current
1668
- // connection. It is raised where a packet arrives, never where one is taken,
1669
- // so consuming does not lower it and the dispatch mode does not change it.
1670
- receivedCounts = /* @__PURE__ */ new Map();
1671
- on(name, handler) {
1672
- validateName(name);
1673
- let set = this.handlers.get(name);
1674
- if (set === void 0) {
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
- set.add(handler);
1679
- if (this.deliverOnArrival && this.hasQueuedMessage(name)) {
1680
- queueMicrotask(() => this.scheduleDrain());
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(handler);
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.scheduleDrain();
2092
+ this.pump();
1785
2093
  }
1786
2094
  }
1787
- enqueueCallback(callback) {
1788
- this.queue.push({ kind: "callback", callback });
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.scheduleDrain();
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` has already drained on arrival.
2110
+ * this from `dispatch`; `Immediate` drains on arrival.
1797
2111
  *
1798
- * A handler that calls `dispatch` arrives back here from inside the drain it
1799
- * was started by. `scheduleDrain` would find `drainTask` already set and
1800
- * return, and the await below would then be the drain waiting on itself —
1801
- * a deadlock with neither timeout nor error. The drain loop already takes
1802
- * every message a handler exists for, so there is nothing a second drain
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
- async pump() {
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
- await queued.callback();
2129
+ this.events.runUserCallback(queued.callback, "Connector callback failed.");
1845
2130
  continue;
1846
2131
  }
1847
2132
  const { message, signal } = queued;
1848
- const handlers = Array.from(this.handlers.get(message.name));
1849
- for (const handler of handlers) {
1850
- try {
1851
- await handler(message, signal);
1852
- } catch (cause) {
1853
- await this.events.publishError(
1854
- {
1855
- code: "userCallbackFailed" /* UserCallbackFailed */,
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
- for (let index = this.queueHead; index < this.queue.length; index += 1) {
2183
+ while (this.deliverable.length > 0) {
2184
+ const index = this.deliverable[0];
1876
2185
  const queued = this.queue[index];
1877
- if (queued !== void 0 && (queued.kind === "callback" || (this.handlers.get(queued.message.name)?.size ?? 0) > 0)) {
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
- hasQueuedMessage(name) {
1884
- for (let index = this.queueHead; index < this.queue.length; index += 1) {
1885
- const queued = this.queue[index];
1886
- if (queued?.kind === "message" && queued.message.name === name) return true;
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
- return false;
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
- if (this.queueHead >= 1024 && this.queueHead * 2 >= this.queue.length) {
1902
- this.queue.splice(0, this.queueHead);
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
- pendingWrites = /* @__PURE__ */ new Set();
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
- async sendControl(connection, name, signal) {
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
- queue(handlers, actor, signal) {
2109
- this.receivedMessages.enqueueCallback(() => {
2110
- for (const handler of Array.from(handlers)) {
2111
- this.invoke(handler, actor, signal);
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
- invoke(handler, actor, signal) {
2116
- try {
2117
- Promise.resolve(handler(actor, signal)).catch((cause) => {
2118
- void this.events.publishError(
2119
- {
2120
- code: "userCallbackFailed" /* UserCallbackFailed */,
2121
- message: "Actor lifecycle handler failed.",
2122
- cause
2123
- },
2124
- signal
2125
- );
2126
- });
2127
- } catch (cause) {
2128
- void this.events.publishError(
2129
- {
2130
- code: "userCallbackFailed" /* UserCallbackFailed */,
2131
- message: "Actor lifecycle handler failed.",
2132
- cause
2133
- },
2134
- signal
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
- function invalidControl(message) {
2140
- return connectorError("frameDecodeFailed" /* FrameDecodeFailed */, message);
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, connectionForSend) {
2468
+ async readAndDispatch(connection, signal, isCurrent) {
2168
2469
  if (connection?.read === void 0) {
2169
2470
  return { available: false, inbound: false };
2170
2471
  }
2171
- const frameBytes = await connection.read(signal);
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 cause;
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, connectionForSend);
2500
+ await this.dispatch(connection, frame.header, frame.payload, signal);
2190
2501
  } catch (cause) {
2191
- if (frame.header.kind === 5 /* Control */ && frame.header.name === ZLINK_STREAM_HEARTBEAT_PING) {
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 cause;
2509
+ throw new ZlinkStreamConnectionEnd(error);
2201
2510
  }
2202
- await this.events.publishError(error, signal);
2511
+ this.events.publishError(error, signal);
2203
2512
  }
2204
2513
  }
2205
2514
  return { available: true, inbound: true };
2206
2515
  }
2207
- async dispatch(connection, header, payload, signal, connectionForSend) {
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
- if (!this.pendingRequests.resolve(
2541
+ this.pendingRequests.resolve(
2212
2542
  header.requestSeq,
2213
- {
2543
+ () => ({
2214
2544
  codec: header.codec,
2215
- payload: this.protocol.decodePayload(header, payload)
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
- await this.events.publishError(decodeError, signal);
2557
+ this.events.publishError(decodeError, signal);
2235
2558
  }
2236
2559
  }
2237
2560
  return;
2238
2561
  }
2239
- if (header.kind === 4 /* Error */ && header.requestSeq !== void 0) {
2562
+ if (header.kind === 4 /* Error */) {
2563
+ let error;
2240
2564
  try {
2241
- const remoteError = decodeRemoteError(this.protocol, header, payload);
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
- const decodeError = toStreamError(
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
- return;
2256
- }
2257
- if (header.kind === 4 /* Error */) {
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, connectionForSend);
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.protocol.decodePayload(header, payload) },
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, connectionForSend) {
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 = decodeSessionClosing(payload);
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
- try {
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(protocol, header, payload) {
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(baseDelayMs * (0.5 + Math.random() * 0.5));
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
- await this.setState("connecting" /* Connecting */, void 0, signal);
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
- try {
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
- await this.setState("connected" /* Connected */, void 0, signal);
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
- const error2 = toStreamError(cause, "disconnected" /* Disconnected */, message);
2420
- throw new ZlinkStreamException(error2);
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 ??= "TransportError";
2424
- void this.setState("disconnected" /* Disconnected */, error, signal);
2425
- this.publishDisconnectedWithoutWaiting(signal);
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 = "ClientClose";
2431
- this.closeRequested = true;
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
- if (this.currentState === "closed" /* Closed */) {
2765
+ this.closeRequested = true;
2766
+ if (this.closeTask === void 0 && this.currentState === "closed" /* Closed */) {
2436
2767
  return;
2437
2768
  }
2438
- this.closeTask = this.closeOnce(signal).finally(() => {
2769
+ const closeTask = this.closeTask ??= this.closeOnce(signal).finally(() => {
2439
2770
  this.closeTask = void 0;
2440
2771
  });
2441
- return await this.closeTask;
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
- const connection = this.currentConnection;
2459
- this.stopHeartbeat();
2460
- this.stopReceiveLoop();
2461
- this.currentConnection = void 0;
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
- await this.receivedMessages.pump();
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 = this.options.reconnect.initialDelayMs;
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
- await this.setState("reconnecting" /* Reconnecting */, lastError, signal);
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
- const error = toStreamError(
2607
- cause,
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
- const connection = this.currentConnection;
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
- async disconnectForTransportFailure(error, origin, generation) {
2686
- this.closeReasonValue ??= "TransportError";
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
- this.disconnectTask = this.tearDownConnection(error).finally(() => {
2697
- this.disconnectTask = void 0;
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
- await this.disconnectTask;
2700
- await this.announceDisconnect(error);
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
- /** Transport teardown only — no application callback runs from here. */
2706
- async tearDownConnection(error) {
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
- this.pendingRequests.failAll(error);
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
- await connection?.close();
2716
- } catch {
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
- async announceDisconnect(error) {
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: the connector runs the registered handler, it
2754
- * does not wait for it to finish. `close` returns once its own work is done —
2755
- * the drain, the transport close, the pending requests — having run the
2756
- * disconnect handler but without looking at whether that handler has ended.
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
- publishDisconnectedWithoutWaiting(signal) {
2776
- void this.publishDisconnectedOnce(signal).catch(() => void 0);
2777
- }
2778
- queueDisconnected(signal) {
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
- transitionState(current, error) {
3099
+ setState(current, error, signal) {
2797
3100
  const previous = this.currentState;
2798
3101
  this.currentState = current;
2799
- if (previous === current && error === void 0) {
2800
- return void 0;
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.add(handler);
2817
- return subscription(() => this.errorHandlers.delete(handler));
3121
+ return registerHandler(this.errorHandlers, handler);
2818
3122
  }
2819
3123
  onDisconnected(handler) {
2820
- this.disconnectedHandlers.add(handler);
2821
- return subscription(() => this.disconnectedHandlers.delete(handler));
3124
+ return registerHandler(this.disconnectedHandlers, handler);
2822
3125
  }
2823
3126
  onStateChanged(handler) {
2824
- this.stateHandlers.add(handler);
2825
- return subscription(() => this.stateHandlers.delete(handler));
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
- async publishError(error, signal) {
2828
- await this.publish(
2829
- Array.from(this.errorHandlers).map((handler) => () => handler(error, signal))
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
- async publishDisconnected(signal) {
2833
- await this.publish(
2834
- Array.from(this.disconnectedHandlers).map((handler) => () => handler(signal))
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
- async publishStateChanged(change, signal) {
2838
- await this.publish(
2839
- Array.from(this.stateHandlers).map((handler) => () => handler(change, signal))
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
- async publish(handlers) {
2843
- await Promise.allSettled(handlers.map(async (handler) => handler()));
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
- try {
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 >= 1024 && this.messageHead * 2 >= this.messages.length) {
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("connectTimeout" /* ConnectTimeout */, "Connect closed before opening."));
3016
- const onError = () => finish(connectorError("connectTimeout" /* ConnectTimeout */, "Connect failed."));
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.pendingRequests.count;
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.add(handler);
3139
- return subscription(() => this.requestSendingHandlers.delete(handler));
3486
+ return registerHandler(this.requestSendingHandlers, handler);
3140
3487
  }
3141
3488
  onReplyReceived(handler) {
3142
- this.replyReceivedHandlers.add(handler);
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(this, this.resolveNameOrDefault(encoded), encoded);
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
- if (message[zlinkStreamActorBinding] !== actor) {
3200
- return;
3201
- }
3202
- return handler(
3203
- {
3204
- name: message.name,
3205
- metadata: message.metadata,
3206
- payload: this.decodePayload(
3207
- message.payload,
3208
- messageType ?? (typeof nameOrType === "function" ? nameOrType : void 0)
3209
- ),
3210
- actorId: message.actorId
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 Array.from(this.requestSendingHandlers)) {
3723
+ for (const { handler } of currentRegistrations(this.requestSendingHandlers)) {
3374
3724
  try {
3375
3725
  handler(sendingContext);
3376
3726
  } catch (cause) {
3377
- queueMicrotask(() => {
3378
- void this.events.publishError(
3379
- toStreamError(
3380
- cause,
3381
- "userCallbackFailed" /* UserCallbackFailed */,
3382
- "Request sending hook failed."
3383
- ),
3384
- signal
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
- await this.sendEncoded(
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
- pending.requestSeq,
3745
+ accepted.requestSeq,
3397
3746
  signal,
3398
3747
  this.nextCorrelationId(),
3399
- actorSlot
3748
+ actorSlot,
3749
+ accepted.promise,
3750
+ accepted.startTimeout
3400
3751
  );
3401
- const reply = await pending.promise;
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(async () => {
3431
- for (const handler of Array.from(this.replyReceivedHandlers)) {
3432
- try {
3433
- await handler(context, signal);
3434
- } catch (cause) {
3435
- await this.events.publishError(
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) {