mqtt-plus 1.4.25 → 1.4.27

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 (68) 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 +29 -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 +5 -1
  24. package/dst-stage1/mqtt-plus.js +10 -1
  25. package/dst-stage2/mqtt-plus.cjs.cjs +169 -123
  26. package/dst-stage2/mqtt-plus.esm.js +168 -122
  27. package/dst-stage2/mqtt-plus.umd.js +11 -11
  28. package/etc/stx.conf +3 -2
  29. package/etc/vite.mts +1 -1
  30. package/package.d/@typescript-eslint+typescript-estree+8.66.0.patch +12 -0
  31. package/package.d/{vite+8.1.4.patch → vite+8.2.1.patch} +2 -2
  32. package/package.json +16 -15
  33. package/src/mqtt-plus-base.ts +15 -12
  34. package/src/mqtt-plus-error.ts +65 -33
  35. package/src/mqtt-plus-event.ts +2 -1
  36. package/src/mqtt-plus-options.ts +8 -0
  37. package/src/mqtt-plus-service.ts +9 -6
  38. package/src/mqtt-plus-sink.ts +28 -54
  39. package/src/mqtt-plus-source.ts +13 -9
  40. package/src/mqtt-plus-util.ts +27 -4
  41. package/src/mqtt-plus.ts +14 -3
  42. package/tst/mqtt-plus-0-fixture.ts +1 -0
  43. package/tst/mqtt-plus-2-event.spec.ts +25 -0
  44. package/tst/mqtt-plus-4-sink.spec.ts +23 -0
  45. package/tst/mqtt-plus-5-source.spec.ts +26 -0
  46. package/tst/mqtt-plus-6-misc.spec.ts +58 -0
  47. package/tst/mqtt-plus-8-run.spec.ts +60 -0
  48. package/dst-stage1/mqtt-plus-api.js.map +0 -1
  49. package/dst-stage1/mqtt-plus-auth.js.map +0 -1
  50. package/dst-stage1/mqtt-plus-base.js.map +0 -1
  51. package/dst-stage1/mqtt-plus-codec.js.map +0 -1
  52. package/dst-stage1/mqtt-plus-encode.js.map +0 -1
  53. package/dst-stage1/mqtt-plus-error.js.map +0 -1
  54. package/dst-stage1/mqtt-plus-event.js.map +0 -1
  55. package/dst-stage1/mqtt-plus-info.js.map +0 -1
  56. package/dst-stage1/mqtt-plus-meta.js.map +0 -1
  57. package/dst-stage1/mqtt-plus-msg.js.map +0 -1
  58. package/dst-stage1/mqtt-plus-options.js.map +0 -1
  59. package/dst-stage1/mqtt-plus-service.js.map +0 -1
  60. package/dst-stage1/mqtt-plus-sink.js.map +0 -1
  61. package/dst-stage1/mqtt-plus-source.js.map +0 -1
  62. package/dst-stage1/mqtt-plus-subscription.js.map +0 -1
  63. package/dst-stage1/mqtt-plus-timer.js.map +0 -1
  64. package/dst-stage1/mqtt-plus-trace.js.map +0 -1
  65. package/dst-stage1/mqtt-plus-util.js.map +0 -1
  66. package/dst-stage1/mqtt-plus-version.js.map +0 -1
  67. package/dst-stage1/mqtt-plus.js.map +0 -1
  68. package/package.d/@typescript-eslint+typescript-estree+8.63.0.patch +0 -12
@@ -15,7 +15,7 @@ var __copyProps = (to, from, except, desc) => {
15
15
  }
16
16
  return to;
17
17
  };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
19
19
  value: mod,
20
20
  enumerable: true
21
21
  }) : target, mod));
@@ -146,6 +146,7 @@ var ReadableTee = class extends node_stream.Readable {
146
146
  }
147
147
  };
