mqtt-plus 1.4.25 → 1.4.26

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.
Files changed (67) hide show
  1. package/.ase/config.yaml +1 -0
  2. package/.ase/service.log +34 -0
  3. package/.ase/service.yaml +1 -1
  4. package/.ase/task/TASK-P18.md +1 -1
  5. package/.ase/task/TASK-P21.md +1 -1
  6. package/.claude/settings.local.json +4 -1
  7. package/AGENTS.md +1 -1
  8. package/CHANGELOG.md +24 -5
  9. package/README.md +2 -2
  10. package/doc/mqtt-plus-broker-setup.md +1 -1
  11. package/doc/mqtt-plus-comm.md +3 -3
  12. package/doc/mqtt-plus-internals.md +6 -0
  13. package/dst-stage1/mqtt-plus-base.d.ts +2 -2
  14. package/dst-stage1/mqtt-plus-base.js +13 -10
  15. package/dst-stage1/mqtt-plus-error.js +54 -28
  16. package/dst-stage1/mqtt-plus-event.js +3 -1
  17. package/dst-stage1/mqtt-plus-options.js +7 -0
  18. package/dst-stage1/mqtt-plus-service.js +9 -6
  19. package/dst-stage1/mqtt-plus-sink.js +27 -54
  20. package/dst-stage1/mqtt-plus-source.js +13 -9
  21. package/dst-stage1/mqtt-plus-util.d.ts +1 -0
  22. package/dst-stage1/mqtt-plus-util.js +25 -4
  23. package/dst-stage1/mqtt-plus.d.ts +1 -1
  24. package/dst-stage1/mqtt-plus.js +2 -0
  25. package/dst-stage2/mqtt-plus.cjs.cjs +164 -123
  26. package/dst-stage2/mqtt-plus.esm.js +154 -122
  27. package/dst-stage2/mqtt-plus.umd.js +11 -11
  28. package/etc/stx.conf +3 -2
  29. package/package.d/@typescript-eslint+typescript-estree+8.66.0.patch +12 -0
  30. package/package.d/{vite+8.1.4.patch → vite+8.2.1.patch} +2 -2
  31. package/package.json +16 -15
  32. package/src/mqtt-plus-base.ts +15 -12
  33. package/src/mqtt-plus-error.ts +65 -33
  34. package/src/mqtt-plus-event.ts +2 -1
  35. package/src/mqtt-plus-options.ts +8 -0
  36. package/src/mqtt-plus-service.ts +9 -6
  37. package/src/mqtt-plus-sink.ts +28 -54
  38. package/src/mqtt-plus-source.ts +13 -9
  39. package/src/mqtt-plus-util.ts +27 -4
  40. package/src/mqtt-plus.ts +3 -1
  41. package/tst/mqtt-plus-0-fixture.ts +1 -0
  42. package/tst/mqtt-plus-2-event.spec.ts +25 -0
  43. package/tst/mqtt-plus-4-sink.spec.ts +23 -0
  44. package/tst/mqtt-plus-5-source.spec.ts +26 -0
  45. package/tst/mqtt-plus-6-misc.spec.ts +58 -0
  46. package/tst/mqtt-plus-8-run.spec.ts +60 -0
  47. package/dst-stage1/mqtt-plus-api.js.map +0 -1
  48. package/dst-stage1/mqtt-plus-auth.js.map +0 -1
  49. package/dst-stage1/mqtt-plus-base.js.map +0 -1
  50. package/dst-stage1/mqtt-plus-codec.js.map +0 -1
  51. package/dst-stage1/mqtt-plus-encode.js.map +0 -1
  52. package/dst-stage1/mqtt-plus-error.js.map +0 -1
  53. package/dst-stage1/mqtt-plus-event.js.map +0 -1
  54. package/dst-stage1/mqtt-plus-info.js.map +0 -1
  55. package/dst-stage1/mqtt-plus-meta.js.map +0 -1
  56. package/dst-stage1/mqtt-plus-msg.js.map +0 -1
  57. package/dst-stage1/mqtt-plus-options.js.map +0 -1
  58. package/dst-stage1/mqtt-plus-service.js.map +0 -1
  59. package/dst-stage1/mqtt-plus-sink.js.map +0 -1
  60. package/dst-stage1/mqtt-plus-source.js.map +0 -1
  61. package/dst-stage1/mqtt-plus-subscription.js.map +0 -1
  62. package/dst-stage1/mqtt-plus-timer.js.map +0 -1
  63. package/dst-stage1/mqtt-plus-trace.js.map +0 -1
  64. package/dst-stage1/mqtt-plus-util.js.map +0 -1
  65. package/dst-stage1/mqtt-plus-version.js.map +0 -1
  66. package/dst-stage1/mqtt-plus.js.map +0 -1
  67. package/package.d/@typescript-eslint+typescript-estree+8.63.0.patch +0 -12
