mqtt-plus 1.4.23 → 1.4.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ase/service.log +10381 -0
- package/.ase/task/TASK-P18.md +47 -0
- package/.ase/task/TASK-P2.md +76 -0
- package/.ase/task/TASK-P21.md +75 -0
- package/.claude/settings.local.json +7 -0
- package/CHANGELOG.md +18 -0
- package/README.md +4 -4
- package/dst-stage1/mqtt-plus-api.d.ts +6 -6
- package/dst-stage1/mqtt-plus-base.js +21 -4
- package/dst-stage1/mqtt-plus-codec.js +15 -6
- package/dst-stage1/mqtt-plus-error.js +47 -14
- package/dst-stage1/mqtt-plus-event.js +1 -1
- package/dst-stage1/mqtt-plus-service.js +1 -1
- package/dst-stage1/mqtt-plus-sink.js +10 -2
- package/dst-stage1/mqtt-plus-source.d.ts +1 -0
- package/dst-stage1/mqtt-plus-source.js +11 -3
- package/dst-stage1/mqtt-plus-subscription.js +7 -2
- package/dst-stage1/mqtt-plus-timer.js +4 -2
- package/dst-stage2/mqtt-plus.cjs.cjs +89 -35
- package/dst-stage2/mqtt-plus.esm.js +89 -35
- package/dst-stage2/mqtt-plus.umd.js +11 -11
- package/package.json +1 -1
- package/src/mqtt-plus-api.ts +5 -5
- package/src/mqtt-plus-base.ts +21 -4
- package/src/mqtt-plus-codec.ts +15 -8
- package/src/mqtt-plus-error.ts +52 -14
- package/src/mqtt-plus-event.ts +1 -1
- package/src/mqtt-plus-service.ts +1 -1
- package/src/mqtt-plus-sink.ts +12 -2
- package/src/mqtt-plus-source.ts +13 -3
- package/src/mqtt-plus-subscription.ts +7 -2
- package/src/mqtt-plus-timer.ts +4 -2
- package/tst/mqtt-plus-4-sink.spec.ts +43 -0
- package/tst/mqtt-plus-5-source.spec.ts +35 -0
- package/tst/mqtt-plus-6-misc.spec.ts +82 -5
- package/tst/mqtt-plus-8-run.spec.ts +107 -0
- package/.ase/service.yaml +0 -1
|
@@ -309,6 +309,31 @@ function runUnroll(isAsync, spool) {
|
|
|
309
309
|
if (isAsync && !(result instanceof Promise)) result = Promise.resolve(result);
|
|
310
310
|
return result;
|
|
311
311
|
}
|
|
312
|
+
function runSettle(isAsync, pending, spool, onfinally, description) {
|
|
313
|
+
if (!isAsync) {
|
|
314
|
+
let failure;
|
|
315
|
+
try {
|
|
316
|
+
runFinally(false, onfinally, description);
|
|
317
|
+
} catch (arg) {
|
|
318
|
+
failure = ensureError(arg, description);
|
|
319
|
+
}
|
|
320
|
+
if (pending || failure !== void 0) {
|
|
321
|
+
runUnroll(false, spool);
|
|
322
|
+
if (!pending && failure !== void 0) throw failure;
|
|
323
|
+
}
|
|
324
|
+
} else return (async () => {
|
|
325
|
+
let failure;
|
|
326
|
+
try {
|
|
327
|
+
await runFinally(true, onfinally, description);
|
|
328
|
+
} catch (arg) {
|
|
329
|
+
failure = ensureError(arg, description);
|
|
330
|
+
}
|
|
331
|
+
if (pending || failure !== void 0) {
|
|
332
|
+
await runUnroll(true, spool);
|
|
333
|
+
if (!pending && failure !== void 0) throw failure;
|
|
334
|
+
}
|
|
335
|
+
})();
|
|
336
|
+
}
|
|
312
337
|
function run(...args) {
|
|
313
338
|
let description;
|
|
314
339
|
let spool;
|
|
@@ -360,40 +385,41 @@ function run(...args) {
|
|
|
360
385
|
result = oncatch(error);
|
|
361
386
|
} catch (arg) {
|
|
362
387
|
error = ensureError(arg, description);
|
|
363
|
-
|
|
364
|
-
runUnroll(false, spool);
|
|
388
|
+
runSettle(false, true, spool, onfinally, description);
|
|
365
389
|
throw error;
|
|
366
390
|
}
|
|
367
|
-
|
|
391
|
+
if (spool && oncleanup) spool.roll(result, oncleanup);
|
|
392
|
+
runSettle(false, false, spool, onfinally, description);
|
|
368
393
|
return result;
|
|
369
394
|
}
|
|
370
|
-
|
|
371
|
-
runUnroll(false, spool);
|
|
395
|
+
runSettle(false, true, spool, onfinally, description);
|
|
372
396
|
throw error;
|
|
373
397
|
}
|
|
374
398
|
if (result instanceof Promise) return result.then(async (result) => {
|
|
375
|
-
await runFinally(true, onfinally, description);
|
|
376
399
|
if (spool && oncleanup) spool.roll(result, oncleanup);
|
|
400
|
+
await runSettle(true, false, spool, onfinally, description);
|
|
377
401
|
return result;
|
|
378
402
|
}, async (arg) => {
|
|
379
403
|
let error = ensureError(arg, description);
|
|
380
|
-
if (oncatch)
|
|
381
|
-
|
|
382
|
-
|
|
404
|
+
if (oncatch) {
|
|
405
|
+
let result;
|
|
406
|
+
try {
|
|
407
|
+
result = await oncatch(error);
|
|
408
|
+
} catch (arg) {
|
|
409
|
+
error = ensureError(arg, description);
|
|
410
|
+
await runSettle(true, true, spool, onfinally, description);
|
|
411
|
+
throw error;
|
|
412
|
+
}
|
|
413
|
+
if (spool && oncleanup) spool.roll(result, oncleanup);
|
|
414
|
+
await runSettle(true, false, spool, onfinally, description);
|
|
383
415
|
return result;
|
|
384
|
-
} catch (arg) {
|
|
385
|
-
error = ensureError(arg, description);
|
|
386
|
-
await runFinally(true, onfinally, description);
|
|
387
|
-
await runUnroll(true, spool);
|
|
388
|
-
throw error;
|
|
389
416
|
}
|
|
390
|
-
await
|
|
391
|
-
await runUnroll(true, spool);
|
|
417
|
+
await runSettle(true, true, spool, onfinally, description);
|
|
392
418
|
throw error;
|
|
393
419
|
});
|
|
394
420
|
else {
|
|
395
|
-
runFinally(false, onfinally, description);
|
|
396
421
|
if (spool && oncleanup) spool.roll(result, oncleanup);
|
|
422
|
+
runSettle(false, false, spool, onfinally, description);
|
|
397
423
|
return result;
|
|
398
424
|
}
|
|
399
425
|
}
|
|
@@ -425,7 +451,7 @@ var OptionsTrait = class {
|
|
|
425
451
|
};
|
|
426
452
|
//#endregion
|
|
427
453
|
//#region dst-stage1/mqtt-plus-codec.js
|
|
428
|
-
var JSONX = class {
|
|
454
|
+
var JSONX = class JSONX {
|
|
429
455
|
static uint8ArrayToBase64(arr) {
|
|
430
456
|
return node_buffer.Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength).toString("base64");
|
|
431
457
|
}
|
|
@@ -433,10 +459,13 @@ var JSONX = class {
|
|
|
433
459
|
return new Uint8Array(node_buffer.Buffer.from(base64, "base64"));
|
|
434
460
|
}
|
|
435
461
|
static stringify(obj) {
|
|
436
|
-
return JSON.stringify(obj, (
|
|
462
|
+
return JSON.stringify(obj, function(key, value) {
|
|
463
|
+
const orig = this[key];
|
|
464
|
+
return orig instanceof node_buffer.Buffer ? { __Buffer: JSONX.uint8ArrayToBase64(orig) } : orig instanceof Uint8Array ? { __Uint8Array: JSONX.uint8ArrayToBase64(orig) } : value;
|
|
465
|
+
});
|
|
437
466
|
}
|
|
438
467
|
static parse(json) {
|
|
439
|
-
return JSON.parse(json, (_, value) => typeof value?.__Uint8Array === "string" ? this.base64ToUint8Array(value.__Uint8Array) : value);
|
|
468
|
+
return JSON.parse(json, (_, value) => typeof value?.__Buffer === "string" ? node_buffer.Buffer.from(value.__Buffer, "base64") : typeof value?.__Uint8Array === "string" ? this.base64ToUint8Array(value.__Uint8Array) : value);
|
|
440
469
|
}
|
|
441
470
|
};
|
|
442
471
|
var Codec = class {
|
|
@@ -941,7 +970,10 @@ var BaseTrait = class extends TraceTrait {
|
|
|
941
970
|
let input;
|
|
942
971
|
if (this.options.codec === "json") input = message.toString();
|
|
943
972
|
else if (this.options.codec === "cbor") input = Buffer.isBuffer(message) ? new Uint8Array(message.buffer, message.byteOffset, message.byteLength) : message;
|
|
944
|
-
else
|
|
973
|
+
else {
|
|
974
|
+
this.error(/* @__PURE__ */ new Error("invalid codec configured"));
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
945
977
|
this._onMessage(topic, input, packet);
|
|
946
978
|
};
|
|
947
979
|
this.mqtt.on("message", this.messageHandler);
|
|
@@ -971,10 +1003,14 @@ var BaseTrait = class extends TraceTrait {
|
|
|
971
1003
|
this.mqtt.subscribe(topic, {
|
|
972
1004
|
qos: 2,
|
|
973
1005
|
...options
|
|
974
|
-
}, (err,
|
|
1006
|
+
}, (err, granted) => {
|
|
975
1007
|
if (err) {
|
|
976
1008
|
this.error(err, `subscribing to MQTT topic "${topic}" failed`);
|
|
977
1009
|
reject(err);
|
|
1010
|
+
} else if (Array.isArray(granted) && granted.some((grant) => typeof grant?.qos === "number" && grant.qos >= 128)) {
|
|
1011
|
+
const error = /* @__PURE__ */ new Error(`broker rejected subscription to MQTT topic "${topic}" (grant codes: ${granted.map((grant) => grant?.qos).join(", ")})`);
|
|
1012
|
+
this.error(error, `subscribing to MQTT topic "${topic}" failed`);
|
|
1013
|
+
reject(error);
|
|
978
1014
|
} else resolve();
|
|
979
1015
|
});
|
|
980
1016
|
});
|
|
@@ -1016,7 +1052,13 @@ var BaseTrait = class extends TraceTrait {
|
|
|
1016
1052
|
}
|
|
1017
1053
|
_onMessage(topic, data, _packet) {
|
|
1018
1054
|
if (this.destroyed) return;
|
|
1019
|
-
|
|
1055
|
+
let topicMatch;
|
|
1056
|
+
try {
|
|
1057
|
+
topicMatch = this.options.topicMatch(topic);
|
|
1058
|
+
} catch (err) {
|
|
1059
|
+
this.error(ensureError(err, "failed to match MQTT topic"));
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1020
1062
|
if (topicMatch === null) return;
|
|
1021
1063
|
if (typeof data === "string") this.log("info", `received from MQTT topic "${topic}" (type: string, length: ${data.length} chars)`);
|
|
1022
1064
|
else this.log("info", `received from MQTT topic "${topic}" (type: buffer, length: ${data.byteLength} bytes)`);
|
|
@@ -1118,7 +1160,7 @@ var RefCountedSubscription = class {
|
|
|
1118
1160
|
}
|
|
1119
1161
|
}
|
|
1120
1162
|
async unsubscribe(topic) {
|
|
1121
|
-
if (this.decrementCount(topic) === 0) if (this.lingerMs > 0) {
|
|
1163
|
+
if (this.decrementCount(topic) === 0) if (this.lingerMs > 0 && !topic.startsWith("$share/")) {
|
|
1122
1164
|
const timer = setTimeout(() => {
|
|
1123
1165
|
this.lingers.delete(topic);
|
|
1124
1166
|
const promise = this.unsubscribeFn(topic).catch(() => {}).finally(() => {
|
|
@@ -1212,7 +1254,7 @@ var TimerTrait = class extends SubscriptionTrait {
|
|
|
1212
1254
|
if (ac !== void 0) ac.abort();
|
|
1213
1255
|
resolve();
|
|
1214
1256
|
}, durationMs);
|
|
1215
|
-
timer.unref();
|
|
1257
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
1216
1258
|
if (signal !== void 0) {
|
|
1217
1259
|
if (ac !== void 0) signal.addEventListener("abort", () => {
|
|
1218
1260
|
if (timer !== null) clearTimeout(timer);
|
|
@@ -1236,7 +1278,7 @@ var TimerTrait = class extends SubscriptionTrait {
|
|
|
1236
1278
|
if (ac !== void 0) ac.abort();
|
|
1237
1279
|
reject(new Error(info));
|
|
1238
1280
|
}, durationMs);
|
|
1239
|
-
timer.unref();
|
|
1281
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
1240
1282
|
if (signal !== void 0) {
|
|
1241
1283
|
if (ac !== void 0) signal.addEventListener("abort", () => {
|
|
1242
1284
|
if (timer !== null) clearTimeout(timer);
|
|
@@ -1417,7 +1459,7 @@ var EventTrait = class extends AuthTrait {
|
|
|
1417
1459
|
if (request.receiver) info.receiver = request.receiver;
|
|
1418
1460
|
if (request.meta) info.meta = request.meta;
|
|
1419
1461
|
try {
|
|
1420
|
-
if (auth) info.authenticated = await this.authenticated(senderId, request.auth, auth, `event "${name}"`);
|
|
1462
|
+
if (auth !== void 0) info.authenticated = await this.authenticated(senderId, request.auth, auth, `event "${name}"`);
|
|
1421
1463
|
await callback(...params, info);
|
|
1422
1464
|
} catch (result) {
|
|
1423
1465
|
const error = ensureError(result);
|
|
@@ -1558,7 +1600,7 @@ var ServiceTrait = class extends EventTrait {
|
|
|
1558
1600
|
armServiceTimeout();
|
|
1559
1601
|
let removeAbortListener;
|
|
1560
1602
|
try {
|
|
1561
|
-
if (auth) info.authenticated = await this.authenticated(senderId, request.auth, auth, `service "${name}"`);
|
|
1603
|
+
if (auth !== void 0) info.authenticated = await this.authenticated(senderId, request.auth, auth, `service "${name}"`);
|
|
1562
1604
|
const abortPromise = new Promise((_resolve, reject) => {
|
|
1563
1605
|
const onAbort = () => {
|
|
1564
1606
|
reject(ensureError(abortSignal.reason));
|
|
@@ -1696,8 +1738,11 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1696
1738
|
this.sourceControllers = /* @__PURE__ */ new Map();
|
|
1697
1739
|
this.sourceSpools = /* @__PURE__ */ new Map();
|
|
1698
1740
|
this.sourceRequests = /* @__PURE__ */ new Map();
|
|
1741
|
+
this.fetchAborts = /* @__PURE__ */ new Map();
|
|
1699
1742
|
}
|
|
1700
1743
|
async destroy() {
|
|
1744
|
+
for (const abort of this.fetchAborts.values()) abort(/* @__PURE__ */ new Error("instance destroyed"));
|
|
1745
|
+
this.fetchAborts.clear();
|
|
1701
1746
|
for (const controller of this.sourceControllers.values()) controller.abort(/* @__PURE__ */ new Error("source destroyed"));
|
|
1702
1747
|
for (const gate of this.sourceCreditGates.values()) gate.abort();
|
|
1703
1748
|
for (const spool of [...this.sourceSpools.values()]) await spool.unroll();
|
|
@@ -1737,10 +1782,6 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1737
1782
|
const sender = request.sender;
|
|
1738
1783
|
const receiver = request.receiver;
|
|
1739
1784
|
const reqSpool = new Spool();
|
|
1740
|
-
this.sourceSpools.set(requestId, reqSpool);
|
|
1741
|
-
reqSpool.roll(() => {
|
|
1742
|
-
this.sourceSpools.delete(requestId);
|
|
1743
|
-
});
|
|
1744
1785
|
if (sender === void 0 || sender === "") {
|
|
1745
1786
|
this.error(/* @__PURE__ */ new Error("invalid request: missing sender"));
|
|
1746
1787
|
await reqSpool.unroll();
|
|
@@ -1767,6 +1808,10 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1767
1808
|
reqSpool.roll(() => {
|
|
1768
1809
|
this.sourceControllers.delete(requestId);
|
|
1769
1810
|
});
|
|
1811
|
+
this.sourceSpools.set(requestId, reqSpool);
|
|
1812
|
+
reqSpool.roll(() => {
|
|
1813
|
+
this.sourceSpools.delete(requestId);
|
|
1814
|
+
});
|
|
1770
1815
|
const info = {
|
|
1771
1816
|
sender,
|
|
1772
1817
|
signal: abortSignal
|
|
@@ -1852,7 +1897,7 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1852
1897
|
reqSpool.roll(() => {
|
|
1853
1898
|
this.onResponse.delete(`source-fetch-credit:${requestId}`);
|
|
1854
1899
|
});
|
|
1855
|
-
if (auth) info.authenticated = await this.authenticated(sender, request.auth, auth, `source "${name}"`);
|
|
1900
|
+
if (auth !== void 0) info.authenticated = await this.authenticated(sender, request.auth, auth, `source "${name}"`);
|
|
1856
1901
|
const callbackPromise = Promise.resolve(callback(...params, info));
|
|
1857
1902
|
callbackPromise.catch(() => {});
|
|
1858
1903
|
await Promise.race([callbackPromise, abortPromise]);
|
|
@@ -2011,6 +2056,10 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
2011
2056
|
metaReject(error);
|
|
2012
2057
|
stream.destroy(error);
|
|
2013
2058
|
};
|
|
2059
|
+
this.fetchAborts.set(requestId, endWithError);
|
|
2060
|
+
spool.roll(() => {
|
|
2061
|
+
this.fetchAborts.delete(requestId);
|
|
2062
|
+
});
|
|
2014
2063
|
let cancelled = false;
|
|
2015
2064
|
const cancelAndUnroll = (reason) => {
|
|
2016
2065
|
if (!cancelled && !streamEnded) {
|
|
@@ -2227,7 +2276,7 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2227
2276
|
try {
|
|
2228
2277
|
if (topicName !== request.name) throw new Error(`sink name mismatch (topic: "${topicName}", payload: "${request.name}")`);
|
|
2229
2278
|
let authenticated = void 0;
|
|
2230
|
-
if (auth) authenticated = await this.authenticated(sender, request.auth, auth, `sink "${name}"`);
|
|
2279
|
+
if (auth !== void 0) authenticated = await this.authenticated(sender, request.auth, auth, `sink "${name}"`);
|
|
2231
2280
|
const creditState = chunkCredit > 0 ? {
|
|
2232
2281
|
chunksReceived: 0,
|
|
2233
2282
|
creditGranted: chunkCredit
|
|
@@ -2237,7 +2286,11 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2237
2286
|
const pushTimerId = `sink-push-recv:${requestId}`;
|
|
2238
2287
|
const refreshPushTimeout = () => this.timerRefresh(pushTimerId, () => {
|
|
2239
2288
|
if (streamEnded || this.destroying) return;
|
|
2240
|
-
|
|
2289
|
+
const error = /* @__PURE__ */ new Error("push stream timeout");
|
|
2290
|
+
abortController.abort(error);
|
|
2291
|
+
streamEnded = true;
|
|
2292
|
+
const stream = this.pushStreams.get(requestId);
|
|
2293
|
+
if (stream !== void 0 && !stream.destroyed) stream.destroy(error);
|
|
2241
2294
|
if (sender && !errorResponseSent) {
|
|
2242
2295
|
errorResponseSent = true;
|
|
2243
2296
|
const cancelMsg = this.msg.makeSinkPushCredit(requestId, name, 0, this.options.id, sender);
|
|
@@ -2517,6 +2570,7 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2517
2570
|
pushInitialResolve = resolve;
|
|
2518
2571
|
pushInitialReject = reject;
|
|
2519
2572
|
});
|
|
2573
|
+
pushInitial.catch(() => {});
|
|
2520
2574
|
this.onResponse.set(`sink-push-response:${requestId}`, (response) => {
|
|
2521
2575
|
if (response.name !== name) {
|
|
2522
2576
|
const error = /* @__PURE__ */ new Error(`sink response name mismatch (expected "${name}", got "${response.name}")`);
|
|
@@ -282,6 +282,31 @@ function runUnroll(isAsync, spool) {
|
|
|
282
282
|
if (isAsync && !(result instanceof Promise)) result = Promise.resolve(result);
|
|
283
283
|
return result;
|
|
284
284
|
}
|
|
285
|
+
function runSettle(isAsync, pending, spool, onfinally, description) {
|
|
286
|
+
if (!isAsync) {
|
|
287
|
+
let failure;
|
|
288
|
+
try {
|
|
289
|
+
runFinally(false, onfinally, description);
|
|
290
|
+
} catch (arg) {
|
|
291
|
+
failure = ensureError(arg, description);
|
|
292
|
+
}
|
|
293
|
+
if (pending || failure !== void 0) {
|
|
294
|
+
runUnroll(false, spool);
|
|
295
|
+
if (!pending && failure !== void 0) throw failure;
|
|
296
|
+
}
|
|
297
|
+
} else return (async () => {
|
|
298
|
+
let failure;
|
|
299
|
+
try {
|
|
300
|
+
await runFinally(true, onfinally, description);
|
|
301
|
+
} catch (arg) {
|
|
302
|
+
failure = ensureError(arg, description);
|
|
303
|
+
}
|
|
304
|
+
if (pending || failure !== void 0) {
|
|
305
|
+
await runUnroll(true, spool);
|
|
306
|
+
if (!pending && failure !== void 0) throw failure;
|
|
307
|
+
}
|
|
308
|
+
})();
|
|
309
|
+
}
|
|
285
310
|
function run(...args) {
|
|
286
311
|
let description;
|
|
287
312
|
let spool;
|
|
@@ -333,40 +358,41 @@ function run(...args) {
|
|
|
333
358
|
result = oncatch(error);
|
|
334
359
|
} catch (arg) {
|
|
335
360
|
error = ensureError(arg, description);
|
|
336
|
-
|
|
337
|
-
runUnroll(false, spool);
|
|
361
|
+
runSettle(false, true, spool, onfinally, description);
|
|
338
362
|
throw error;
|
|
339
363
|
}
|
|
340
|
-
|
|
364
|
+
if (spool && oncleanup) spool.roll(result, oncleanup);
|
|
365
|
+
runSettle(false, false, spool, onfinally, description);
|
|
341
366
|
return result;
|
|
342
367
|
}
|
|
343
|
-
|
|
344
|
-
runUnroll(false, spool);
|
|
368
|
+
runSettle(false, true, spool, onfinally, description);
|
|
345
369
|
throw error;
|
|
346
370
|
}
|
|
347
371
|
if (result instanceof Promise) return result.then(async (result) => {
|
|
348
|
-
await runFinally(true, onfinally, description);
|
|
349
372
|
if (spool && oncleanup) spool.roll(result, oncleanup);
|
|
373
|
+
await runSettle(true, false, spool, onfinally, description);
|
|
350
374
|
return result;
|
|
351
375
|
}, async (arg) => {
|
|
352
376
|
let error = ensureError(arg, description);
|
|
353
|
-
if (oncatch)
|
|
354
|
-
|
|
355
|
-
|
|
377
|
+
if (oncatch) {
|
|
378
|
+
let result;
|
|
379
|
+
try {
|
|
380
|
+
result = await oncatch(error);
|
|
381
|
+
} catch (arg) {
|
|
382
|
+
error = ensureError(arg, description);
|
|
383
|
+
await runSettle(true, true, spool, onfinally, description);
|
|
384
|
+
throw error;
|
|
385
|
+
}
|
|
386
|
+
if (spool && oncleanup) spool.roll(result, oncleanup);
|
|
387
|
+
await runSettle(true, false, spool, onfinally, description);
|
|
356
388
|
return result;
|
|
357
|
-
} catch (arg) {
|
|
358
|
-
error = ensureError(arg, description);
|
|
359
|
-
await runFinally(true, onfinally, description);
|
|
360
|
-
await runUnroll(true, spool);
|
|
361
|
-
throw error;
|
|
362
389
|
}
|
|
363
|
-
await
|
|
364
|
-
await runUnroll(true, spool);
|
|
390
|
+
await runSettle(true, true, spool, onfinally, description);
|
|
365
391
|
throw error;
|
|
366
392
|
});
|
|
367
393
|
else {
|
|
368
|
-
runFinally(false, onfinally, description);
|
|
369
394
|
if (spool && oncleanup) spool.roll(result, oncleanup);
|
|
395
|
+
runSettle(false, false, spool, onfinally, description);
|
|
370
396
|
return result;
|
|
371
397
|
}
|
|
372
398
|
}
|
|
@@ -398,7 +424,7 @@ var OptionsTrait = class {
|
|
|
398
424
|
};
|
|
399
425
|
//#endregion
|
|
400
426
|
//#region dst-stage1/mqtt-plus-codec.js
|
|
401
|
-
var JSONX = class {
|
|
427
|
+
var JSONX = class JSONX {
|
|
402
428
|
static uint8ArrayToBase64(arr) {
|
|
403
429
|
return Buffer$1.from(arr.buffer, arr.byteOffset, arr.byteLength).toString("base64");
|
|
404
430
|
}
|
|
@@ -406,10 +432,13 @@ var JSONX = class {
|
|
|
406
432
|
return new Uint8Array(Buffer$1.from(base64, "base64"));
|
|
407
433
|
}
|
|
408
434
|
static stringify(obj) {
|
|
409
|
-
return JSON.stringify(obj, (
|
|
435
|
+
return JSON.stringify(obj, function(key, value) {
|
|
436
|
+
const orig = this[key];
|
|
437
|
+
return orig instanceof Buffer$1 ? { __Buffer: JSONX.uint8ArrayToBase64(orig) } : orig instanceof Uint8Array ? { __Uint8Array: JSONX.uint8ArrayToBase64(orig) } : value;
|
|
438
|
+
});
|
|
410
439
|
}
|
|
411
440
|
static parse(json) {
|
|
412
|
-
return JSON.parse(json, (_, value) => typeof value?.__Uint8Array === "string" ? this.base64ToUint8Array(value.__Uint8Array) : value);
|
|
441
|
+
return JSON.parse(json, (_, value) => typeof value?.__Buffer === "string" ? Buffer$1.from(value.__Buffer, "base64") : typeof value?.__Uint8Array === "string" ? this.base64ToUint8Array(value.__Uint8Array) : value);
|
|
413
442
|
}
|
|
414
443
|
};
|
|
415
444
|
var Codec = class {
|
|
@@ -914,7 +943,10 @@ var BaseTrait = class extends TraceTrait {
|
|
|
914
943
|
let input;
|
|
915
944
|
if (this.options.codec === "json") input = message.toString();
|
|
916
945
|
else if (this.options.codec === "cbor") input = Buffer.isBuffer(message) ? new Uint8Array(message.buffer, message.byteOffset, message.byteLength) : message;
|
|
917
|
-
else
|
|
946
|
+
else {
|
|
947
|
+
this.error(/* @__PURE__ */ new Error("invalid codec configured"));
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
918
950
|
this._onMessage(topic, input, packet);
|
|
919
951
|
};
|
|
920
952
|
this.mqtt.on("message", this.messageHandler);
|
|
@@ -944,10 +976,14 @@ var BaseTrait = class extends TraceTrait {
|
|
|
944
976
|
this.mqtt.subscribe(topic, {
|
|
945
977
|
qos: 2,
|
|
946
978
|
...options
|
|
947
|
-
}, (err,
|
|
979
|
+
}, (err, granted) => {
|
|
948
980
|
if (err) {
|
|
949
981
|
this.error(err, `subscribing to MQTT topic "${topic}" failed`);
|
|
950
982
|
reject(err);
|
|
983
|
+
} else if (Array.isArray(granted) && granted.some((grant) => typeof grant?.qos === "number" && grant.qos >= 128)) {
|
|
984
|
+
const error = /* @__PURE__ */ new Error(`broker rejected subscription to MQTT topic "${topic}" (grant codes: ${granted.map((grant) => grant?.qos).join(", ")})`);
|
|
985
|
+
this.error(error, `subscribing to MQTT topic "${topic}" failed`);
|
|
986
|
+
reject(error);
|
|
951
987
|
} else resolve();
|
|
952
988
|
});
|
|
953
989
|
});
|
|
@@ -989,7 +1025,13 @@ var BaseTrait = class extends TraceTrait {
|
|
|
989
1025
|
}
|
|
990
1026
|
_onMessage(topic, data, _packet) {
|
|
991
1027
|
if (this.destroyed) return;
|
|
992
|
-
|
|
1028
|
+
let topicMatch;
|
|
1029
|
+
try {
|
|
1030
|
+
topicMatch = this.options.topicMatch(topic);
|
|
1031
|
+
} catch (err) {
|
|
1032
|
+
this.error(ensureError(err, "failed to match MQTT topic"));
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
993
1035
|
if (topicMatch === null) return;
|
|
994
1036
|
if (typeof data === "string") this.log("info", `received from MQTT topic "${topic}" (type: string, length: ${data.length} chars)`);
|
|
995
1037
|
else this.log("info", `received from MQTT topic "${topic}" (type: buffer, length: ${data.byteLength} bytes)`);
|
|
@@ -1091,7 +1133,7 @@ var RefCountedSubscription = class {
|
|
|
1091
1133
|
}
|
|
1092
1134
|
}
|
|
1093
1135
|
async unsubscribe(topic) {
|
|
1094
|
-
if (this.decrementCount(topic) === 0) if (this.lingerMs > 0) {
|
|
1136
|
+
if (this.decrementCount(topic) === 0) if (this.lingerMs > 0 && !topic.startsWith("$share/")) {
|
|
1095
1137
|
const timer = setTimeout(() => {
|
|
1096
1138
|
this.lingers.delete(topic);
|
|
1097
1139
|
const promise = this.unsubscribeFn(topic).catch(() => {}).finally(() => {
|
|
@@ -1185,7 +1227,7 @@ var TimerTrait = class extends SubscriptionTrait {
|
|
|
1185
1227
|
if (ac !== void 0) ac.abort();
|
|
1186
1228
|
resolve();
|
|
1187
1229
|
}, durationMs);
|
|
1188
|
-
timer.unref();
|
|
1230
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
1189
1231
|
if (signal !== void 0) {
|
|
1190
1232
|
if (ac !== void 0) signal.addEventListener("abort", () => {
|
|
1191
1233
|
if (timer !== null) clearTimeout(timer);
|
|
@@ -1209,7 +1251,7 @@ var TimerTrait = class extends SubscriptionTrait {
|
|
|
1209
1251
|
if (ac !== void 0) ac.abort();
|
|
1210
1252
|
reject(new Error(info));
|
|
1211
1253
|
}, durationMs);
|
|
1212
|
-
timer.unref();
|
|
1254
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
1213
1255
|
if (signal !== void 0) {
|
|
1214
1256
|
if (ac !== void 0) signal.addEventListener("abort", () => {
|
|
1215
1257
|
if (timer !== null) clearTimeout(timer);
|
|
@@ -1390,7 +1432,7 @@ var EventTrait = class extends AuthTrait {
|
|
|
1390
1432
|
if (request.receiver) info.receiver = request.receiver;
|
|
1391
1433
|
if (request.meta) info.meta = request.meta;
|
|
1392
1434
|
try {
|
|
1393
|
-
if (auth) info.authenticated = await this.authenticated(senderId, request.auth, auth, `event "${name}"`);
|
|
1435
|
+
if (auth !== void 0) info.authenticated = await this.authenticated(senderId, request.auth, auth, `event "${name}"`);
|
|
1394
1436
|
await callback(...params, info);
|
|
1395
1437
|
} catch (result) {
|
|
1396
1438
|
const error = ensureError(result);
|
|
@@ -1531,7 +1573,7 @@ var ServiceTrait = class extends EventTrait {
|
|
|
1531
1573
|
armServiceTimeout();
|
|
1532
1574
|
let removeAbortListener;
|
|
1533
1575
|
try {
|
|
1534
|
-
if (auth) info.authenticated = await this.authenticated(senderId, request.auth, auth, `service "${name}"`);
|
|
1576
|
+
if (auth !== void 0) info.authenticated = await this.authenticated(senderId, request.auth, auth, `service "${name}"`);
|
|
1535
1577
|
const abortPromise = new Promise((_resolve, reject) => {
|
|
1536
1578
|
const onAbort = () => {
|
|
1537
1579
|
reject(ensureError(abortSignal.reason));
|
|
@@ -1669,8 +1711,11 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1669
1711
|
this.sourceControllers = /* @__PURE__ */ new Map();
|
|
1670
1712
|
this.sourceSpools = /* @__PURE__ */ new Map();
|
|
1671
1713
|
this.sourceRequests = /* @__PURE__ */ new Map();
|
|
1714
|
+
this.fetchAborts = /* @__PURE__ */ new Map();
|
|
1672
1715
|
}
|
|
1673
1716
|
async destroy() {
|
|
1717
|
+
for (const abort of this.fetchAborts.values()) abort(/* @__PURE__ */ new Error("instance destroyed"));
|
|
1718
|
+
this.fetchAborts.clear();
|
|
1674
1719
|
for (const controller of this.sourceControllers.values()) controller.abort(/* @__PURE__ */ new Error("source destroyed"));
|
|
1675
1720
|
for (const gate of this.sourceCreditGates.values()) gate.abort();
|
|
1676
1721
|
for (const spool of [...this.sourceSpools.values()]) await spool.unroll();
|
|
@@ -1710,10 +1755,6 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1710
1755
|
const sender = request.sender;
|
|
1711
1756
|
const receiver = request.receiver;
|
|
1712
1757
|
const reqSpool = new Spool();
|
|
1713
|
-
this.sourceSpools.set(requestId, reqSpool);
|
|
1714
|
-
reqSpool.roll(() => {
|
|
1715
|
-
this.sourceSpools.delete(requestId);
|
|
1716
|
-
});
|
|
1717
1758
|
if (sender === void 0 || sender === "") {
|
|
1718
1759
|
this.error(/* @__PURE__ */ new Error("invalid request: missing sender"));
|
|
1719
1760
|
await reqSpool.unroll();
|
|
@@ -1740,6 +1781,10 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1740
1781
|
reqSpool.roll(() => {
|
|
1741
1782
|
this.sourceControllers.delete(requestId);
|
|
1742
1783
|
});
|
|
1784
|
+
this.sourceSpools.set(requestId, reqSpool);
|
|
1785
|
+
reqSpool.roll(() => {
|
|
1786
|
+
this.sourceSpools.delete(requestId);
|
|
1787
|
+
});
|
|
1743
1788
|
const info = {
|
|
1744
1789
|
sender,
|
|
1745
1790
|
signal: abortSignal
|
|
@@ -1825,7 +1870,7 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1825
1870
|
reqSpool.roll(() => {
|
|
1826
1871
|
this.onResponse.delete(`source-fetch-credit:${requestId}`);
|
|
1827
1872
|
});
|
|
1828
|
-
if (auth) info.authenticated = await this.authenticated(sender, request.auth, auth, `source "${name}"`);
|
|
1873
|
+
if (auth !== void 0) info.authenticated = await this.authenticated(sender, request.auth, auth, `source "${name}"`);
|
|
1829
1874
|
const callbackPromise = Promise.resolve(callback(...params, info));
|
|
1830
1875
|
callbackPromise.catch(() => {});
|
|
1831
1876
|
await Promise.race([callbackPromise, abortPromise]);
|
|
@@ -1984,6 +2029,10 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1984
2029
|
metaReject(error);
|
|
1985
2030
|
stream.destroy(error);
|
|
1986
2031
|
};
|
|
2032
|
+
this.fetchAborts.set(requestId, endWithError);
|
|
2033
|
+
spool.roll(() => {
|
|
2034
|
+
this.fetchAborts.delete(requestId);
|
|
2035
|
+
});
|
|
1987
2036
|
let cancelled = false;
|
|
1988
2037
|
const cancelAndUnroll = (reason) => {
|
|
1989
2038
|
if (!cancelled && !streamEnded) {
|
|
@@ -2200,7 +2249,7 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2200
2249
|
try {
|
|
2201
2250
|
if (topicName !== request.name) throw new Error(`sink name mismatch (topic: "${topicName}", payload: "${request.name}")`);
|
|
2202
2251
|
let authenticated = void 0;
|
|
2203
|
-
if (auth) authenticated = await this.authenticated(sender, request.auth, auth, `sink "${name}"`);
|
|
2252
|
+
if (auth !== void 0) authenticated = await this.authenticated(sender, request.auth, auth, `sink "${name}"`);
|
|
2204
2253
|
const creditState = chunkCredit > 0 ? {
|
|
2205
2254
|
chunksReceived: 0,
|
|
2206
2255
|
creditGranted: chunkCredit
|
|
@@ -2210,7 +2259,11 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2210
2259
|
const pushTimerId = `sink-push-recv:${requestId}`;
|
|
2211
2260
|
const refreshPushTimeout = () => this.timerRefresh(pushTimerId, () => {
|
|
2212
2261
|
if (streamEnded || this.destroying) return;
|
|
2213
|
-
|
|
2262
|
+
const error = /* @__PURE__ */ new Error("push stream timeout");
|
|
2263
|
+
abortController.abort(error);
|
|
2264
|
+
streamEnded = true;
|
|
2265
|
+
const stream = this.pushStreams.get(requestId);
|
|
2266
|
+
if (stream !== void 0 && !stream.destroyed) stream.destroy(error);
|
|
2214
2267
|
if (sender && !errorResponseSent) {
|
|
2215
2268
|
errorResponseSent = true;
|
|
2216
2269
|
const cancelMsg = this.msg.makeSinkPushCredit(requestId, name, 0, this.options.id, sender);
|
|
@@ -2490,6 +2543,7 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2490
2543
|
pushInitialResolve = resolve;
|
|
2491
2544
|
pushInitialReject = reject;
|
|
2492
2545
|
});
|
|
2546
|
+
pushInitial.catch(() => {});
|
|
2493
2547
|
this.onResponse.set(`sink-push-response:${requestId}`, (response) => {
|
|
2494
2548
|
if (response.name !== name) {
|
|
2495
2549
|
const error = /* @__PURE__ */ new Error(`sink response name mismatch (expected "${name}", got "${response.name}")`);
|