148
148
  async function sendBufferAsChunks(buffer, chunkSize, sendChunk, creditGate, abortSignal) {
149
+ if (!(Number.isFinite(chunkSize) && chunkSize > 0)) throw new Error("invalid chunk size");
149
150
  if (buffer.byteLength === 0) await sendChunk(void 0, void 0, true);
150
151
  else for (let i = 0; i < buffer.byteLength; i += chunkSize) {
151
152
  if (abortSignal?.aborted) throw abortSignal.reason ?? /* @__PURE__ */ new Error("aborted");
@@ -157,6 +158,7 @@ async function sendBufferAsChunks(buffer, chunkSize, sendChunk, creditGate, abor
157
158
  }
158
159
  }
159
160
  async function sendStreamAsChunks(readable, chunkSize, sendChunk, creditGate, abortSignal) {
161
+ if (!(Number.isFinite(chunkSize) && chunkSize > 0)) throw new Error("invalid chunk size");
160
162
  let pending;
161
163
  for await (const raw of readable) {
162
164
  if (abortSignal?.aborted) throw abortSignal.reason ?? /* @__PURE__ */ new Error("aborted");
@@ -179,11 +181,17 @@ async function sendStreamAsChunks(readable, chunkSize, sendChunk, creditGate, ab
179
181
  await sendChunk(pending, void 0, true);
180
182
  } else await sendChunk(void 0, void 0, true);
181
183
  }
184
+ function alignHandlerParams(params, handler) {
185
+ if (typeof handler !== "function") return params;
186
+ const arity = handler.length - 1;
187
+ if (params.length >= arity) return params;
188
+ return [...params, ...Array(arity - params.length).fill(void 0)];
189
+ }
182
190
  function makeMutuallyExclusiveFields(obj, f1Name, f2Name, onConsumed) {
183
191
  if (!(typeof obj === "object" && obj !== null)) throw new Error("invalid object");
184
192
  let consumed;
185
- const f1Value = obj[f1Name];
186
- const f2Value = obj[f2Name];
193
+ let f1Value = obj[f1Name];
194
+ let f2Value = obj[f2Name];
187
195
  Object.defineProperty(obj, f1Name, {
188
196
  get: () => {
189
197
  if (consumed === "f2") throw new Error(`field "${f1Name}" is mutually exclusive with field "${f2Name}" and "${f2Name}" was already consumed`);
@@ -191,7 +199,10 @@ function makeMutuallyExclusiveFields(obj, f1Name, f2Name, onConsumed) {
191
199
  consumed = "f1";
192
200
  return f1Value;
193
201
  },
194
- enumerable: true,
202
+ set: (value) => {
203
+ f1Value = value;
204
+ },
205
+ enumerable: false,
195
206
  configurable: true
196
207
  });
197
208
  Object.defineProperty(obj, f2Name, {
@@ -201,7 +212,10 @@ function makeMutuallyExclusiveFields(obj, f1Name, f2Name, onConsumed) {
201
212
  consumed = "f2";
202
213
  return f2Value;
203
214
  },
204
- enumerable: true,
215
+ set: (value) => {
216
+ f2Value = value;
217
+ },
218
+ enumerable: false,
205
219
  configurable: true
206
220
  });
207
221
  }
@@ -285,8 +299,10 @@ function ensureError(error, prefix, debug = false) {
285
299
  } else return new Error(msg);
286
300
  }
287
301
  function runFinally(isAsync, onfinally, description) {
288
- if (!onfinally) if (isAsync) return Promise.resolve();
289
- else return;
302
+ if (!onfinally) {
303
+ if (isAsync) return Promise.resolve();
304
+ else return;
305
+ }
290
306
  let result;
291
307
  try {
292
308
  result = onfinally();
@@ -302,8 +318,10 @@ function runFinally(isAsync, onfinally, description) {
302
318
  return result;
303
319
  }
304
320
  function runUnroll(isAsync, spool) {
305
- if (!spool) if (isAsync) return Promise.resolve();
306
- else return;
321
+ if (!spool) {
322
+ if (isAsync) return Promise.resolve();
323
+ else return;
324
+ }
307
325
  let result = spool.unroll();
308
326
  if (!isAsync && result instanceof Promise) throw new Error("spool unroll returned Promise in non-async context");
309
327
  if (isAsync && !(result instanceof Promise)) result = Promise.resolve(result);
@@ -317,9 +335,12 @@ function runSettle(isAsync, pending, spool, onfinally, description) {
317
335
  } catch (arg) {
318
336
  failure = ensureError(arg, description);
319
337
  }
320
- if (pending || failure !== void 0) {
338
+ if (pending !== void 0 || failure !== void 0) {
321
339
  runUnroll(false, spool);
322
- if (!pending && failure !== void 0) throw failure;
340
+ if (failure !== void 0) {
341
+ if (pending !== void 0) pending.suppressed = failure;
342
+ else throw failure;
343
+ }
323
344
  }
324
345
  } else return (async () => {
325
346
  let failure;
@@ -328,12 +349,34 @@ function runSettle(isAsync, pending, spool, onfinally, description) {
328
349
  } catch (arg) {
329
350
  failure = ensureError(arg, description);
330
351
  }
331
- if (pending || failure !== void 0) {
352
+ if (pending !== void 0 || failure !== void 0) {
332
353
  await runUnroll(true, spool);
333
- if (!pending && failure !== void 0) throw failure;
354
+ if (failure !== void 0) {
355
+ if (pending !== void 0) pending.suppressed = failure;
356
+ else throw failure;
357
+ }
334
358
  }
335
359
  })();
336
360
  }
361
+ function runFail(error, spool, onfinally, description) {
362
+ const attach = (arg) => {
363
+ error.suppressed = ensureError(arg, description);
364
+ };
365
+ let result = void 0;
366
+ try {
367
+ result = onfinally?.();
368
+ } catch (arg) {
369
+ attach(arg);
370
+ }
371
+ if (result instanceof Promise) return result.catch(attach).then(() => spool?.unroll()).then(() => {
372
+ throw error;
373
+ });
374
+ const unrolled = spool?.unroll();
375
+ if (unrolled instanceof Promise) return unrolled.then(() => {
376
+ throw error;
377
+ });
378
+ throw error;
379
+ }
337
380
  function run(...args) {
338
381
  let description;
339
382
  let spool;
@@ -379,25 +422,18 @@ function run(...args) {
379
422
  try {
380
423
  result = action();
381
424
  } catch (arg) {
382
- let error = ensureError(arg, description);
383
- if (oncatch) {
384
- try {
385
- result = oncatch(error);
386
- } catch (arg) {
387
- error = ensureError(arg, description);
388
- runSettle(false, true, spool, onfinally, description);
389
- throw error;
390
- }
391
- if (spool && oncleanup) spool.roll(result, oncleanup);
392
- runSettle(false, false, spool, onfinally, description);
393
- return result;
425
+ const error = ensureError(arg, description);
426
+ if (!oncatch) return runFail(error, spool, onfinally, description);
427
+ try {
428
+ result = oncatch(error);
429
+ } catch (arg) {
430
+ return runFail(ensureError(arg, description), spool, onfinally, description);
394
431
  }
395
- runSettle(false, true, spool, onfinally, description);
396
- throw error;
432
+ oncatch = void 0;
397
433
  }
398
434
  if (result instanceof Promise) return result.then(async (result) => {
399
435
  if (spool && oncleanup) spool.roll(result, oncleanup);
400
- await runSettle(true, false, spool, onfinally, description);
436
+ await runSettle(true, void 0, spool, onfinally, description);
401
437
  return result;
402
438
  }, async (arg) => {
403
439
  let error = ensureError(arg, description);
@@ -407,19 +443,19 @@ function run(...args) {
407
443
  result = await oncatch(error);
408
444
  } catch (arg) {
409
445
  error = ensureError(arg, description);
410
- await runSettle(true, true, spool, onfinally, description);
446
+ await runSettle(true, error, spool, onfinally, description);
411
447
  throw error;
412
448
  }
413
449
  if (spool && oncleanup) spool.roll(result, oncleanup);
414
- await runSettle(true, false, spool, onfinally, description);
450
+ await runSettle(true, void 0, spool, onfinally, description);
415
451
  return result;
416
452
  }
417
- await runSettle(true, true, spool, onfinally, description);
453
+ await runSettle(true, error, spool, onfinally, description);
418
454
  throw error;
419
455
  });
420
456
  else {
421
457
  if (spool && oncleanup) spool.roll(result, oncleanup);
422
- runSettle(false, false, spool, onfinally, description);
458
+ runSettle(false, void 0, spool, onfinally, description);
423
459
  return result;
424
460
  }
425
461
  }
@@ -430,9 +466,9 @@ var OptionsTrait = class {
430
466
  this.options = {
431
467
  id: "",
432
468
  codec: "cbor",
433
- timeout: 10 * 1e3,
469
+ timeout: 1e4,
434
470
  share: "",
435
- chunkSize: 16 * 1024,
471
+ chunkSize: 16384,
436
472
  chunkCredit: 4,
437
473
  topicMake: (name, operation, peerId) => {
438
474
  return `${name}/${operation}/${peerId ?? "any"}`;
@@ -447,6 +483,9 @@ var OptionsTrait = class {
447
483
  },
448
484
  ...options
449
485
  };
486
+ if (!(Number.isFinite(this.options.timeout) && this.options.timeout > 0)) throw new Error("options: \"timeout\" must be a positive number");
487
+ if (!(Number.isInteger(this.options.chunkSize) && this.options.chunkSize > 0)) throw new Error("options: \"chunkSize\" must be a positive integer");
488
+ if (!(Number.isInteger(this.options.chunkCredit) && this.options.chunkCredit >= 0)) throw new Error("options: \"chunkCredit\" must be a non-negative integer");
450
489
  }
451
490
  };
452
491
  //#endregion
@@ -959,13 +998,13 @@ var BaseTrait = class extends TraceTrait {
959
998
  };
960
999
  } });
961
1000
  }
962
- this.mqtt = mqtt;
1001
+ this._mqtt = mqtt;
963
1002
  if (this.options.id === "") {
964
1003
  const clientId = mqtt.isFakeProxy ? void 0 : mqtt.options?.clientId;
965
1004
  this.options.id = typeof clientId === "string" && clientId !== "" ? clientId : (0, nanoid.nanoid)();
966
1005
  }
967
1006
  this.log("info", "hooking into MQTT client");
968
- this.messageHandler = (topic, message, packet) => {
1007
+ this._messageHandler = (topic, message, packet) => {
969
1008
  if (this.destroyed) return;
970
1009
  let input;
971
1010
  if (this.options.codec === "json") input = message.toString();
@@ -976,12 +1015,12 @@ var BaseTrait = class extends TraceTrait {
976
1015
  }
977
1016
  this._onMessage(topic, input, packet);
978
1017
  };
979
- this.mqtt.on("message", this.messageHandler);
1018
+ this._mqtt.on("message", this._messageHandler);
980
1019
  }
981
1020
  async destroy() {
982
1021
  this.destroyed = true;
983
1022
  this.log("info", "un-hooking from MQTT client");
984
- this.mqtt.off("message", this.messageHandler);
1023
+ this._mqtt.off("message", this._messageHandler);
985
1024
  this.onRequest.clear();
986
1025
  this.onResponse.clear();
987
1026
  }
@@ -1000,7 +1039,7 @@ var BaseTrait = class extends TraceTrait {
1000
1039
  async subscribeTopic(topic, options = {}) {
1001
1040
  this.log("info", `subscribing to MQTT topic "${topic}"`);
1002
1041
  return new Promise((resolve, reject) => {
1003
- this.mqtt.subscribe(topic, {
1042
+ this._mqtt.subscribe(topic, {
1004
1043
  qos: 2,
1005
1044
  ...options
1006
1045
  }, (err, granted) => {
@@ -1018,7 +1057,7 @@ var BaseTrait = class extends TraceTrait {
1018
1057
  async unsubscribeTopic(topic) {
1019
1058
  this.log("info", `unsubscribing from MQTT topic "${topic}"`);
1020
1059
  return new Promise((resolve, reject) => {
1021
- this.mqtt.unsubscribe(topic, (err, _packet) => {
1060
+ this._mqtt.unsubscribe(topic, (err, _packet) => {
1022
1061
  if (err) {
1023
1062
  this.error(err, `unsubscribing from MQTT topic "${topic}" failed`);
1024
1063
  reject(err);
@@ -1042,7 +1081,7 @@ var BaseTrait = class extends TraceTrait {
1042
1081
  this.log("debug", `publishing to MQTT topic "${topic}"`, { message: messageOnDemand });
1043
1082
  return new Promise((resolve, reject) => {
1044
1083
  const messageData = typeof message === "string" ? message : Buffer.from(message.buffer, message.byteOffset, message.byteLength);
1045
- this.mqtt.publish(topic, messageData, options, (err) => {
1084
+ this._mqtt.publish(topic, messageData, options, (err) => {
1046
1085
  if (err) {
1047
1086
  this.error(err, `publishing to MQTT topic "${topic}" failed`);
1048
1087
  reject(err);
@@ -1077,7 +1116,10 @@ var BaseTrait = class extends TraceTrait {
1077
1116
  return;
1078
1117
  }
1079
1118
  this.log("debug", `received from MQTT topic "${topic}"`, { message });
1080
- 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}")`);
1119
+ if (topicMatch.peerId !== void 0 && message.receiver !== void 0 && message.receiver !== topicMatch.peerId) {
1120
+ this.log("warning", `receiver mismatch -- dropped message from direct topic "${topic}" (expected "${topicMatch.peerId}", got "${message.receiver}")`);
1121
+ return;
1122
+ }
1081
1123
  if (this.msg.isRequest(message)) {
1082
1124
  const handler = this.onRequest.get(`${message.type}:${message.name}`);
1083
1125
  if (handler !== void 0) Promise.resolve().then(() => {
@@ -1100,7 +1142,7 @@ var BaseTrait = class extends TraceTrait {
1100
1142
  //#endregion
1101
1143
  //#region dst-stage1/mqtt-plus-subscription.js
1102
1144
  var RefCountedSubscription = class {
1103
- constructor(subscribeFn, unsubscribeFn, lingerMs = 30 * 1e3) {
1145
+ constructor(subscribeFn, unsubscribeFn, lingerMs = 3e4) {
1104
1146
  this.subscribeFn = subscribeFn;
1105
1147
  this.unsubscribeFn = unsubscribeFn;
1106
1148
  this.lingerMs = lingerMs;
@@ -1116,12 +1158,14 @@ var RefCountedSubscription = class {
1116
1158
  }
1117
1159
  decrementCount(topic) {
1118
1160
  const count = this.counts.get(topic);
1119
- if (count !== void 0) if (count <= 1) {
1120
- this.counts.delete(topic);
1121
- return 0;
1122
- } else {
1123
- this.counts.set(topic, count - 1);
1124
- return count - 1;
1161
+ if (count !== void 0) {
1162
+ if (count <= 1) {
1163
+ this.counts.delete(topic);
1164
+ return 0;
1165
+ } else {
1166
+ this.counts.set(topic, count - 1);
1167
+ return count - 1;
1168
+ }
1125
1169
  }
1126
1170
  }
1127
1171
  clearCount(topic) {
@@ -1160,21 +1204,23 @@ var RefCountedSubscription = class {
1160
1204
  }
1161
1205
  }
1162
1206
  async unsubscribe(topic) {
1163
- if (this.decrementCount(topic) === 0) if (this.lingerMs > 0 && !topic.startsWith("$share/")) {
1164
- const timer = setTimeout(() => {
1165
- this.lingers.delete(topic);
1207
+ if (this.decrementCount(topic) === 0) {
1208
+ if (this.lingerMs > 0 && !topic.startsWith("$share/")) {
1209
+ const timer = setTimeout(() => {
1210
+ this.lingers.delete(topic);
1211
+ const promise = this.unsubscribeFn(topic).catch(() => {}).finally(() => {
1212
+ this.unsubbing.delete(topic);
1213
+ });
1214
+ this.unsubbing.set(topic, promise);
1215
+ }, this.lingerMs);
1216
+ this.lingers.set(topic, timer);
1217
+ } else {
1166
1218
  const promise = this.unsubscribeFn(topic).catch(() => {}).finally(() => {
1167
1219
  this.unsubbing.delete(topic);
1168
1220
  });
1169
1221
  this.unsubbing.set(topic, promise);
1170
- }, this.lingerMs);
1171
- this.lingers.set(topic, timer);
1172
- } else {
1173
- const promise = this.unsubscribeFn(topic).catch(() => {}).finally(() => {
1174
- this.unsubbing.delete(topic);
1175
- });
1176
- this.unsubbing.set(topic, promise);
1177
- await promise;
1222
+ await promise;
1223
+ }
1178
1224
  }
1179
1225
  }
1180
1226
  async flush() {
@@ -1443,7 +1489,7 @@ var EventTrait = class extends AuthTrait {
1443
1489
  this.log("warning", `event name mismatch -- dropped request for "${name}" (topic: "${topicName}", payload: "${request.name}")`, { requestId });
1444
1490
  return;
1445
1491
  }
1446
- const params = request.params ?? [];
1492
+ const params = alignHandlerParams(request.params ?? [], callback);
1447
1493
  if (this.eventControllers.has(requestId)) {
1448
1494
  this.log("warning", `duplicate event request id -- dropped request for event "${name}"`, { requestId });
1449
1495
  return;
@@ -1567,7 +1613,7 @@ var ServiceTrait = class extends EventTrait {
1567
1613
  if (request.receiver && request.receiver !== this.options.id) return;
1568
1614
  const requestId = request.id;
1569
1615
  const senderId = request.sender;
1570
- const params = request.params ?? [];
1616
+ const params = alignHandlerParams(request.params ?? [], callback);
1571
1617
  if (senderId === void 0 || senderId === "") {
1572
1618
  this.error(/* @__PURE__ */ new Error("invalid request: missing sender"));
1573
1619
  return;
@@ -1671,7 +1717,9 @@ var ServiceTrait = class extends EventTrait {
1671
1717
  const settle = () => {
1672
1718
  if (settled) return false;
1673
1719
  settled = true;
1674
- spool.unroll();
1720
+ spool.unroll(false)?.catch((err) => {
1721
+ this.error(ensureError(err, `cleanup after call of service "${name}" failed`));
1722
+ });
1675
1723
  return true;
1676
1724
  };
1677
1725
  const promise = new Promise((resolve, reject) => {
@@ -1778,7 +1826,7 @@ var SourceTrait = class extends ServiceTrait {
1778
1826
  this.onRequest.set(`source-fetch-request:${name}`, async (request, topicName) => {
1779
1827
  if (request.receiver && request.receiver !== this.options.id) return;
1780
1828
  const requestId = request.id;
1781
- const params = request.params ?? [];
1829
+ const params = alignHandlerParams(request.params ?? [], callback);
1782
1830
  const sender = request.sender;
1783
1831
  const receiver = request.receiver;
1784
1832
  const reqSpool = new Spool();
@@ -1831,11 +1879,16 @@ var SourceTrait = class extends ServiceTrait {
1831
1879
  if (current.size === 0) this.sourceRequests.delete(name);
1832
1880
  }
1833
1881
  });
1834
- abortSignal.addEventListener("abort", () => {
1835
- if (info.stream instanceof node_stream.Readable && !info.stream.destroyed) info.stream.destroy(ensureError(abortSignal.reason));
1836
- }, { once: true });
1882
+ const noopError = () => {};
1883
+ const destroyStream = () => {
1884
+ if (info.stream instanceof node_stream.Readable && !info.stream.destroyed) {
1885
+ info.stream.on("error", noopError);
1886
+ info.stream.destroy(ensureError(abortSignal.reason));
1887
+ }
1888
+ };
1889
+ abortSignal.addEventListener("abort", destroyStream, { once: true });
1837
1890
  reqSpool.roll(() => {
1838
- if (abortSignal.aborted && info.stream instanceof node_stream.Readable && !info.stream.destroyed) info.stream.destroy(ensureError(abortSignal.reason));
1891
+ if (abortSignal.aborted) destroyStream();
1839
1892
  });
1840
1893
  let abortReject;
1841
1894
  const abortPromise = new Promise((_resolve, reject) => {
@@ -1982,7 +2035,7 @@ var SourceTrait = class extends ServiceTrait {
1982
2035
  const sourceNameMismatchError = (kind, actualName) => /* @__PURE__ */ new Error(`${kind} name mismatch (expected "${name}", got "${actualName}")`);
1983
2036
  await this.subscribeTopicAndSpool(spool, responseTopic, { qos: options.qos ?? 2 });
1984
2037
  const chunkCredit = this.options.chunkCredit;
1985
- const maxBufferedBytes = chunkCredit > 0 ? chunkCredit * this.options.chunkSize : 16 * 1024;
2038
+ const maxBufferedBytes = chunkCredit > 0 ? chunkCredit * this.options.chunkSize : 16384;
1986
2039
  let chunksReceived = 0;
1987
2040
  let creditGranted = chunkCredit;
1988
2041
  let responderId = receiver;
@@ -2220,7 +2273,7 @@ var SinkTrait = class extends SourceTrait {
2220
2273
  this.onRequest.set(`sink-push-request:${name}`, async (request, topicName) => {
2221
2274
  if (request.receiver && request.receiver !== this.options.id) return;
2222
2275
  const requestId = request.id;
2223
- const params = request.params ?? [];
2276
+ const params = alignHandlerParams(request.params ?? [], callback);
2224
2277
  const sender = request.sender;
2225
2278
  const receiver = request.receiver;
2226
2279
  const reqSpool = new Spool();
@@ -2281,7 +2334,7 @@ var SinkTrait = class extends SourceTrait {
2281
2334
  chunksReceived: 0,
2282
2335
  creditGranted: chunkCredit
2283
2336
  } : void 0;
2284
- const maxBufferedBytes = chunkCredit > 0 ? chunkCredit * this.options.chunkSize : 16 * 1024;
2337
+ const maxBufferedBytes = chunkCredit > 0 ? chunkCredit * this.options.chunkSize : 16384;
2285
2338
  let streamEnded = false;
2286
2339
  const pushTimerId = `sink-push-recv:${requestId}`;
2287
2340
  const refreshPushTimeout = () => this.timerRefresh(pushTimerId, () => {
@@ -2511,8 +2564,12 @@ var SinkTrait = class extends SourceTrait {
2511
2564
  });
2512
2565
  if (data instanceof node_stream.Readable) {
2513
2566
  const stream = data;
2567
+ const noopError = () => {};
2514
2568
  abortSignal.addEventListener("abort", () => {
2515
- if (!stream.destroyed) stream.destroy(ensureError(abortSignal.reason));
2569
+ if (!stream.destroyed) {
2570
+ stream.on("error", noopError);
2571
+ stream.destroy(ensureError(abortSignal.reason));
2572
+ }
2516
2573
  }, { once: true });
2517
2574
  }
2518
2575
  const pushTimerId = `sink-push-send:${requestId}`;
@@ -2546,18 +2603,21 @@ var SinkTrait = class extends SourceTrait {
2546
2603
  pushFinalizeReject = reject;
2547
2604
  });
2548
2605
  pushFinalize.catch(() => {});
2606
+ const failPush = (error) => {
2607
+ abortController.abort(error);
2608
+ if (!pushAcked) {
2609
+ pushInitialSettled = true;
2610
+ pushInitialReject(error);
2611
+ } else if (!pushFinalized) {
2612
+ pushFinalized = true;
2613
+ pushFinalizeReject(error);
2614
+ }
2615
+ };
2549
2616
  const lockResponder = (kind, sender) => {
2550
2617
  if (sender === void 0 || sender === "") {
2551
2618
  const error = /* @__PURE__ */ new Error(`received ${kind} without sender`);
2552
2619
  remoteErrorObject = error;
2553
- abortController.abort(error);
2554
- if (!pushAcked) {
2555
- pushInitialSettled = true;
2556
- pushInitialReject(error);
2557
- } else if (!pushFinalized) {
2558
- pushFinalized = true;
2559
- pushFinalizeReject(error);
2560
- }
2620
+ failPush(error);
2561
2621
  return false;
2562
2622
  }
2563
2623
  if (responderId === void 0) responderId = sender;
@@ -2575,37 +2635,25 @@ var SinkTrait = class extends SourceTrait {
2575
2635
  if (response.name !== name) {
2576
2636
  const error = /* @__PURE__ */ new Error(`sink response name mismatch (expected "${name}", got "${response.name}")`);
2577
2637
  remoteErrorObject = error;
2578
- abortController.abort(error);
2579
- if (!pushAcked) {
2580
- pushInitialSettled = true;
2581
- pushInitialReject(error);
2582
- } else if (!pushFinalized) {
2583
- pushFinalized = true;
2584
- pushFinalizeReject(error);
2585
- }
2638
+ failPush(error);
2586
2639
  return;
2587
2640
  }
2588
2641
  if (!lockResponder("sink response", response.sender)) return;
2589
2642
  if (response.error) {
2590
2643
  const error = new Error(response.error);
2591
2644
  remoteErrorObject = error;
2592
- abortController.abort(error);
2593
- if (!pushAcked) {
2594
- pushInitialSettled = true;
2595
- pushInitialReject(error);
2596
- } else if (!pushFinalized) {
2597
- pushFinalized = true;
2598
- pushFinalizeReject(error);
2599
- }
2645
+ failPush(error);
2600
2646
  } else if (!pushAcked) {
2601
2647
  initialCredit = response.credit;
2602
2648
  pushAcked = true;
2603
2649
  pushInitialSettled = true;
2604
2650
  pushInitialResolve();
2605
- } else if (!pushFinalized) if (pushDataComplete) {
2606
- pushFinalized = true;
2607
- pushFinalizeResolve();
2608
- } else pushTerminalReceived = true;
2651
+ } else if (!pushFinalized) {
2652
+ if (pushDataComplete) {
2653
+ pushFinalized = true;
2654
+ pushFinalizeResolve();
2655
+ } else pushTerminalReceived = true;
2656
+ }
2609
2657
  });
2610
2658
  spool.roll(() => {
2611
2659
  this.onResponse.delete(`sink-push-response:${requestId}`);
@@ -2614,28 +2662,14 @@ var SinkTrait = class extends SourceTrait {
2614
2662
  if (response.name !== name) {
2615
2663
  const error = /* @__PURE__ */ new Error(`sink credit name mismatch (expected "${name}", got "${response.name}")`);
2616
2664
  remoteErrorObject = error;
2617
- abortController.abort(error);
2618
- if (!pushAcked) {
2619
- pushInitialSettled = true;
2620
- pushInitialReject(error);
2621
- } else if (!pushFinalized) {
2622
- pushFinalized = true;
2623
- pushFinalizeReject(error);
2624
- }
2665
+ failPush(error);
2625
2666
  return;
2626
2667
  }
2627
2668
  if (!lockResponder("sink credit", response.sender)) return;
2628
2669
  if (response.credit === 0) {
2629
2670
  cancelledByReceiver = true;
2630
2671
  const error = /* @__PURE__ */ new Error(`push to sink "${name}" cancelled by receiver`);
2631
- abortController.abort(error);
2632
- if (!pushAcked) {
2633
- pushInitialSettled = true;
2634
- pushInitialReject(error);
2635
- } else if (!pushFinalized) {
2636
- pushFinalized = true;
2637
- pushFinalizeReject(error);
2638
- }
2672
+ failPush(error);
2639
2673
  return;
2640
2674
  }
2641
2675
  if (creditGate !== void 0) {
@@ -2644,11 +2678,7 @@ var SinkTrait = class extends SourceTrait {
2644
2678
  } else if (pushAcked && initialCredit === void 0) {
2645
2679
  const error = /* @__PURE__ */ new Error(`push to sink "${name}" received unsolicited credit (credit-flow disabled)`);
2646
2680
  remoteErrorObject = error;
2647
- abortController.abort(error);
2648
- if (!pushFinalized) {
2649
- pushFinalized = true;
2650
- pushFinalizeReject(error);
2651
- }
2681
+ failPush(error);
2652
2682
  } else pendingCredit += response.credit;
2653
2683
  });
2654
2684
  spool.roll(() => {
@@ -2735,7 +2765,7 @@ var SinkTrait = class extends SourceTrait {
2735
2765
  }
2736
2766
  } catch {}
2737
2767
  if (pushAcked && !remoteErrorObject) await new Promise((resolve) => {
2738
- setImmediate(resolve);
2768
+ setTimeout(resolve, 0);
2739
2769
  });
2740
2770
  if (remoteErrorObject) throw remoteErrorObject;
2741
2771
  throw err;
@@ -2746,6 +2776,22 @@ var SinkTrait = class extends SourceTrait {
2746
2776
  };
2747
2777
  //#endregion
2748
2778
  //#region dst-stage1/mqtt-plus.js
2749
- var MQTTp = class extends SinkTrait {};
2779
+ var MQTTp = class extends SinkTrait {
2780
+ static {
2781
+ this.VERSION = "1.4";
2782
+ }
2783
+ static {
2784
+ this.MIN_VERSION = "1.4";
2785
+ }
2786
+ static {
2787
+ this.version = version;
2788
+ }
2789
+ static {
2790
+ this.minVersion = minVersion;
2791
+ }
2792
+ static {
2793
+ this.versionToNum = versionToNum;
2794
+ }
2795
+ };
2750
2796
  //#endregion
2751
2797
  module.exports = MQTTp;