@@ -119,6 +119,7 @@ var ReadableTee = class extends Readable {
119
119
  }
120
120
  };
121
121
  async function sendBufferAsChunks(buffer, chunkSize, sendChunk, creditGate, abortSignal) {
122
+ if (!(Number.isFinite(chunkSize) && chunkSize > 0)) throw new Error("invalid chunk size");
122
123
  if (buffer.byteLength === 0) await sendChunk(void 0, void 0, true);
123
124
  else for (let i = 0; i < buffer.byteLength; i += chunkSize) {
124
125
  if (abortSignal?.aborted) throw abortSignal.reason ?? /* @__PURE__ */ new Error("aborted");
@@ -130,6 +131,7 @@ async function sendBufferAsChunks(buffer, chunkSize, sendChunk, creditGate, abor
130
131
  }
131
132
  }
132
133
  async function sendStreamAsChunks(readable, chunkSize, sendChunk, creditGate, abortSignal) {
134
+ if (!(Number.isFinite(chunkSize) && chunkSize > 0)) throw new Error("invalid chunk size");
133
135
  let pending;
134
136
  for await (const raw of readable) {
135
137
  if (abortSignal?.aborted) throw abortSignal.reason ?? /* @__PURE__ */ new Error("aborted");
@@ -152,11 +154,17 @@ async function sendStreamAsChunks(readable, chunkSize, sendChunk, creditGate, ab
152
154
  await sendChunk(pending, void 0, true);
153
155
  } else await sendChunk(void 0, void 0, true);
154
156
  }
157
+ function alignHandlerParams(params, handler) {
158
+ if (typeof handler !== "function") return params;
159
+ const arity = handler.length - 1;
160
+ if (params.length >= arity) return params;
161
+ return [...params, ...Array(arity - params.length).fill(void 0)];
162
+ }
155
163
  function makeMutuallyExclusiveFields(obj, f1Name, f2Name, onConsumed) {
156
164
  if (!(typeof obj === "object" && obj !== null)) throw new Error("invalid object");
157
165
  let consumed;
158
- const f1Value = obj[f1Name];
159
- const f2Value = obj[f2Name];
166
+ let f1Value = obj[f1Name];
167
+ let f2Value = obj[f2Name];
160
168
  Object.defineProperty(obj, f1Name, {
161
169
  get: () => {
162
170
  if (consumed === "f2") throw new Error(`field "${f1Name}" is mutually exclusive with field "${f2Name}" and "${f2Name}" was already consumed`);
@@ -164,7 +172,10 @@ function makeMutuallyExclusiveFields(obj, f1Name, f2Name, onConsumed) {
164
172
  consumed = "f1";
165
173
  return f1Value;
166
174
  },
167
- enumerable: true,
175
+ set: (value) => {
176
+ f1Value = value;
177
+ },
178
+ enumerable: false,
168
179
  configurable: true
169
180
  });
170
181
  Object.defineProperty(obj, f2Name, {
@@ -174,7 +185,10 @@ function makeMutuallyExclusiveFields(obj, f1Name, f2Name, onConsumed) {
174
185
  consumed = "f2";
175
186
  return f2Value;
176
187
  },
177
- enumerable: true,
188
+ set: (value) => {
189
+ f2Value = value;
190
+ },
191
+ enumerable: false,
178
192
  configurable: true
179
193
  });
180
194
  }
@@ -258,8 +272,10 @@ function ensureError(error, prefix, debug = false) {
258
272
  } else return new Error(msg);
259
273
  }
260
274
  function runFinally(isAsync, onfinally, description) {
261
- if (!onfinally) if (isAsync) return Promise.resolve();
262
- else return;
275
+ if (!onfinally) {
276
+ if (isAsync) return Promise.resolve();
277
+ else return;
278
+ }
263
279
  let result;
264
280
  try {
265
281
  result = onfinally();
@@ -275,8 +291,10 @@ function runFinally(isAsync, onfinally, description) {
275
291
  return result;
276
292
  }
277
293
  function runUnroll(isAsync, spool) {
278
- if (!spool) if (isAsync) return Promise.resolve();
279
- else return;
294
+ if (!spool) {
295
+ if (isAsync) return Promise.resolve();
296
+ else return;
297
+ }
280
298
  let result = spool.unroll();
281
299
  if (!isAsync && result instanceof Promise) throw new Error("spool unroll returned Promise in non-async context");
282
300
  if (isAsync && !(result instanceof Promise)) result = Promise.resolve(result);
@@ -290,9 +308,12 @@ function runSettle(isAsync, pending, spool, onfinally, description) {
290
308
  } catch (arg) {
291
309
  failure = ensureError(arg, description);
292
310
  }
293
- if (pending || failure !== void 0) {
311
+ if (pending !== void 0 || failure !== void 0) {
294
312
  runUnroll(false, spool);
295
- if (!pending && failure !== void 0) throw failure;
313
+ if (failure !== void 0) {
314
+ if (pending !== void 0) pending.suppressed = failure;
315
+ else throw failure;
316
+ }
296
317
  }
297
318
  } else return (async () => {
298
319
  let failure;
@@ -301,12 +322,34 @@ function runSettle(isAsync, pending, spool, onfinally, description) {
301
322
  } catch (arg) {
302
323
  failure = ensureError(arg, description);
303
324
  }
304
- if (pending || failure !== void 0) {
325
+ if (pending !== void 0 || failure !== void 0) {
305
326
  await runUnroll(true, spool);
306
- if (!pending && failure !== void 0) throw failure;
327
+ if (failure !== void 0) {
328
+ if (pending !== void 0) pending.suppressed = failure;
329
+ else throw failure;
330
+ }
307
331
  }
308
332
  })();
309
333
  }
334
+ function runFail(error, spool, onfinally, description) {
335
+ const attach = (arg) => {
336
+ error.suppressed = ensureError(arg, description);
337
+ };
338
+ let result = void 0;
339
+ try {
340
+ result = onfinally?.();
341
+ } catch (arg) {
342
+ attach(arg);
343
+ }
344
+ if (result instanceof Promise) return result.catch(attach).then(() => spool?.unroll()).then(() => {
345
+ throw error;
346
+ });
347
+ const unrolled = spool?.unroll();
348
+ if (unrolled instanceof Promise) return unrolled.then(() => {
349
+ throw error;
350
+ });
351
+ throw error;
352
+ }
310
353
  function run(...args) {
311
354
  let description;
312
355
  let spool;
@@ -352,25 +395,18 @@ function run(...args) {
352
395
  try {
353
396
  result = action();
354
397
  } catch (arg) {
355
- let error = ensureError(arg, description);
356
- if (oncatch) {
357
- try {
358
- result = oncatch(error);
359
- } catch (arg) {
360
- error = ensureError(arg, description);
361
- runSettle(false, true, spool, onfinally, description);
362
- throw error;
363
- }
364
- if (spool && oncleanup) spool.roll(result, oncleanup);
365
- runSettle(false, false, spool, onfinally, description);
366
- return result;
398
+ const error = ensureError(arg, description);
399
+ if (!oncatch) return runFail(error, spool, onfinally, description);
400
+ try {
401
+ result = oncatch(error);
402
+ } catch (arg) {
403
+ return runFail(ensureError(arg, description), spool, onfinally, description);
367
404
  }
368
- runSettle(false, true, spool, onfinally, description);
369
- throw error;
405
+ oncatch = void 0;
370
406
  }
371
407
  if (result instanceof Promise) return result.then(async (result) => {
372
408
  if (spool && oncleanup) spool.roll(result, oncleanup);
373
- await runSettle(true, false, spool, onfinally, description);
409
+ await runSettle(true, void 0, spool, onfinally, description);
374
410
  return result;
375
411
  }, async (arg) => {
376
412
  let error = ensureError(arg, description);
@@ -380,19 +416,19 @@ function run(...args) {
380
416
  result = await oncatch(error);
381
417
  } catch (arg) {
382
418
  error = ensureError(arg, description);
383
- await runSettle(true, true, spool, onfinally, description);
419
+ await runSettle(true, error, spool, onfinally, description);
384
420
  throw error;
385
421
  }
386
422
  if (spool && oncleanup) spool.roll(result, oncleanup);
387
- await runSettle(true, false, spool, onfinally, description);
423
+ await runSettle(true, void 0, spool, onfinally, description);
388
424
  return result;
389
425
  }
390
- await runSettle(true, true, spool, onfinally, description);
426
+ await runSettle(true, error, spool, onfinally, description);
391
427
  throw error;
392
428
  });
393
429
  else {
394
430
  if (spool && oncleanup) spool.roll(result, oncleanup);
395
- runSettle(false, false, spool, onfinally, description);
431
+ runSettle(false, void 0, spool, onfinally, description);
396
432
  return result;
397
433
  }
398
434
  }
@@ -403,9 +439,9 @@ var OptionsTrait = class {
403
439
  this.options = {
404
440
  id: "",
405
441
  codec: "cbor",
406
- timeout: 10 * 1e3,
442
+ timeout: 1e4,
407
443
  share: "",
408
- chunkSize: 16 * 1024,
444
+ chunkSize: 16384,
409
445
  chunkCredit: 4,
410
446
  topicMake: (name, operation, peerId) => {
411
447
  return `${name}/${operation}/${peerId ?? "any"}`;
@@ -420,6 +456,9 @@ var OptionsTrait = class {
420
456
  },
421
457
  ...options
422
458
  };
459
+ if (!(Number.isFinite(this.options.timeout) && this.options.timeout > 0)) throw new Error("options: \"timeout\" must be a positive number");
460
+ if (!(Number.isInteger(this.options.chunkSize) && this.options.chunkSize > 0)) throw new Error("options: \"chunkSize\" must be a positive integer");
461
+ if (!(Number.isInteger(this.options.chunkCredit) && this.options.chunkCredit >= 0)) throw new Error("options: \"chunkCredit\" must be a non-negative integer");
423
462
  }
424
463
  };
425
464
  //#endregion
@@ -537,6 +576,8 @@ var versionToNum = (str) => {
537
576
  if (minor > 99) throw new Error("invalid version string: minor version exceeds 99");
538
577
  return parseInt(m[1], 10) * 100 + minor;
539
578
  };
579
+ var VERSION = "1.4";
580
+ var MIN_VERSION = "1.4";
540
581
  var version = versionToNum("1.4");
541
582
  var minVersion = versionToNum("1.4");
542
583
  //#endregion
@@ -932,13 +973,13 @@ var BaseTrait = class extends TraceTrait {
932
973
  };
933
974
  } });
934
975
  }
935
- this.mqtt = mqtt;
976
+ this._mqtt = mqtt;
936
977
  if (this.options.id === "") {
937
978
  const clientId = mqtt.isFakeProxy ? void 0 : mqtt.options?.clientId;
938
979
  this.options.id = typeof clientId === "string" && clientId !== "" ? clientId : nanoid();
939
980
  }
940
981
  this.log("info", "hooking into MQTT client");
941
- this.messageHandler = (topic, message, packet) => {
982
+ this._messageHandler = (topic, message, packet) => {
942
983
  if (this.destroyed) return;
943
984
  let input;
944
985
  if (this.options.codec === "json") input = message.toString();
@@ -949,12 +990,12 @@ var BaseTrait = class extends TraceTrait {
949
990
  }
950
991
  this._onMessage(topic, input, packet);
951
992
  };
952
- this.mqtt.on("message", this.messageHandler);
993
+ this._mqtt.on("message", this._messageHandler);
953
994
  }
954
995
  async destroy() {
955
996
  this.destroyed = true;
956
997
  this.log("info", "un-hooking from MQTT client");
957
- this.mqtt.off("message", this.messageHandler);
998
+ this._mqtt.off("message", this._messageHandler);
958
999
  this.onRequest.clear();
959
1000
  this.onResponse.clear();
960
1001
  }
@@ -973,7 +1014,7 @@ var BaseTrait = class extends TraceTrait {
973
1014
  async subscribeTopic(topic, options = {}) {
974
1015
  this.log("info", `subscribing to MQTT topic "${topic}"`);
975
1016
  return new Promise((resolve, reject) => {
976
- this.mqtt.subscribe(topic, {
1017
+ this._mqtt.subscribe(topic, {
977
1018
  qos: 2,
978
1019
  ...options
979
1020
  }, (err, granted) => {
@@ -991,7 +1032,7 @@ var BaseTrait = class extends TraceTrait {
991
1032
  async unsubscribeTopic(topic) {
992
1033
  this.log("info", `unsubscribing from MQTT topic "${topic}"`);
993
1034
  return new Promise((resolve, reject) => {
994
- this.mqtt.unsubscribe(topic, (err, _packet) => {
1035
+ this._mqtt.unsubscribe(topic, (err, _packet) => {
995
1036
  if (err) {
996
1037
  this.error(err, `unsubscribing from MQTT topic "${topic}" failed`);
997
1038
  reject(err);
@@ -1015,7 +1056,7 @@ var BaseTrait = class extends TraceTrait {
1015
1056
  this.log("debug", `publishing to MQTT topic "${topic}"`, { message: messageOnDemand });
1016
1057
  return new Promise((resolve, reject) => {
1017
1058
  const messageData = typeof message === "string" ? message : Buffer.from(message.buffer, message.byteOffset, message.byteLength);
1018
- this.mqtt.publish(topic, messageData, options, (err) => {
1059
+ this._mqtt.publish(topic, messageData, options, (err) => {
1019
1060
  if (err) {
1020
1061
  this.error(err, `publishing to MQTT topic "${topic}" failed`);
1021
1062
  reject(err);
@@ -1050,7 +1091,10 @@ var BaseTrait = class extends TraceTrait {
1050
1091
  return;
1051
1092
  }
1052
1093
  this.log("debug", `received from MQTT topic "${topic}"`, { message });
1053
- if (topicMatch.peerId !== void 0 && message.receiver !== void 0 && message.receiver !== topicMatch.peerId) this.log("warning", `receiver mismatch on direct topic "${topic}" (expected "${topicMatch.peerId}", got "${message.receiver}")`);
1094
+ if (topicMatch.peerId !== void 0 && message.receiver !== void 0 && message.receiver !== topicMatch.peerId) {
1095
+ this.log("warning", `receiver mismatch -- dropped message from direct topic "${topic}" (expected "${topicMatch.peerId}", got "${message.receiver}")`);
1096
+ return;
1097
+ }
1054
1098
  if (this.msg.isRequest(message)) {
1055
1099
  const handler = this.onRequest.get(`${message.type}:${message.name}`);
1056
1100
  if (handler !== void 0) Promise.resolve().then(() => {
@@ -1073,7 +1117,7 @@ var BaseTrait = class extends TraceTrait {
1073
1117
  //#endregion
1074
1118
  //#region dst-stage1/mqtt-plus-subscription.js
1075
1119
  var RefCountedSubscription = class {
1076
- constructor(subscribeFn, unsubscribeFn, lingerMs = 30 * 1e3) {
1120
+ constructor(subscribeFn, unsubscribeFn, lingerMs = 3e4) {
1077
1121
  this.subscribeFn = subscribeFn;
1078
1122
  this.unsubscribeFn = unsubscribeFn;
1079
1123
  this.lingerMs = lingerMs;
@@ -1089,12 +1133,14 @@ var RefCountedSubscription = class {
1089
1133
  }
1090
1134
  decrementCount(topic) {
1091
1135
  const count = this.counts.get(topic);
1092
- if (count !== void 0) if (count <= 1) {
1093
- this.counts.delete(topic);
1094
- return 0;
1095
- } else {
1096
- this.counts.set(topic, count - 1);
1097
- return count - 1;
1136
+ if (count !== void 0) {
1137
+ if (count <= 1) {
1138
+ this.counts.delete(topic);
1139
+ return 0;
1140
+ } else {
1141
+ this.counts.set(topic, count - 1);
1142
+ return count - 1;
1143
+ }
1098
1144
  }
1099
1145
  }
1100
1146
  clearCount(topic) {
@@ -1133,21 +1179,23 @@ var RefCountedSubscription = class {
1133
1179
  }
1134
1180
  }
1135
1181
  async unsubscribe(topic) {
1136
- if (this.decrementCount(topic) === 0) if (this.lingerMs > 0 && !topic.startsWith("$share/")) {
1137
- const timer = setTimeout(() => {
1138
- this.lingers.delete(topic);
1182
+ if (this.decrementCount(topic) === 0) {
1183
+ if (this.lingerMs > 0 && !topic.startsWith("$share/")) {
1184
+ const timer = setTimeout(() => {
1185
+ this.lingers.delete(topic);
1186
+ const promise = this.unsubscribeFn(topic).catch(() => {}).finally(() => {
1187
+ this.unsubbing.delete(topic);
1188
+ });
1189
+ this.unsubbing.set(topic, promise);
1190
+ }, this.lingerMs);
1191
+ this.lingers.set(topic, timer);
1192
+ } else {
1139
1193
  const promise = this.unsubscribeFn(topic).catch(() => {}).finally(() => {
1140
1194
  this.unsubbing.delete(topic);
1141
1195
  });
1142
1196
  this.unsubbing.set(topic, promise);
1143
- }, this.lingerMs);
1144
- this.lingers.set(topic, timer);
1145
- } else {
1146
- const promise = this.unsubscribeFn(topic).catch(() => {}).finally(() => {
1147
- this.unsubbing.delete(topic);
1148
- });
1149
- this.unsubbing.set(topic, promise);
1150
- await promise;
1197
+ await promise;
1198
+ }
1151
1199
  }
1152
1200
  }
1153
1201
  async flush() {
@@ -1416,7 +1464,7 @@ var EventTrait = class extends AuthTrait {
1416
1464
  this.log("warning", `event name mismatch -- dropped request for "${name}" (topic: "${topicName}", payload: "${request.name}")`, { requestId });
1417
1465
  return;
1418
1466
  }
1419
- const params = request.params ?? [];
1467
+ const params = alignHandlerParams(request.params ?? [], callback);
1420
1468
  if (this.eventControllers.has(requestId)) {
1421
1469
  this.log("warning", `duplicate event request id -- dropped request for event "${name}"`, { requestId });
1422
1470
  return;
@@ -1540,7 +1588,7 @@ var ServiceTrait = class extends EventTrait {
1540
1588
  if (request.receiver && request.receiver !== this.options.id) return;
1541
1589
  const requestId = request.id;
1542
1590
  const senderId = request.sender;
1543
- const params = request.params ?? [];
1591
+ const params = alignHandlerParams(request.params ?? [], callback);
1544
1592
  if (senderId === void 0 || senderId === "") {
1545
1593
  this.error(/* @__PURE__ */ new Error("invalid request: missing sender"));
1546
1594
  return;
@@ -1644,7 +1692,9 @@ var ServiceTrait = class extends EventTrait {
1644
1692
  const settle = () => {
1645
1693
  if (settled) return false;
1646
1694
  settled = true;
1647
- spool.unroll();
1695
+ spool.unroll(false)?.catch((err) => {
1696
+ this.error(ensureError(err, `cleanup after call of service "${name}" failed`));
1697
+ });
1648
1698
  return true;
1649
1699
  };
1650
1700
  const promise = new Promise((resolve, reject) => {
@@ -1751,7 +1801,7 @@ var SourceTrait = class extends ServiceTrait {
1751
1801
  this.onRequest.set(`source-fetch-request:${name}`, async (request, topicName) => {
1752
1802
  if (request.receiver && request.receiver !== this.options.id) return;
1753
1803
  const requestId = request.id;
1754
- const params = request.params ?? [];
1804
+ const params = alignHandlerParams(request.params ?? [], callback);
1755
1805
  const sender = request.sender;
1756
1806
  const receiver = request.receiver;
1757
1807
  const reqSpool = new Spool();
@@ -1804,11 +1854,16 @@ var SourceTrait = class extends ServiceTrait {
1804
1854
  if (current.size === 0) this.sourceRequests.delete(name);
1805
1855
  }
1806
1856
  });
1807
- abortSignal.addEventListener("abort", () => {
1808
- if (info.stream instanceof Readable && !info.stream.destroyed) info.stream.destroy(ensureError(abortSignal.reason));
1809
- }, { once: true });
1857
+ const noopError = () => {};
1858
+ const destroyStream = () => {
1859
+ if (info.stream instanceof Readable && !info.stream.destroyed) {
1860
+ info.stream.on("error", noopError);
1861
+ info.stream.destroy(ensureError(abortSignal.reason));
1862
+ }
1863
+ };
1864
+ abortSignal.addEventListener("abort", destroyStream, { once: true });
1810
1865
  reqSpool.roll(() => {
1811
- if (abortSignal.aborted && info.stream instanceof Readable && !info.stream.destroyed) info.stream.destroy(ensureError(abortSignal.reason));
1866
+ if (abortSignal.aborted) destroyStream();
1812
1867
  });
1813
1868
  let abortReject;
1814
1869
  const abortPromise = new Promise((_resolve, reject) => {
@@ -1955,7 +2010,7 @@ var SourceTrait = class extends ServiceTrait {
1955
2010
  const sourceNameMismatchError = (kind, actualName) => /* @__PURE__ */ new Error(`${kind} name mismatch (expected "${name}", got "${actualName}")`);
1956
2011
  await this.subscribeTopicAndSpool(spool, responseTopic, { qos: options.qos ?? 2 });
1957
2012
  const chunkCredit = this.options.chunkCredit;
1958
- const maxBufferedBytes = chunkCredit > 0 ? chunkCredit * this.options.chunkSize : 16 * 1024;
2013
+ const maxBufferedBytes = chunkCredit > 0 ? chunkCredit * this.options.chunkSize : 16384;
1959
2014
  let chunksReceived = 0;
1960
2015
  let creditGranted = chunkCredit;
1961
2016
  let responderId = receiver;
@@ -2193,7 +2248,7 @@ var SinkTrait = class extends SourceTrait {
2193
2248
  this.onRequest.set(`sink-push-request:${name}`, async (request, topicName) => {
2194
2249
  if (request.receiver && request.receiver !== this.options.id) return;
2195
2250
  const requestId = request.id;
2196
- const params = request.params ?? [];
2251
+ const params = alignHandlerParams(request.params ?? [], callback);
2197
2252
  const sender = request.sender;
2198
2253
  const receiver = request.receiver;
2199
2254
  const reqSpool = new Spool();
@@ -2254,7 +2309,7 @@ var SinkTrait = class extends SourceTrait {
2254
2309
  chunksReceived: 0,
2255
2310
  creditGranted: chunkCredit
2256
2311
  } : void 0;
2257
- const maxBufferedBytes = chunkCredit > 0 ? chunkCredit * this.options.chunkSize : 16 * 1024;
2312
+ const maxBufferedBytes = chunkCredit > 0 ? chunkCredit * this.options.chunkSize : 16384;
2258
2313
  let streamEnded = false;
2259
2314
  const pushTimerId = `sink-push-recv:${requestId}`;
2260
2315
  const refreshPushTimeout = () => this.timerRefresh(pushTimerId, () => {
@@ -2484,8 +2539,12 @@ var SinkTrait = class extends SourceTrait {
2484
2539
  });
2485
2540
  if (data instanceof Readable) {
2486
2541
  const stream = data;
2542
+ const noopError = () => {};
2487
2543
  abortSignal.addEventListener("abort", () => {
2488
- if (!stream.destroyed) stream.destroy(ensureError(abortSignal.reason));
2544
+ if (!stream.destroyed) {
2545
+ stream.on("error", noopError);
2546
+ stream.destroy(ensureError(abortSignal.reason));
2547
+ }
2489
2548
  }, { once: true });
2490
2549
  }
2491
2550
  const pushTimerId = `sink-push-send:${requestId}`;
@@ -2519,18 +2578,21 @@ var SinkTrait = class extends SourceTrait {
2519
2578
  pushFinalizeReject = reject;
2520
2579
  });
2521
2580
  pushFinalize.catch(() => {});
2581
+ const failPush = (error) => {
2582
+ abortController.abort(error);
2583
+ if (!pushAcked) {
2584
+ pushInitialSettled = true;
2585
+ pushInitialReject(error);
2586
+ } else if (!pushFinalized) {
2587
+ pushFinalized = true;
2588
+ pushFinalizeReject(error);
2589
+ }
2590
+ };
2522
2591
  const lockResponder = (kind, sender) => {
2523
2592
  if (sender === void 0 || sender === "") {
2524
2593
  const error = /* @__PURE__ */ new Error(`received ${kind} without sender`);
2525
2594
  remoteErrorObject = error;
2526
- abortController.abort(error);
2527
- if (!pushAcked) {
2528
- pushInitialSettled = true;
2529
- pushInitialReject(error);
2530
- } else if (!pushFinalized) {
2531
- pushFinalized = true;
2532
- pushFinalizeReject(error);
2533
- }
2595
+ failPush(error);
2534
2596
  return false;
2535
2597
  }
2536
2598
  if (responderId === void 0) responderId = sender;
@@ -2548,37 +2610,25 @@ var SinkTrait = class extends SourceTrait {
2548
2610
  if (response.name !== name) {
2549
2611
  const error = /* @__PURE__ */ new Error(`sink response name mismatch (expected "${name}", got "${response.name}")`);
2550
2612
  remoteErrorObject = error;
2551
- abortController.abort(error);
2552
- if (!pushAcked) {
2553
- pushInitialSettled = true;
2554
- pushInitialReject(error);
2555
- } else if (!pushFinalized) {
2556
- pushFinalized = true;
2557
- pushFinalizeReject(error);
2558
- }
2613
+ failPush(error);
2559
2614
  return;
2560
2615
  }
2561
2616
  if (!lockResponder("sink response", response.sender)) return;
2562
2617
  if (response.error) {
2563
2618
  const error = new Error(response.error);
2564
2619
  remoteErrorObject = error;
2565
- abortController.abort(error);
2566
- if (!pushAcked) {
2567
- pushInitialSettled = true;
2568
- pushInitialReject(error);
2569
- } else if (!pushFinalized) {
2570
- pushFinalized = true;
2571
- pushFinalizeReject(error);
2572
- }
2620
+ failPush(error);
2573
2621
  } else if (!pushAcked) {
2574
2622
  initialCredit = response.credit;
2575
2623
  pushAcked = true;
2576
2624
  pushInitialSettled = true;
2577
2625
  pushInitialResolve();
2578
- } else if (!pushFinalized) if (pushDataComplete) {
2579
- pushFinalized = true;
2580
- pushFinalizeResolve();
2581
- } else pushTerminalReceived = true;
2626
+ } else if (!pushFinalized) {
2627
+ if (pushDataComplete) {
2628
+ pushFinalized = true;
2629
+ pushFinalizeResolve();
2630
+ } else pushTerminalReceived = true;
2631
+ }
2582
2632
  });
2583
2633
  spool.roll(() => {
2584
2634
  this.onResponse.delete(`sink-push-response:${requestId}`);
@@ -2587,28 +2637,14 @@ var SinkTrait = class extends SourceTrait {
2587
2637
  if (response.name !== name) {
2588
2638
  const error = /* @__PURE__ */ new Error(`sink credit name mismatch (expected "${name}", got "${response.name}")`);
2589
2639
  remoteErrorObject = error;
2590
- abortController.abort(error);
2591
- if (!pushAcked) {
2592
- pushInitialSettled = true;
2593
- pushInitialReject(error);
2594
- } else if (!pushFinalized) {
2595
- pushFinalized = true;
2596
- pushFinalizeReject(error);
2597
- }
2640
+ failPush(error);
2598
2641
  return;
2599
2642
  }
2600
2643
  if (!lockResponder("sink credit", response.sender)) return;
2601
2644
  if (response.credit === 0) {
2602
2645
  cancelledByReceiver = true;
2603
2646
  const error = /* @__PURE__ */ new Error(`push to sink "${name}" cancelled by receiver`);
2604
- abortController.abort(error);
2605
- if (!pushAcked) {
2606
- pushInitialSettled = true;
2607
- pushInitialReject(error);
2608
- } else if (!pushFinalized) {
2609
- pushFinalized = true;
2610
- pushFinalizeReject(error);
2611
- }
2647
+ failPush(error);
2612
2648
  return;
2613
2649
  }
2614
2650
  if (creditGate !== void 0) {
@@ -2617,11 +2653,7 @@ var SinkTrait = class extends SourceTrait {
2617
2653
  } else if (pushAcked && initialCredit === void 0) {
2618
2654
  const error = /* @__PURE__ */ new Error(`push to sink "${name}" received unsolicited credit (credit-flow disabled)`);
2619
2655
  remoteErrorObject = error;
2620
- abortController.abort(error);
2621
- if (!pushFinalized) {
2622
- pushFinalized = true;
2623
- pushFinalizeReject(error);
2624
- }
2656
+ failPush(error);
2625
2657
  } else pendingCredit += response.credit;
2626
2658
  });
2627
2659
  spool.roll(() => {
@@ -2708,7 +2740,7 @@ var SinkTrait = class extends SourceTrait {
2708
2740
  }
2709
2741
  } catch {}
2710
2742
  if (pushAcked && !remoteErrorObject) await new Promise((resolve) => {
2711
- setImmediate(resolve);
2743
+ setTimeout(resolve, 0);
2712
2744
  });
2713
2745
  if (remoteErrorObject) throw remoteErrorObject;
2714
2746
  throw err;
@@ -2721,4 +2753,4 @@ var SinkTrait = class extends SourceTrait {
2721
2753
  //#region dst-stage1/mqtt-plus.js
2722
2754
  var MQTTp = class extends SinkTrait {};
2723
2755
  //#endregion
2724
- export { MQTTp as default };
2756
+ export { MIN_VERSION, VERSION, MQTTp as default, minVersion, version, versionToNum };