mqtt-plus 1.4.18 → 1.4.19
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/CHANGELOG.md +6 -0
- package/dst-stage1/mqtt-plus-api.js +0 -1
- package/dst-stage1/mqtt-plus-auth.js +0 -1
- package/dst-stage1/mqtt-plus-base.js +0 -1
- package/dst-stage1/mqtt-plus-codec.js +0 -1
- package/dst-stage1/mqtt-plus-encode.js +0 -1
- package/dst-stage1/mqtt-plus-error.js +0 -1
- package/dst-stage1/mqtt-plus-event.js +0 -1
- package/dst-stage1/mqtt-plus-info.js +0 -1
- package/dst-stage1/mqtt-plus-meta.js +0 -1
- package/dst-stage1/mqtt-plus-msg.js +0 -1
- package/dst-stage1/mqtt-plus-options.js +0 -1
- package/dst-stage1/mqtt-plus-service.js +0 -1
- package/dst-stage1/mqtt-plus-sink.js +26 -10
- package/dst-stage1/mqtt-plus-source.js +17 -11
- package/dst-stage1/mqtt-plus-subscription.js +0 -1
- package/dst-stage1/mqtt-plus-timer.js +0 -1
- package/dst-stage1/mqtt-plus-trace.js +0 -1
- package/dst-stage1/mqtt-plus-util.js +0 -1
- package/dst-stage1/mqtt-plus-version.js +0 -1
- package/dst-stage1/mqtt-plus.js +0 -1
- package/dst-stage1/tsc.tsbuildinfo +1 -1
- package/dst-stage2/mqtt-plus.cjs.cjs +30 -11
- package/dst-stage2/mqtt-plus.esm.js +30 -11
- package/dst-stage2/mqtt-plus.umd.js +1 -1
- package/package.json +3 -3
- package/src/mqtt-plus-sink.ts +26 -9
- package/src/mqtt-plus-source.ts +18 -10
- /package/package.d/{@typescript-eslint+typescript-estree+8.58.1.patch → @typescript-eslint+typescript-estree+8.58.2.patch} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -120,8 +120,6 @@ export class SinkTrait extends SourceTrait {
|
|
|
120
120
|
const receiver = request.receiver;
|
|
121
121
|
/* create a resource spool for request cleanup */
|
|
122
122
|
const reqSpool = new Spool();
|
|
123
|
-
this.pushSpools.set(requestId, reqSpool);
|
|
124
|
-
reqSpool.roll(() => { this.pushSpools.delete(requestId); });
|
|
125
123
|
/* sanity check sender */
|
|
126
124
|
if (sender === undefined || sender === "") {
|
|
127
125
|
this.error(new Error("invalid request: missing sender"));
|
|
@@ -162,6 +160,8 @@ export class SinkTrait extends SourceTrait {
|
|
|
162
160
|
}
|
|
163
161
|
this.pushRecvControllers.set(requestId, abortController);
|
|
164
162
|
reqSpool.roll(() => { this.pushRecvControllers.delete(requestId); });
|
|
163
|
+
this.pushSpools.set(requestId, reqSpool);
|
|
164
|
+
reqSpool.roll(() => { this.pushSpools.delete(requestId); });
|
|
165
165
|
/* check authentication and prepare stream */
|
|
166
166
|
let dataCompleted = false;
|
|
167
167
|
let ackSent = false;
|
|
@@ -358,10 +358,9 @@ export class SinkTrait extends SourceTrait {
|
|
|
358
358
|
await sendResponse(undefined, true);
|
|
359
359
|
ackSent = true;
|
|
360
360
|
/* call handler */
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
]);
|
|
361
|
+
const callbackPromise = Promise.resolve(callback(...params, info));
|
|
362
|
+
callbackPromise.catch(() => { }); /* guard against unhandled rejection if abort wins the race */
|
|
363
|
+
await Promise.race([callbackPromise, abortPromise]);
|
|
365
364
|
/* ensure stream is consumed or destroyed to prevent hang */
|
|
366
365
|
if (readable.readableFlowing !== true && !readable.destroyed)
|
|
367
366
|
readable.resume();
|
|
@@ -395,10 +394,14 @@ export class SinkTrait extends SourceTrait {
|
|
|
395
394
|
if (stream !== undefined && !stream.destroyed)
|
|
396
395
|
stream.destroy(error);
|
|
397
396
|
}
|
|
398
|
-
/* send error as nak response or as mid-stream error response
|
|
397
|
+
/* send error as nak response or as mid-stream error response
|
|
398
|
+
(skip when a terminal signal was already emitted, e.g. the
|
|
399
|
+
pre-emptive credit=0 cancel published by the timeout handler) */
|
|
399
400
|
this.error(error);
|
|
400
|
-
errorResponseSent
|
|
401
|
-
|
|
401
|
+
if (!errorResponseSent) {
|
|
402
|
+
errorResponseSent = true;
|
|
403
|
+
await sendResponse(error.message).catch(() => { });
|
|
404
|
+
}
|
|
402
405
|
}
|
|
403
406
|
finally {
|
|
404
407
|
/* cleanup resources */
|
|
@@ -625,6 +628,17 @@ export class SinkTrait extends SourceTrait {
|
|
|
625
628
|
creditGate.replenish(response.credit);
|
|
626
629
|
refreshTimeout();
|
|
627
630
|
}
|
|
631
|
+
else if (pushAcked && initialCredit === undefined) {
|
|
632
|
+
/* protocol violation: receiver sent credit despite
|
|
633
|
+
not granting initial credit during ack */
|
|
634
|
+
const error = new Error(`push to sink "${name}" received unsolicited credit (credit-flow disabled)`);
|
|
635
|
+
remoteErrorObject = error;
|
|
636
|
+
abortController.abort(error);
|
|
637
|
+
if (!pushFinalized) {
|
|
638
|
+
pushFinalized = true;
|
|
639
|
+
pushFinalizeReject(error);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
628
642
|
else
|
|
629
643
|
pendingCredit += response.credit;
|
|
630
644
|
});
|
|
@@ -660,6 +674,9 @@ export class SinkTrait extends SourceTrait {
|
|
|
660
674
|
refreshTimeout();
|
|
661
675
|
pendingCredit = 0;
|
|
662
676
|
}
|
|
677
|
+
else if (pendingCredit > 0)
|
|
678
|
+
/* protocol violation: receiver sent credit before ack despite not granting initial credit */
|
|
679
|
+
throw new Error(`push to sink "${name}" received unsolicited credit (credit-flow disabled)`);
|
|
663
680
|
/* register credit gate at instance level */
|
|
664
681
|
if (creditGate) {
|
|
665
682
|
this.pushCreditGates.set(requestId, creditGate);
|
|
@@ -736,4 +753,3 @@ export class SinkTrait extends SourceTrait {
|
|
|
736
753
|
}
|
|
737
754
|
}
|
|
738
755
|
}
|
|
739
|
-
//# sourceMappingURL=mqtt-plus-sink.js.map
|
|
@@ -199,9 +199,8 @@ export class SourceTrait extends ServiceTrait {
|
|
|
199
199
|
try {
|
|
200
200
|
if (topicName !== request.name)
|
|
201
201
|
throw new Error(`source name mismatch (topic: "${topicName}", payload: "${request.name}")`);
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
/* register credit/cancel handler (unconditional for cancel support) */
|
|
202
|
+
/* register credit/cancel handler early (before any await) so cancel
|
|
203
|
+
signals arriving during async authentication are not lost */
|
|
205
204
|
this.onResponse.set(`source-fetch-credit:${requestId}`, (creditParsed) => {
|
|
206
205
|
if (abortSignal.aborted)
|
|
207
206
|
return;
|
|
@@ -229,10 +228,13 @@ export class SourceTrait extends ServiceTrait {
|
|
|
229
228
|
reqSpool.roll(() => {
|
|
230
229
|
this.onResponse.delete(`source-fetch-credit:${requestId}`);
|
|
231
230
|
});
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
231
|
+
/* check for authentication */
|
|
232
|
+
if (auth)
|
|
233
|
+
info.authenticated = await this.authenticated(sender, request.auth, auth, `source "${name}"`);
|
|
234
|
+
/* finally call the handler callback */
|
|
235
|
+
const callbackPromise = Promise.resolve(callback(...params, info));
|
|
236
|
+
callbackPromise.catch(() => { }); /* guard against unhandled rejection if abort wins the race */
|
|
237
|
+
await Promise.race([callbackPromise, abortPromise]);
|
|
236
238
|
/* check for valid data source */
|
|
237
239
|
if (!(info.stream instanceof Readable) && !(info.buffer instanceof Promise) && !(info.buffer instanceof Uint8Array))
|
|
238
240
|
throw new Error("handler did not provide data via info.stream or info.buffer fields");
|
|
@@ -259,9 +261,14 @@ export class SourceTrait extends ServiceTrait {
|
|
|
259
261
|
await sendStreamAsChunks(info.stream, this.options.chunkSize, sendChunk, creditGate, abortSignal);
|
|
260
262
|
else if (info.buffer instanceof Promise || info.buffer instanceof Uint8Array) {
|
|
261
263
|
/* handle Buffer result */
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
264
|
+
let buffer;
|
|
265
|
+
if (info.buffer instanceof Promise) {
|
|
266
|
+
const bufferPromise = info.buffer;
|
|
267
|
+
bufferPromise.catch(() => { }); /* guard against unhandled rejection if abort wins the race */
|
|
268
|
+
buffer = await Promise.race([bufferPromise, abortPromise]);
|
|
269
|
+
}
|
|
270
|
+
else
|
|
271
|
+
buffer = info.buffer;
|
|
265
272
|
/* re-check abort: a late info.buffer resolution could win the race */
|
|
266
273
|
/* by a microtask margin even after abort fired -- discard silently */
|
|
267
274
|
abortSignal.throwIfAborted();
|
|
@@ -557,4 +564,3 @@ export class SourceTrait extends ServiceTrait {
|
|
|
557
564
|
return result;
|
|
558
565
|
}
|
|
559
566
|
}
|
|
560
|
-
//# sourceMappingURL=mqtt-plus-source.js.map
|
package/dst-stage1/mqtt-plus.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/mqtt-plus-api.ts","../src/mqtt-plus-auth.ts","../src/mqtt-plus-base.ts","../src/mqtt-plus-codec.ts","../src/mqtt-plus-encode.ts","../src/mqtt-plus-error.ts","../src/mqtt-plus-event.ts","../src/mqtt-plus-info.ts","../src/mqtt-plus-meta.ts","../src/mqtt-plus-msg.ts","../src/mqtt-plus-options.ts","../src/mqtt-plus-service.ts","../src/mqtt-plus-sink.ts","../src/mqtt-plus-source.ts","../src/mqtt-plus-subscription.ts","../src/mqtt-plus-timer.ts","../src/mqtt-plus-trace.ts","../src/mqtt-plus-util.ts","../src/mqtt-plus-version.ts","../src/mqtt-plus.ts"],"version":"6.0.
|
|
1
|
+
{"root":["../src/mqtt-plus-api.ts","../src/mqtt-plus-auth.ts","../src/mqtt-plus-base.ts","../src/mqtt-plus-codec.ts","../src/mqtt-plus-encode.ts","../src/mqtt-plus-error.ts","../src/mqtt-plus-event.ts","../src/mqtt-plus-info.ts","../src/mqtt-plus-meta.ts","../src/mqtt-plus-msg.ts","../src/mqtt-plus-options.ts","../src/mqtt-plus-service.ts","../src/mqtt-plus-sink.ts","../src/mqtt-plus-source.ts","../src/mqtt-plus-subscription.ts","../src/mqtt-plus-timer.ts","../src/mqtt-plus-trace.ts","../src/mqtt-plus-util.ts","../src/mqtt-plus-version.ts","../src/mqtt-plus.ts"],"version":"6.0.3"}
|
|
@@ -1824,7 +1824,6 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1824
1824
|
let cancelledByFetcher = false;
|
|
1825
1825
|
try {
|
|
1826
1826
|
if (topicName !== request.name) throw new Error(`source name mismatch (topic: "${topicName}", payload: "${request.name}")`);
|
|
1827
|
-
if (auth) info.authenticated = await this.authenticated(sender, request.auth, auth, `source "${name}"`);
|
|
1828
1827
|
this.onResponse.set(`source-fetch-credit:${requestId}`, (creditParsed) => {
|
|
1829
1828
|
if (abortSignal.aborted) return;
|
|
1830
1829
|
if (creditParsed.name !== name) {
|
|
@@ -1849,7 +1848,10 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1849
1848
|
reqSpool.roll(() => {
|
|
1850
1849
|
this.onResponse.delete(`source-fetch-credit:${requestId}`);
|
|
1851
1850
|
});
|
|
1852
|
-
await
|
|
1851
|
+
if (auth) info.authenticated = await this.authenticated(sender, request.auth, auth, `source "${name}"`);
|
|
1852
|
+
const callbackPromise = Promise.resolve(callback(...params, info));
|
|
1853
|
+
callbackPromise.catch(() => {});
|
|
1854
|
+
await Promise.race([callbackPromise, abortPromise]);
|
|
1853
1855
|
if (!(info.stream instanceof node_stream.Readable) && !(info.buffer instanceof Promise) && !(info.buffer instanceof Uint8Array)) throw new Error("handler did not provide data via info.stream or info.buffer fields");
|
|
1854
1856
|
if (info.stream instanceof node_stream.Readable && (info.buffer instanceof Promise || info.buffer instanceof Uint8Array)) throw new Error("handler has set both info.stream and info.buffer fields");
|
|
1855
1857
|
const initialCredit = request.credit;
|
|
@@ -1866,7 +1868,12 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1866
1868
|
ackSent = true;
|
|
1867
1869
|
if (info.stream instanceof node_stream.Readable) await sendStreamAsChunks(info.stream, this.options.chunkSize, sendChunk, creditGate, abortSignal);
|
|
1868
1870
|
else if (info.buffer instanceof Promise || info.buffer instanceof Uint8Array) {
|
|
1869
|
-
|
|
1871
|
+
let buffer;
|
|
1872
|
+
if (info.buffer instanceof Promise) {
|
|
1873
|
+
const bufferPromise = info.buffer;
|
|
1874
|
+
bufferPromise.catch(() => {});
|
|
1875
|
+
buffer = await Promise.race([bufferPromise, abortPromise]);
|
|
1876
|
+
} else buffer = info.buffer;
|
|
1870
1877
|
abortSignal.throwIfAborted();
|
|
1871
1878
|
await sendBufferAsChunks(buffer, this.options.chunkSize, sendChunk, creditGate, abortSignal);
|
|
1872
1879
|
}
|
|
@@ -2164,10 +2171,6 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2164
2171
|
const sender = request.sender;
|
|
2165
2172
|
const receiver = request.receiver;
|
|
2166
2173
|
const reqSpool = new Spool();
|
|
2167
|
-
this.pushSpools.set(requestId, reqSpool);
|
|
2168
|
-
reqSpool.roll(() => {
|
|
2169
|
-
this.pushSpools.delete(requestId);
|
|
2170
|
-
});
|
|
2171
2174
|
if (sender === void 0 || sender === "") {
|
|
2172
2175
|
this.error(/* @__PURE__ */ new Error("invalid request: missing sender"));
|
|
2173
2176
|
await reqSpool.unroll();
|
|
@@ -2208,6 +2211,10 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2208
2211
|
reqSpool.roll(() => {
|
|
2209
2212
|
this.pushRecvControllers.delete(requestId);
|
|
2210
2213
|
});
|
|
2214
|
+
this.pushSpools.set(requestId, reqSpool);
|
|
2215
|
+
reqSpool.roll(() => {
|
|
2216
|
+
this.pushSpools.delete(requestId);
|
|
2217
|
+
});
|
|
2211
2218
|
let dataCompleted = false;
|
|
2212
2219
|
let ackSent = false;
|
|
2213
2220
|
let errorResponseSent = false;
|
|
@@ -2367,7 +2374,9 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2367
2374
|
});
|
|
2368
2375
|
await sendResponse(void 0, true);
|
|
2369
2376
|
ackSent = true;
|
|
2370
|
-
|
|
2377
|
+
const callbackPromise = Promise.resolve(callback(...params, info));
|
|
2378
|
+
callbackPromise.catch(() => {});
|
|
2379
|
+
await Promise.race([callbackPromise, abortPromise]);
|
|
2371
2380
|
if (readable.readableFlowing !== true && !readable.destroyed) readable.resume();
|
|
2372
2381
|
await streamDone.catch((err) => {
|
|
2373
2382
|
this.error(ensureError(err), `stream drain after sink "${name}" callback failed`);
|
|
@@ -2388,8 +2397,10 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2388
2397
|
if (stream !== void 0 && !stream.destroyed) stream.destroy(error);
|
|
2389
2398
|
}
|
|
2390
2399
|
this.error(error);
|
|
2391
|
-
errorResponseSent
|
|
2392
|
-
|
|
2400
|
+
if (!errorResponseSent) {
|
|
2401
|
+
errorResponseSent = true;
|
|
2402
|
+
await sendResponse(error.message).catch(() => {});
|
|
2403
|
+
}
|
|
2393
2404
|
} finally {
|
|
2394
2405
|
const stream = this.pushStreams.get(requestId);
|
|
2395
2406
|
if (stream !== void 0 && !stream.destroyed && !dataCompleted && !errorResponseSent) stream.destroy(abortSignal.aborted ? ensureError(abortSignal.reason) : /* @__PURE__ */ new Error("sink push aborted without cause"));
|
|
@@ -2572,6 +2583,14 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2572
2583
|
if (creditGate !== void 0) {
|
|
2573
2584
|
creditGate.replenish(response.credit);
|
|
2574
2585
|
refreshTimeout();
|
|
2586
|
+
} else if (pushAcked && initialCredit === void 0) {
|
|
2587
|
+
const error = /* @__PURE__ */ new Error(`push to sink "${name}" received unsolicited credit (credit-flow disabled)`);
|
|
2588
|
+
remoteErrorObject = error;
|
|
2589
|
+
abortController.abort(error);
|
|
2590
|
+
if (!pushFinalized) {
|
|
2591
|
+
pushFinalized = true;
|
|
2592
|
+
pushFinalizeReject(error);
|
|
2593
|
+
}
|
|
2575
2594
|
} else pendingCredit += response.credit;
|
|
2576
2595
|
});
|
|
2577
2596
|
spool.roll(() => {
|
|
@@ -2607,7 +2626,7 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2607
2626
|
creditGate = new CreditGate(initialCredit + pendingCredit);
|
|
2608
2627
|
if (pendingCredit > 0) refreshTimeout();
|
|
2609
2628
|
pendingCredit = 0;
|
|
2610
|
-
}
|
|
2629
|
+
} else if (pendingCredit > 0) throw new Error(`push to sink "${name}" received unsolicited credit (credit-flow disabled)`);
|
|
2611
2630
|
if (creditGate) {
|
|
2612
2631
|
this.pushCreditGates.set(requestId, creditGate);
|
|
2613
2632
|
spool.roll(() => {
|
|
@@ -1797,7 +1797,6 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1797
1797
|
let cancelledByFetcher = false;
|
|
1798
1798
|
try {
|
|
1799
1799
|
if (topicName !== request.name) throw new Error(`source name mismatch (topic: "${topicName}", payload: "${request.name}")`);
|
|
1800
|
-
if (auth) info.authenticated = await this.authenticated(sender, request.auth, auth, `source "${name}"`);
|
|
1801
1800
|
this.onResponse.set(`source-fetch-credit:${requestId}`, (creditParsed) => {
|
|
1802
1801
|
if (abortSignal.aborted) return;
|
|
1803
1802
|
if (creditParsed.name !== name) {
|
|
@@ -1822,7 +1821,10 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1822
1821
|
reqSpool.roll(() => {
|
|
1823
1822
|
this.onResponse.delete(`source-fetch-credit:${requestId}`);
|
|
1824
1823
|
});
|
|
1825
|
-
await
|
|
1824
|
+
if (auth) info.authenticated = await this.authenticated(sender, request.auth, auth, `source "${name}"`);
|
|
1825
|
+
const callbackPromise = Promise.resolve(callback(...params, info));
|
|
1826
|
+
callbackPromise.catch(() => {});
|
|
1827
|
+
await Promise.race([callbackPromise, abortPromise]);
|
|
1826
1828
|
if (!(info.stream instanceof Readable) && !(info.buffer instanceof Promise) && !(info.buffer instanceof Uint8Array)) throw new Error("handler did not provide data via info.stream or info.buffer fields");
|
|
1827
1829
|
if (info.stream instanceof Readable && (info.buffer instanceof Promise || info.buffer instanceof Uint8Array)) throw new Error("handler has set both info.stream and info.buffer fields");
|
|
1828
1830
|
const initialCredit = request.credit;
|
|
@@ -1839,7 +1841,12 @@ var SourceTrait = class extends ServiceTrait {
|
|
|
1839
1841
|
ackSent = true;
|
|
1840
1842
|
if (info.stream instanceof Readable) await sendStreamAsChunks(info.stream, this.options.chunkSize, sendChunk, creditGate, abortSignal);
|
|
1841
1843
|
else if (info.buffer instanceof Promise || info.buffer instanceof Uint8Array) {
|
|
1842
|
-
|
|
1844
|
+
let buffer;
|
|
1845
|
+
if (info.buffer instanceof Promise) {
|
|
1846
|
+
const bufferPromise = info.buffer;
|
|
1847
|
+
bufferPromise.catch(() => {});
|
|
1848
|
+
buffer = await Promise.race([bufferPromise, abortPromise]);
|
|
1849
|
+
} else buffer = info.buffer;
|
|
1843
1850
|
abortSignal.throwIfAborted();
|
|
1844
1851
|
await sendBufferAsChunks(buffer, this.options.chunkSize, sendChunk, creditGate, abortSignal);
|
|
1845
1852
|
}
|
|
@@ -2137,10 +2144,6 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2137
2144
|
const sender = request.sender;
|
|
2138
2145
|
const receiver = request.receiver;
|
|
2139
2146
|
const reqSpool = new Spool();
|
|
2140
|
-
this.pushSpools.set(requestId, reqSpool);
|
|
2141
|
-
reqSpool.roll(() => {
|
|
2142
|
-
this.pushSpools.delete(requestId);
|
|
2143
|
-
});
|
|
2144
2147
|
if (sender === void 0 || sender === "") {
|
|
2145
2148
|
this.error(/* @__PURE__ */ new Error("invalid request: missing sender"));
|
|
2146
2149
|
await reqSpool.unroll();
|
|
@@ -2181,6 +2184,10 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2181
2184
|
reqSpool.roll(() => {
|
|
2182
2185
|
this.pushRecvControllers.delete(requestId);
|
|
2183
2186
|
});
|
|
2187
|
+
this.pushSpools.set(requestId, reqSpool);
|
|
2188
|
+
reqSpool.roll(() => {
|
|
2189
|
+
this.pushSpools.delete(requestId);
|
|
2190
|
+
});
|
|
2184
2191
|
let dataCompleted = false;
|
|
2185
2192
|
let ackSent = false;
|
|
2186
2193
|
let errorResponseSent = false;
|
|
@@ -2340,7 +2347,9 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2340
2347
|
});
|
|
2341
2348
|
await sendResponse(void 0, true);
|
|
2342
2349
|
ackSent = true;
|
|
2343
|
-
|
|
2350
|
+
const callbackPromise = Promise.resolve(callback(...params, info));
|
|
2351
|
+
callbackPromise.catch(() => {});
|
|
2352
|
+
await Promise.race([callbackPromise, abortPromise]);
|
|
2344
2353
|
if (readable.readableFlowing !== true && !readable.destroyed) readable.resume();
|
|
2345
2354
|
await streamDone.catch((err) => {
|
|
2346
2355
|
this.error(ensureError(err), `stream drain after sink "${name}" callback failed`);
|
|
@@ -2361,8 +2370,10 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2361
2370
|
if (stream !== void 0 && !stream.destroyed) stream.destroy(error);
|
|
2362
2371
|
}
|
|
2363
2372
|
this.error(error);
|
|
2364
|
-
errorResponseSent
|
|
2365
|
-
|
|
2373
|
+
if (!errorResponseSent) {
|
|
2374
|
+
errorResponseSent = true;
|
|
2375
|
+
await sendResponse(error.message).catch(() => {});
|
|
2376
|
+
}
|
|
2366
2377
|
} finally {
|
|
2367
2378
|
const stream = this.pushStreams.get(requestId);
|
|
2368
2379
|
if (stream !== void 0 && !stream.destroyed && !dataCompleted && !errorResponseSent) stream.destroy(abortSignal.aborted ? ensureError(abortSignal.reason) : /* @__PURE__ */ new Error("sink push aborted without cause"));
|
|
@@ -2545,6 +2556,14 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2545
2556
|
if (creditGate !== void 0) {
|
|
2546
2557
|
creditGate.replenish(response.credit);
|
|
2547
2558
|
refreshTimeout();
|
|
2559
|
+
} else if (pushAcked && initialCredit === void 0) {
|
|
2560
|
+
const error = /* @__PURE__ */ new Error(`push to sink "${name}" received unsolicited credit (credit-flow disabled)`);
|
|
2561
|
+
remoteErrorObject = error;
|
|
2562
|
+
abortController.abort(error);
|
|
2563
|
+
if (!pushFinalized) {
|
|
2564
|
+
pushFinalized = true;
|
|
2565
|
+
pushFinalizeReject(error);
|
|
2566
|
+
}
|
|
2548
2567
|
} else pendingCredit += response.credit;
|
|
2549
2568
|
});
|
|
2550
2569
|
spool.roll(() => {
|
|
@@ -2580,7 +2599,7 @@ var SinkTrait = class extends SourceTrait {
|
|
|
2580
2599
|
creditGate = new CreditGate(initialCredit + pendingCredit);
|
|
2581
2600
|
if (pendingCredit > 0) refreshTimeout();
|
|
2582
2601
|
pendingCredit = 0;
|
|
2583
|
-
}
|
|
2602
|
+
} else if (pendingCredit > 0) throw new Error(`push to sink "${name}" received unsolicited credit (credit-flow disabled)`);
|
|
2584
2603
|
if (creditGate) {
|
|
2585
2604
|
this.pushCreditGates.set(requestId, creditGate);
|
|
2586
2605
|
spool.roll(() => {
|
|
@@ -10,4 +10,4 @@ ${Pi.repeat(a.depth)}}`:a.close=`}`;break}case Y.TAG:o+=String(c),o+=Li(Y.POS_IN
|
|
|
10
10
|
`;let n=0;for(let i of e.children){if(Vi(i)){let a=String(n);e.mt===Y.MAP?a=n%2?`val ${(n-1)/2}`:`key ${n/2}`:e.mt===Y.TAG&&(a=``),r+=Wi(i,t,a)}n++}}return r}var Gi={...Ni.defaultDecodeOptions,initialDepth:0,noPrefixHex:!1,minCol:0};function Ki(e,t){let n={...Gi,...t,ParentType:Bi,saveOriginal:!0},r=new ki(e,n),i,a;for(let e of r){if(a=Ni.create(e,i,n,r),e[2]===Kn.BREAK)if(i?.isStreaming)i.left=1;else throw Error(`Unexpected BREAK`);if(!Vi(a)){let t=new Bi(e,0,i,n);t.leaf=!0,t.children.push(a),Xn(t,r.toHere(e[3])),a=t}let t=(a.depth+1)*2,o=a.numBytes();for(o&&(t+=1,t+=o*2),n.minCol=Math.max(n.minCol,t),i&&i.push(a,r,e[3]),i=a;i?.done;)a=i,a.leaf||Xn(a,r.toHere(a.offset)),{parent:i}=i}t&&(t.minCol=n.minCol);let o=n.noPrefixHex?``:`0x${ir(r.toHere(0))}
|
|
11
11
|
`;return o+=Wi(a,n),o}var qi=!lr();function Ji(e){if(typeof e==`object`&&e){if(e.constructor!==Number)throw Error(`Expected number: ${e}`)}else if(typeof e!=`number`)throw Error(`Expected number: ${e}`)}function Yi(e){if(typeof e==`object`&&e){if(e.constructor!==String)throw Error(`Expected string: ${e}`)}else if(typeof e!=`string`)throw Error(`Expected string: ${e}`)}function Xi(e){if(!(e instanceof Uint8Array))throw Error(`Expected Uint8Array: ${e}`)}function Zi(e){if(!Array.isArray(e))throw Error(`Expected Array: ${e}`)}$(Map,(e,t,n)=>{let r=[...e.entries()].map(e=>[e[0],e[1],Ti(e[0],n)]);if(n.rejectDuplicateKeys){let e=new Set;for(let[t,n,i]of r){let t=ir(i);if(e.has(t))throw Error(`Duplicate map key: 0x${t}`);e.add(t)}}n.sortKeys&&r.sort(n.sortKeys),xi(e,e.size,Y.MAP,t,n);for(let[e,i,a]of r)t.write(a),wi(i,t,n)});function Qi(e){return Yi(e.contents),new Date(e.contents)}Qi.comment=e=>{Yi(e.contents);let t=new Date(e.contents);return`(String ${e.tag===X.DATE_FULL?`Full `:``}Date) ${t.toISOString()}`},Q.registerDecoder(X.DATE_STRING,Qi),Q.registerDecoder(X.DATE_FULL,Qi);function $i(e){return Ji(e.contents),new Date(e.contents*1e3)}$i.comment=e=>(Ji(e.contents),`(Epoch Date) ${new Date(e.contents*1e3).toISOString()}`),Q.registerDecoder(X.DATE_EPOCH,$i);var ea=1e3*60*60*24;function ta(e){return Ji(e.contents),new Date(e.contents*ea)}ta.comment=e=>(Ji(e.contents),`(Epoch Date) ${new Date(e.contents*ea).toISOString()}`),Q.registerDecoder(X.DATE_EPOCH_DAYS,ta),$(Date,(e,t,n)=>{switch(n.dateTag){case X.DATE_EPOCH:return[n.dateTag,e.valueOf()/1e3];case X.DATE_STRING:return[n.dateTag,e.toISOString().replace(/\.000Z$/,`Z`)];case X.DATE_EPOCH_DAYS:return[n.dateTag,Math.floor(e.valueOf()/ea)];case X.DATE_FULL:return[n.dateTag,e.toISOString().split(`T`)[0]];default:throw Error(`Unsupported date tag: ${n.dateTag}`)}});function na(e,t,n){if(Xi(t.contents),n.rejectBigInts)throw Error(`Decoding unwanted big integer: ${t}(h'${ir(t.contents)}')`);if(n.requirePreferred&&t.contents[0]===0)throw Error(`Decoding overly-large bigint: ${t.tag}(h'${ir(t.contents)})`);let r=t.contents.reduce((e,t)=>e<<8n|BigInt(t),0n);e&&(r=-1n-r);let i=r>=-(2**53-1)&&r<=2**53-1;if(n.requirePreferred&&i)throw Error(`Decoding bigint that could have been int: ${r}n`);return n.collapseBigInts&&i&&(r=Number(r)),n.boxed?Zn(r,t.contents):r}var ra=na.bind(null,!1),ia=na.bind(null,!0);ra.comment=(e,t)=>`(Positive BigInt) ${na(!1,e,t)}n`,ia.comment=(e,t)=>`(Negative BigInt) ${na(!0,e,t)}n`,Q.registerDecoder(X.POS_BIGINT,ra),Q.registerDecoder(X.NEG_BIGINT,ia);function aa(e,t){return Xi(e.contents),e}aa.comment=(e,t,n)=>{Xi(e.contents);let r={...t,initialDepth:n+2,noPrefixHex:!0},i=Jn(e),a=2**((i[0]&31)-24)+1,o=i[a]&31,s=ir(i.subarray(a,++a));o>=24&&(s+=` `,s+=ir(i.subarray(a,a+2**(o-24)))),r.minCol=Math.max(r.minCol,(n+1)*2+s.length);let c=Ki(e.contents,r),l=`Embedded CBOR
|
|
12
12
|
`;return l+=`${``.padStart((n+1)*2,` `)}${s}`.padEnd(r.minCol+1,` `),l+=`-- Bytes (Length: ${e.contents.length})
|
|
13
|
-
`,l+=c,l},aa.noChildren=!0,Q.registerDecoder(X.CBOR,aa),Q.registerDecoder(X.URI,e=>(Yi(e.contents),new URL(e.contents)),`URI`),$(URL,e=>[X.URI,e.toString()]),Q.registerDecoder(X.BASE64URL,e=>(Yi(e.contents),cr(e.contents)),`Base64url-encoded`),Q.registerDecoder(X.BASE64,e=>(Yi(e.contents),or(e.contents)),`Base64-encoded`),Q.registerDecoder(35,e=>(Yi(e.contents),new RegExp(e.contents)),`RegExp`),Q.registerDecoder(21065,e=>{Yi(e.contents);let t=`^(?:${e.contents})$`;return new RegExp(t,`u`)},`I-RegExp`),Q.registerDecoder(X.REGEXP,e=>{if(Zi(e.contents),e.contents.length<1||e.contents.length>2)throw Error(`Invalid RegExp Array: ${e.contents}`);return new RegExp(e.contents[0],e.contents[1])},`RegExp`),$(RegExp,e=>[X.REGEXP,[e.source,e.flags]]),Q.registerDecoder(64,e=>(Xi(e.contents),e.contents),`uint8 Typed Array`);function oa(e,t,n){Xi(e.contents);let r=e.contents.length;if(r%t.BYTES_PER_ELEMENT!==0)throw Error(`Number of bytes must be divisible by ${t.BYTES_PER_ELEMENT}, got: ${r}`);r/=t.BYTES_PER_ELEMENT;let i=new t(r),a=new DataView(e.contents.buffer,e.contents.byteOffset,e.contents.byteLength),o=a[`get${t.name.replace(/Array/,``)}`].bind(a);for(let e=0;e<r;e++)i[e]=o(e*t.BYTES_PER_ELEMENT,n);return i}function sa(e,t,n,r,i){let a=i.forceEndian??qi;if(mi(a?t:n,e,i),pi(r.byteLength,e,Y.BYTE_STRING),qi===a)e.write(new Uint8Array(r.buffer,r.byteOffset,r.byteLength));else{let t=e[`write${r.constructor.name.replace(/Array/,``)}`].bind(e);for(let e of r)t(e,a)}}Q.registerDecoder(65,e=>oa(e,Uint16Array,!1),`uint16, big endian, Typed Array`),Q.registerDecoder(66,e=>oa(e,Uint32Array,!1),`uint32, big endian, Typed Array`),Q.registerDecoder(67,e=>oa(e,BigUint64Array,!1),`uint64, big endian, Typed Array`),Q.registerDecoder(68,e=>(Xi(e.contents),new Uint8ClampedArray(e.contents)),`uint8 Typed Array, clamped arithmetic`),$(Uint8ClampedArray,e=>[68,new Uint8Array(e.buffer,e.byteOffset,e.byteLength)]),Q.registerDecoder(69,e=>oa(e,Uint16Array,!0),`uint16, little endian, Typed Array`),$(Uint16Array,(e,t,n)=>sa(t,69,65,e,n)),Q.registerDecoder(70,e=>oa(e,Uint32Array,!0),`uint32, little endian, Typed Array`),$(Uint32Array,(e,t,n)=>sa(t,70,66,e,n)),Q.registerDecoder(71,e=>oa(e,BigUint64Array,!0),`uint64, little endian, Typed Array`),$(BigUint64Array,(e,t,n)=>sa(t,71,67,e,n)),Q.registerDecoder(72,e=>(Xi(e.contents),new Int8Array(e.contents)),`sint8 Typed Array`),$(Int8Array,e=>[72,new Uint8Array(e.buffer,e.byteOffset,e.byteLength)]),Q.registerDecoder(73,e=>oa(e,Int16Array,!1),`sint16, big endian, Typed Array`),Q.registerDecoder(74,e=>oa(e,Int32Array,!1),`sint32, big endian, Typed Array`),Q.registerDecoder(75,e=>oa(e,BigInt64Array,!1),`sint64, big endian, Typed Array`),Q.registerDecoder(77,e=>oa(e,Int16Array,!0),`sint16, little endian, Typed Array`),$(Int16Array,(e,t,n)=>sa(t,77,73,e,n)),Q.registerDecoder(78,e=>oa(e,Int32Array,!0),`sint32, little endian, Typed Array`),$(Int32Array,(e,t,n)=>sa(t,78,74,e,n)),Q.registerDecoder(79,e=>oa(e,BigInt64Array,!0),`sint64, little endian, Typed Array`),$(BigInt64Array,(e,t,n)=>sa(t,79,75,e,n)),Q.registerDecoder(81,e=>oa(e,Float32Array,!1),`IEEE 754 binary32, big endian, Typed Array`),Q.registerDecoder(82,e=>oa(e,Float64Array,!1),`IEEE 754 binary64, big endian, Typed Array`),Q.registerDecoder(85,e=>oa(e,Float32Array,!0),`IEEE 754 binary32, little endian, Typed Array`),$(Float32Array,(e,t,n)=>sa(t,85,81,e,n)),Q.registerDecoder(86,e=>oa(e,Float64Array,!0),`IEEE 754 binary64, big endian, Typed Array`),$(Float64Array,(e,t,n)=>sa(t,86,82,e,n)),Q.registerDecoder(X.SET,(e,t)=>{if(Zi(e.contents),t.sortKeys){let n=Ni.decodeToEncodeOpts(t),r=null;for(let i of e.contents){let e=[i,void 0,Ti(i,n)];if(r&&t.sortKeys(r,e)>=0)throw Error(`Set items out of order in tag #${X.SET}`);r=e}}return new Set(e.contents)},`Set`),$(Set,(e,t,n)=>{let r=[...e];if(n.sortKeys){let e=r.map(e=>[e,void 0,Ti(e,n)]);e.sort(n.sortKeys),r=e.map(([e])=>e)}return[X.SET,r]}),Q.registerDecoder(X.JSON,e=>(Yi(e.contents),JSON.parse(e.contents)),`JSON-encoded`);function ca(e){return Xi(e.contents),new Yr().decode(e.contents)}ca.comment=e=>{Xi(e.contents);let t=new Yr;return`(WTF8 string): ${JSON.stringify(t.decode(e.contents))}`},Q.registerDecoder(X.WTF8,ca),Q.registerDecoder(X.SELF_DESCRIBED,e=>e.contents,`Self-Described`),Q.registerDecoder(X.INVALID_16,()=>{throw Error(`Tag always invalid: ${X.INVALID_16}`)},`Invalid`),Q.registerDecoder(X.INVALID_32,()=>{throw Error(`Tag always invalid: ${X.INVALID_32}`)},`Invalid`),Q.registerDecoder(X.INVALID_64,()=>{throw Error(`Tag always invalid: ${X.INVALID_64}`)},`Invalid`),Q.registerDecoder(X.SYMBOL,e=>{let t=e.contents;if(Array.isArray(e.contents)){if(e.contents.length!==1)throw Error(`Expected Array of size 1: ${e.contents}`);[t]=e.contents}if(Yi(t),!t.length)throw Error(`Expected non-empty string: ${e.contents}`);return Symbol.for(t)},`Symbol`);function la(e){throw Error(`Encoding ${e.constructor.name} intentionally unimplmented. It is not concrete enough to interoperate. Convert to Uint8Array first.`)}$(ArrayBuffer,la),$(DataView,la),typeof SharedArrayBuffer<`u`&&$(SharedArrayBuffer,la);function ua(e){return[NaN,e.valueOf()]}$(Boolean,ua),$(Number,ua),$(String,ua),$(BigInt,ua);function da(e){let t={...Ni.defaultDecodeOptions};if(e.dcbor?Object.assign(t,Ni.dcborDecodeOptions):e.cde&&Object.assign(t,Ni.cdeDecodeOptions),Object.assign(t,e),Object.hasOwn(t,`rejectLongNumbers`))throw TypeError(`rejectLongNumbers has changed to requirePreferred`);return t.boxed&&(t.saveOriginal=!0),t}var fa=class{parent=void 0;ret=void 0;step(e,t,n){if(this.ret=Ni.create(e,this.parent,t,n),e[2]===Kn.BREAK)if(this.parent?.isStreaming)this.parent.left=0;else throw Error(`Unexpected BREAK`);else this.parent&&this.parent.push(this.ret,n,e[3]);for(this.ret instanceof Ni&&(this.parent=this.ret);this.parent?.done;){this.ret=this.parent.convert(n);let e=this.parent.parent;e?.replaceLast(this.ret,this.parent,n),this.parent=e}}};function pa(e,t={}){let n=da(t),r=new ki(e,n),i=new fa;for(let e of r)i.step(e,n,r);return i.ret}var{cdeDecodeOptions:ma,dcborDecodeOptions:ha,defaultDecodeOptions:ga}=Ni,_a=class{constructor(e={}){this.options={id:R(),codec:`cbor`,timeout:10*1e3,share:``,chunkSize:16*1024,chunkCredit:4,topicMake:(e,t,n)=>`${e}/${t}/${n??`any`}`,topicMatch:e=>{let t=e.match(/^(.+)\/([^/]+)\/([^/]+)$/);return t?{name:t[1],operation:t[2],peerId:t[3]===`any`?void 0:t[3]}:null},...e}}},va=class{static uint8ArrayToBase64(e){return xe.from(e.buffer,e.byteOffset,e.byteLength).toString(`base64`)}static base64ToUint8Array(e){return new Uint8Array(xe.from(e,`base64`))}static stringify(e){return JSON.stringify(e,(e,t)=>t instanceof Uint8Array?{__Uint8Array:this.uint8ArrayToBase64(t)}:t)}static parse(e){return JSON.parse(e,(e,t)=>typeof t?.__Uint8Array==`string`?this.base64ToUint8Array(t.__Uint8Array):t)}},ya=class{constructor(e){this.format=e,this.types=new dr,this.tags=new Map;let t=64e3;this.types.registerEncoder(xe,e=>[t,new Uint8Array(e.buffer,e.byteOffset,e.byteLength)]),this.tags.set(t,e=>xe.from(e.contents))}encode(e){let t;if(this.format===`cbor`)try{t=Ti(e,{types:this.types})}catch(e){throw Error(`failed to encode CBOR format`,{cause:e})}else if(this.format===`json`)try{t=va.stringify(e)}catch(e){throw Error(`failed to encode JSON format`,{cause:e})}else throw Error(`invalid format "${this.format}"`);return t}decode(e){let t;if(this.format===`cbor`){if(!(e instanceof Uint8Array))throw Error(`failed to decode CBOR format (data type is not Uint8Array)`);if(e.byteLength===0)throw Error(`failed to decode CBOR format (data is empty)`);try{t=pa(e,{tags:this.tags})}catch(e){throw Error(`failed to decode CBOR format`,{cause:e})}}else if(this.format===`json`){if(typeof e!=`string`)throw Error(`failed to decode JSON format (data type is not string)`);if(e.length===0)throw Error(`failed to decode JSON format (data is empty)`);try{t=va.parse(e)}catch(e){throw Error(`failed to decode JSON format`,{cause:e})}}else throw Error(`invalid format "${this.format}"`);return t}},ba=class extends _a{constructor(e={}){super(e),this.codec=new ya(this.options.codec)}},xa=class e extends ba{static{this.encoder=new TextEncoder}static{this.decoder=new TextDecoder}str2buf(t){return e.encoder.encode(t)}buf2str(t){return e.decoder.decode(t)}arr2buf(e){let t;return t=e instanceof Uint8Array?e:new Uint8Array(e.buffer,e.byteOffset,e.byteLength),t}buf2arr(e,t){let n,r=t;if(r===xe)n=xe.from(e.buffer,e.byteOffset,e.byteLength);else if(r===Uint8Array)n=e;else if(r===Int8Array)n=new Int8Array(e.buffer,e.byteOffset,e.byteLength);else throw Error(`invalid data type`);return n}},Sa=e=>{let t=e.match(/^(\d+)\.(\d+)$/);if(t===null)throw Error(`invalid version string`);let n=parseInt(t[2],10);if(n>99)throw Error(`invalid version string: minor version exceeds 99`);return parseInt(t[1],10)*100+n},Ca=Sa(`1.4`),wa=Sa(`1.4`),Ta=Un(Bn(J(),Hn()),Dn(e=>!Array.isArray(e))),Ea=Un(Pn(Un(J(),kn(8192))),kn(8)),Da=class{constructor(e,t,n,r){this.type=e,this.id=t,this.sender=n,this.receiver=r,this.version=`MQTT+/1.4`}},Oa={version:Un(J(),jn(/^MQTT\+\/\d+\.\d+$/)),type:J(),id:J(),sender:q(J()),receiver:q(J())},ka=class extends Da{constructor(e,t,n,r,i,a,o){super(`event-emission`,e,r,i),this.name=t,this.params=n,this.auth=a,this.meta=o}},Aa=Vn({...Oa,type:Ln(`event-emission`),name:J(),params:q(Un(Pn(Hn()),kn(64))),auth:q(Ea),meta:q(Ta)}),ja=class extends Da{constructor(e,t,n,r,i,a,o,s){super(`service-call-request`,e,r,i),this.name=t,this.params=n,this.auth=a,this.meta=o,this.qos=s}},Ma=Vn({...Oa,type:Ln(`service-call-request`),name:J(),params:q(Un(Pn(Hn()),kn(64))),auth:q(Ea),meta:q(Ta),qos:q(zn([0,1,2]))}),Na=class extends Da{constructor(e,t,n,r,i,a){super(`service-call-response`,e,i,a),this.name=t,this.result=n,this.error=r}},Pa=Vn({...Oa,type:Ln(`service-call-response`),name:J(),result:q(Hn()),error:q(J())}),Fa=class extends Da{constructor(e,t,n,r,i,a,o,s){super(`sink-push-request`,e,r,i),this.name=t,this.params=n,this.auth=a,this.meta=o,this.qos=s}},Ia=Vn({...Oa,type:Ln(`sink-push-request`),name:J(),params:q(Un(Pn(Hn()),kn(64))),auth:q(Ea),meta:q(Ta),qos:q(zn([0,1,2]))}),La=class extends Da{constructor(e,t,n,r,i,a){super(`sink-push-response`,e,r,i),this.name=t,this.error=n,this.credit=a}},Ra=Vn({...Oa,type:Ln(`sink-push-response`),name:J(),error:q(J()),credit:q(Un(Rn(),On(),An(1)))}),za=class extends Da{constructor(e,t,n,r,i,a,o){super(`sink-push-chunk`,e,a,o),this.name=t,this.chunk=n,this.error=r,this.final=i}},Ba=Vn({...Oa,type:Ln(`sink-push-chunk`),name:J(),chunk:q(In(Uint8Array)),error:q(J()),final:q(Fn())}),Va=class extends Da{constructor(e,t,n,r,i){super(`sink-push-credit`,e,r,i),this.name=t,this.credit=n}},Ha=Vn({...Oa,type:Ln(`sink-push-credit`),name:J(),credit:Un(Rn(),On(),An(0))}),Ua=class extends Da{constructor(e,t,n,r,i,a,o,s,c){super(`source-fetch-request`,e,r,i),this.name=t,this.params=n,this.auth=a,this.meta=o,this.credit=s,this.qos=c}},Wa=Vn({...Oa,type:Ln(`source-fetch-request`),name:J(),params:q(Un(Pn(Hn()),kn(64))),auth:q(Ea),meta:q(Ta),credit:q(Un(Rn(),On(),An(1))),qos:q(zn([0,1,2]))}),Ga=class extends Da{constructor(e,t,n,r,i,a){super(`source-fetch-response`,e,r,i),this.name=t,this.error=n,this.meta=a}},Ka=Vn({...Oa,type:Ln(`source-fetch-response`),name:J(),error:q(J()),meta:q(Ta)}),qa=class extends Da{constructor(e,t,n,r,i,a,o){super(`source-fetch-chunk`,e,a,o),this.name=t,this.chunk=n,this.error=r,this.final=i}},Ja=Vn({...Oa,type:Ln(`source-fetch-chunk`),name:J(),chunk:q(In(Uint8Array)),error:q(J()),final:q(Fn())}),Ya=class extends Da{constructor(e,t,n,r,i){super(`source-fetch-credit`,e,r,i),this.name=t,this.credit=n}},Xa=Vn({...Oa,type:Ln(`source-fetch-credit`),name:J(),credit:Un(Rn(),On(),An(0))}),Za=class{makeEventEmission(e,t,n,r,i,a,o){return new ka(e,t,n,r,i,a,o)}makeServiceCallRequest(e,t,n,r,i,a,o,s){return new ja(e,t,n,r,i,a,o,s)}makeServiceCallResponse(e,t,n,r,i,a){return new Na(e,t,n,r,i,a)}makeSinkPushRequest(e,t,n,r,i,a,o,s){return new Fa(e,t,n,r,i,a,o,s)}makeSinkPushResponse(e,t,n,r,i,a){return new La(e,t,n,r,i,a)}makeSinkPushChunk(e,t,n,r,i,a,o){return new za(e,t,n,r,i,a,o)}makeSinkPushCredit(e,t,n,r,i){return new Va(e,t,n,r,i)}makeSourceFetchRequest(e,t,n,r,i,a,o,s,c){return new Ua(e,t,n,r,i,a,o,s,c)}makeSourceFetchResponse(e,t,n,r,i,a){return new Ga(e,t,n,r,i,a)}makeSourceFetchChunk(e,t,n,r,i,a,o){return new qa(e,t,n,r,i,a,o)}makeSourceFetchCredit(e,t,n,r,i){return new Ya(e,t,n,r,i)}parse(e){if(typeof e!=`object`||!e)throw Error(`invalid argument: not an object`);if(typeof e.version!=`string`)throw Error(`invalid object: missing or invalid "version" field`);let t=e.version.match(/^MQTT\+\/(\d+\.\d+)$/),n=t===null?0:Sa(t[1]);if(Math.floor(n/100)!==Math.floor(Ca/100)||n<wa)throw Error(`protocol version mismatch (expected version 1.4...1.4, got version ${e.version})`);let r=(e,t,n)=>{let r=Wn(n,e);if(!r.success){let e=r.issues.map(e=>e.message).join(`; `);throw Error(`invalid ${t} object: ${e}`)}return r.output};if(typeof e.type!=`string`)throw Error(`invalid object: missing or invalid "type" field`);if(e.type===`event-emission`){let t=r(e,`EventEmission`,Aa);return this.makeEventEmission(t.id,t.name,t.params,t.sender,t.receiver,t.auth,t.meta)}else if(e.type===`service-call-request`){let t=r(e,`ServiceCallRequest`,Ma);return this.makeServiceCallRequest(t.id,t.name,t.params,t.sender,t.receiver,t.auth,t.meta,t.qos)}else if(e.type===`service-call-response`){let t=r(e,`ServiceCallResponse`,Pa);return this.makeServiceCallResponse(t.id,t.name,t.result,t.error,t.sender,t.receiver)}else if(e.type===`sink-push-request`){let t=r(e,`SinkPushRequest`,Ia);return this.makeSinkPushRequest(t.id,t.name,t.params,t.sender,t.receiver,t.auth,t.meta,t.qos)}else if(e.type===`sink-push-response`){let t=r(e,`SinkPushResponse`,Ra);return this.makeSinkPushResponse(t.id,t.name,t.error,t.sender,t.receiver,t.credit)}else if(e.type===`sink-push-chunk`){let t=r(e,`SinkPushChunk`,Ba);return this.makeSinkPushChunk(t.id,t.name,t.chunk,t.error,t.final,t.sender,t.receiver)}else if(e.type===`sink-push-credit`){let t=r(e,`SinkPushCredit`,Ha);return this.makeSinkPushCredit(t.id,t.name,t.credit,t.sender,t.receiver)}else if(e.type===`source-fetch-request`){let t=r(e,`SourceFetchRequest`,Wa);return this.makeSourceFetchRequest(t.id,t.name,t.params,t.sender,t.receiver,t.auth,t.meta,t.credit,t.qos)}else if(e.type===`source-fetch-response`){let t=r(e,`SourceFetchResponse`,Ka);return this.makeSourceFetchResponse(t.id,t.name,t.error,t.sender,t.receiver,t.meta)}else if(e.type===`source-fetch-chunk`){let t=r(e,`SourceFetchChunk`,Ja);return this.makeSourceFetchChunk(t.id,t.name,t.chunk,t.error,t.final,t.sender,t.receiver)}else if(e.type===`source-fetch-credit`){let t=r(e,`SourceFetchCredit`,Xa);return this.makeSourceFetchCredit(t.id,t.name,t.credit,t.sender,t.receiver)}else throw Error(`invalid object: not of any known type`)}isRequest(e){return e instanceof ka||e instanceof ja||e instanceof Ua||e instanceof Fa}isResponse(e){return e instanceof Na||e instanceof La||e instanceof za||e instanceof Va||e instanceof Ga||e instanceof qa||e instanceof Ya}},Qa=class extends xa{constructor(){super(...arguments),this.msg=new Za}},$a=class{constructor(e,t,n,r){this.timestamp=e,this.level=t,this.msg=n,this.data=r}async resolve(){if(this.msg instanceof Promise&&(this.msg=await this.msg.catch(()=>`<resolve-failed>`)),this.data)for(let e of Object.keys(this.data))this.data[e]instanceof Promise&&(this.data[e]=await this.data[e].catch(()=>`<resolve-failed>`))}toString(){let e=new Date(this.timestamp),t=`${e.getFullYear()}-${(e.getMonth()+1).toString().padStart(2,`0`)}-${e.getDate().toString().padStart(2,`0`)} ${e.getHours().toString().padStart(2,`0`)}:${e.getMinutes().toString().padStart(2,`0`)}:${e.getSeconds().toString().padStart(2,`0`)}.${e.getMilliseconds().toString().padStart(3,`0`)}`,n=this.msg instanceof Promise?`<unresolved>`:this.msg,r=``;if(this.data!==void 0){let e=this.data;r=` (${Object.keys(e).map(t=>{let n=e[t]instanceof Promise?`<unresolved>`:e[t];return`${t}: ${va.stringify(n)}`}).join(`, `)})`}return`[${t}] ${this.level}: ${n}${r}`}},eo=class extends Qa{constructor(){super(...arguments),this._events=new pn.EventEmitter}on(...e){this._events.on(...e)}off(...e){this._events.off(...e)}emitEvent(...e){return this._events.listenerCount(e[0])===0?!1:this._events.emit(...e)}log(e,t,n){let r=new $a(Date.now(),e,t,n);this.emitEvent(`log`,r)}error(e,t){let n=e;t!==void 0&&(n=Error(`${t}: ${e.message}`,{cause:e}),n.stack=e.stack);try{this.emitEvent(`error`,n),this.log(`error`,n.message)}catch{}}},to=class extends eo{constructor(e,t={}){super(t),this.destroyed=!1,this.onRequest=new Map,this.onResponse=new Map,e===null&&(this.log(`info`,`establishing proxy MQTT client`),e=new Proxy({},{get(e,t,n){return t===`isFakeProxy`?!0:typeof t==`string`&&[`on`,`off`,`once`].includes(t)?()=>{}:()=>{throw Error(`Underlying MQTT operation "${String(t)}" called on a null MQTT client -- only MQTT+ "emit({ ..., dry: true })" is supported in this special operation mode`)}}})),this.mqtt=e,this.log(`info`,`hooking into MQTT client`),this.messageHandler=(e,t,n)=>{if(this.destroyed)return;let r;if(this.options.codec===`json`)r=t.toString();else if(this.options.codec===`cbor`)r=be.isBuffer(t)?new Uint8Array(t.buffer,t.byteOffset,t.byteLength):t;else throw Error(`invalid codec configured`);this._onMessage(e,r,n)},this.mqtt.on(`message`,this.messageHandler)}async destroy(){this.destroyed=!0,this.log(`info`,`un-hooking from MQTT client`),this.mqtt.off(`message`,this.messageHandler),this.onRequest.clear(),this.onResponse.clear()}makeRegistration(e,t,n){let r=!1;return{destroy:async()=>{r||(r=!0,await e.unroll(!1)?.catch(e=>{let r=G(e,`destroy: ${t} "${n}" failed to cleanup`);throw this.error(r),r}))}}}async subscribeTopic(e,t={}){return this.log(`info`,`subscribing to MQTT topic "${e}"`),new Promise((n,r)=>{this.mqtt.subscribe(e,{qos:2,...t},(t,i)=>{t?(this.error(t,`subscribing to MQTT topic "${e}" failed`),r(t)):n()})})}async unsubscribeTopic(e){return this.log(`info`,`unsubscribing from MQTT topic "${e}"`),new Promise((t,n)=>{this.mqtt.unsubscribe(e,(r,i)=>{r?(this.error(r,`unsubscribing from MQTT topic "${e}" failed`),n(r)):t()})})}async publishToTopic(e,t,n={}){typeof t==`string`?this.log(`info`,`publishing to MQTT topic "${e}" (type: string, length: ${t.length} chars)`):this.log(`info`,`publishing to MQTT topic "${e}" (type: buffer, length: ${t.byteLength} bytes)`);let r=new Se((e,n)=>{let r;try{let e=this.codec.decode(t);r=this.msg.parse(e)}catch(e){return n(e)}e(r)});return this.log(`debug`,`publishing to MQTT topic "${e}"`,{message:r}),new Promise((r,i)=>{let a=typeof t==`string`?t:be.from(t.buffer,t.byteOffset,t.byteLength);this.mqtt.publish(e,a,n,t=>{t?(this.error(t,`publishing to MQTT topic "${e}" failed`),i(t)):r()})})}_onMessage(e,t,n){if(this.destroyed)return;let r=this.options.topicMatch(e);if(r===null)return;typeof t==`string`?this.log(`info`,`received from MQTT topic "${e}" (type: string, length: ${t.length} chars)`):this.log(`info`,`received from MQTT topic "${e}" (type: buffer, length: ${t.byteLength} bytes)`);let i;try{i=this.codec.decode(t)}catch(e){this.error(G(e,`failed to parse message into object`));return}let a;try{a=this.msg.parse(i)}catch(e){this.error(G(e,`failed to parse object into typed message object`));return}if(this.log(`debug`,`received from MQTT topic "${e}"`,{message:a}),r.peerId!==void 0&&a.receiver!==void 0&&a.receiver!==r.peerId&&this.log(`warning`,`receiver mismatch on direct topic "${e}" (expected "${r.peerId}", got "${a.receiver}")`),this.msg.isRequest(a)){let t=this.onRequest.get(`${a.type}:${a.name}`);t!==void 0&&Promise.resolve().then(()=>{if(!this.destroyed)return t(a,r.name)}).catch(t=>{this.error(G(t,`dispatching request message from MQTT topic "${e}" (type: ${a.type}, id: ${a.id}, name: ${a.name}) failed`))})}else if(this.msg.isResponse(a)){let t=this.onResponse.get(`${a.type}:${a.id}`);t!==void 0&&Promise.resolve().then(()=>{if(!this.destroyed)return t(a,r.name)}).catch(t=>{this.error(G(t,`dispatching response message from MQTT topic "${e}" (type: ${a.type}, id: ${a.id}, name: ${a.name}) failed`))})}}},no=class{constructor(e,t,n=30*1e3){this.subscribeFn=e,this.unsubscribeFn=t,this.lingerMs=n,this.counts=new Map,this.pending=new Map,this.lingers=new Map,this.unsubbing=new Map}incrementCount(e){let t=this.counts.get(e)??0;return this.counts.set(e,t+1),t}decrementCount(e){let t=this.counts.get(e);if(t!==void 0)return t<=1?(this.counts.delete(e),0):(this.counts.set(e,t-1),t-1)}clearCount(e){this.counts.delete(e)}async subscribe(e,t={qos:2}){if(this.incrementCount(e)===0){let n=this.lingers.get(e);if(n){clearTimeout(n),this.lingers.delete(e);return}let r,i,a=new Promise((e,t)=>{r=e,i=t});a.catch(()=>{}),this.pending.set(e,a);let o=this.unsubbing.get(e);return o&&await o,this.subscribeFn(e,t).then(()=>{this.pending.delete(e),r()},t=>{this.pending.delete(e),this.clearCount(e),i(t)}),a}else{let t=this.pending.get(e);if(t)return t}}async unsubscribe(e){if(this.decrementCount(e)===0)if(this.lingerMs>0){let t=setTimeout(()=>{this.lingers.delete(e);let t=this.unsubscribeFn(e).catch(()=>{}).finally(()=>{this.unsubbing.delete(e)});this.unsubbing.set(e,t)},this.lingerMs);this.lingers.set(e,t)}else{let t=this.unsubscribeFn(e).catch(()=>{}).finally(()=>{this.unsubbing.delete(e)});this.unsubbing.set(e,t),await t}}async flush(){let e=new Set([...this.counts.keys(),...this.lingers.keys(),...this.pending.keys(),...this.unsubbing.keys()]);for(let e of this.lingers.values())clearTimeout(e);this.lingers.clear(),this.counts.clear(),await Promise.allSettled([...this.pending.values(),...this.unsubbing.values()]),await Promise.allSettled([...e].map(e=>this.unsubscribeFn(e).catch(()=>{}))),this.pending.clear(),this.unsubbing.clear()}},ro=class extends to{constructor(){super(...arguments),this.subscriptions=new no((e,t)=>this.subscribeTopic(e,t),e=>this.unsubscribeTopic(e))}async subscribeTopicAndSpool(e,t,n={}){await Pe(`subscribe to MQTT topic "${t}"`,e,()=>this.subscriptions.subscribe(t,{qos:2,...n})),e.roll(()=>this.subscriptions.unsubscribe(t))}async destroy(){await this.subscriptions.flush(),await super.destroy()}},io=class extends ro{constructor(){super(...arguments),this.timers=new Map}async destroy(){for(let e of this.timers.values())clearTimeout(e);this.timers.clear(),await super.destroy()}timerRefresh(e,t){let n=this.timers.get(e);n!==void 0&&clearTimeout(n),this.timers.set(e,setTimeout(async()=>{if(!this.destroyed){this.timers.delete(e);try{await t()}catch(t){this.error(G(t),`timer "${e}" failed`)}}},this.options.timeout))}timerClear(e){let t=this.timers.get(e);t!==void 0&&(clearTimeout(t),this.timers.delete(e))}sleep(e,t){return new Promise(n=>{if(t?.aborted){n();return}let r=t===void 0?void 0:new AbortController,i=setTimeout(()=>{i=null,r!==void 0&&r.abort(),n()},e);i.unref(),t!==void 0&&r!==void 0&&t.addEventListener(`abort`,()=>{i!==null&&clearTimeout(i),n()},{once:!0,signal:r.signal})})}timeout(e,t=`timeout`,n){return new Promise((r,i)=>{if(n?.aborted){i(Error(`aborted`));return}let a=n===void 0?void 0:new AbortController,o=setTimeout(()=>{o=null,a!==void 0&&a.abort(),i(Error(t))},e);o.unref(),n!==void 0&&a!==void 0&&n.addEventListener(`abort`,()=>{o!==null&&clearTimeout(o),i(Error(`aborted`))},{once:!0,signal:a.signal})})}},ao=class extends io{constructor(){super(...arguments),this._meta=new Map}meta(...e){let[t,n]=e;if(e.length===0)return Object.fromEntries(this._meta);if(e.length===1)return this._meta.get(t);n==null?this._meta.delete(t):this._meta.set(t,n)}metaStore(e){let t=e===void 0||Object.keys(e).length===0;if(!(this._meta.size===0&&t))return this._meta.size>0&&t?Object.fromEntries(this._meta):this._meta.size===0&&!t?e:{...Object.fromEntries(this._meta),...e}}},oo=new TextEncoder,so=class extends ao{constructor(){super(...arguments),this._credential=null,this._tokens=new Set}credential(e){if(e.length===0)throw Error(`credential must not be empty`);this._credential=ln(un,oo.encode(e),oo.encode(`mqtt-plus`),6e5,32)}async issue(e){if(this._credential===null)throw Error(`credential has to be provided before issuing tokens`);if(e.roles.length===0)throw Error(`payload.roles must be a non-empty array`);if(e.roles.length>64)throw Error(`payload.roles must not exceed 64 roles`);let t=new Qt(e);return t.setProtectedHeader({alg:`HS256`,typ:`JWT`}),await t.sign(this._credential)}authenticate(e,t){if(e===void 0){let e=Array.from(this._tokens);return e.length>0?e:void 0}else if(t===!0)this._tokens.delete(e);else{if(e.length>8192)throw Error(`token must not exceed 8192 characters`);if(!this._tokens.has(e)&&this._tokens.size>=8)throw Error(`at most 8 tokens can be authenticated at once`);this._tokens.add(e)}}async validateToken(e){if(this._credential===null)throw Error(`credential has to be provided before validating tokens`);try{return(await nn(e,this._credential)).payload}catch(e){let t=e?.code??e?.name??`unknown`,n=e?.message??``;return this.log(`warning`,`token validation failed`,{code:t,reason:n}),null}}async authenticated(e,t,n,r){let i=!1,a=typeof n==`string`?[n]:n.roles;if(t!==void 0)for(let n of t.slice(0,8)){if(n.length>8192)continue;let t=await this.validateToken(n);if(t!==null&&!(t.id&&t.id!==e)&&Array.isArray(t.roles)&&!(t.roles.length>64)){for(let e of a)if(t.roles.includes(e)){i=!0;break}if(i)break}}let o=typeof n==`string`||n.mode===`require`;if(!i&&o)throw Error(`${r} failed authentication`);return i}},co=class extends so{constructor(){super(...arguments),this.eventControllers=new Map}async destroy(){for(let e of this.eventControllers.values())e.abort(Error(`event destroyed`));this.eventControllers.clear(),await super.destroy()}async event(e,...t){if(this.destroyed)throw Error(`event: instance already destroyed`);let n,r,i={},a=this.options.share,o;if(typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.callback,i=e.options??{},a=e.share??this.options.share,o=e.auth):(n=e,r=t[0]),typeof r!=`function`)throw Error(`event: callback argument is required and must be a function`);let s=new je;if(this.onRequest.has(`event-emission:${n}`))throw Error(`event: event "${n}" already registered`);let c=new Set;s.roll(()=>{for(let e of c)this.eventControllers.get(e)?.abort(Error(`event "${n}" destroyed`)),this.eventControllers.delete(e);c.clear()});let l=a===``?n:`$share/${a}/${n}`,u=this.options.topicMake(l,`event-emission`),d=this.options.topicMake(n,`event-emission`,this.options.id);return this.onRequest.set(`event-emission:${n}`,async(e,t)=>{if(e.receiver&&e.receiver!==this.options.id)return;let i=e.id,a=e.sender;if(a===void 0||a===``){this.error(Error(`invalid request: missing sender`));return}if(t!==e.name){this.log(`warning`,`event name mismatch -- dropped request for "${n}" (topic: "${t}", payload: "${e.name}")`,{requestId:i});return}let s=e.params??[];if(this.eventControllers.has(i)){this.log(`warning`,`duplicate event request id -- dropped request for event "${n}"`,{requestId:i});return}let l=new AbortController,u=l.signal;c.add(i),this.eventControllers.set(i,l);let d={sender:a,signal:u};e.receiver&&(d.receiver=e.receiver),e.meta&&(d.meta=e.meta);try{o&&(d.authenticated=await this.authenticated(a,e.auth,o,`event "${n}"`)),await r(...s,d)}catch(e){let t=G(e);this.error(t,`handler for event "${n}" failed`)}finally{l.abort(),c.delete(i),this.eventControllers.delete(i)}}),s.roll(()=>{this.onRequest.delete(`event-emission:${n}`)}),await this.subscribeTopicAndSpool(s,u,i),await this.subscribeTopicAndSpool(s,d,i),this.makeRegistration(s,`event`,n)}emit(e,...t){if(this.destroyed)throw Error(`emit: instance already destroyed`);let n,r,i,a={},o,s;typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.params,i=e.receiver,a=e.options??{},o=e.meta,s=e.dry):(n=e,r=t);let c=R(),l=this.authenticate(),u=this.metaStore(o),d=this.msg.makeEventEmission(c,n,r,this.options.id,i,l,u),f=this.codec.encode(d),p=this.options.topicMake(n,`event-emission`,i);return s?{topic:p,payload:f,options:{qos:2,...a}}:Pe(`publish event as MQTT message to topic "${p}"`,()=>this.publishToTopic(p,f,{qos:2,...a}))}},lo=class extends co{constructor(){super(...arguments),this.serviceControllers=new Map,this.pendingCalls=new Map}async destroy(){for(let e of this.serviceControllers.values())e.abort(Error(`service destroyed`));this.serviceControllers.clear();let e=[...this.pendingCalls.values()];this.pendingCalls.clear();for(let t of e)t(Error(`instance destroyed`));await super.destroy()}async service(e,...t){if(this.destroyed)throw Error(`service: instance already destroyed`);let n,r,i={},a=this.options.share,o;if(typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.callback,i=e.options??{},a=e.share??this.options.share,o=e.auth):(n=e,r=t[0]),typeof r!=`function`)throw Error(`service: callback argument is required and must be a function`);let s=new je;if(this.onRequest.has(`service-call-request:${n}`))throw Error(`service: service "${n}" already registered`);let c=new Set;s.roll(()=>{for(let e of c)this.serviceControllers.get(e)?.abort(Error(`service "${n}" destroyed`)),this.serviceControllers.delete(e);c.clear()});let l=a===``?n:`$share/${a}/${n}`,u=this.options.topicMake(l,`service-call-request`),d=this.options.topicMake(n,`service-call-request`,this.options.id);return this.onRequest.set(`service-call-request:${n}`,async(e,t)=>{if(e.receiver&&e.receiver!==this.options.id)return;let i=e.id,a=e.sender,s=e.params??[];if(a===void 0||a===``){this.error(Error(`invalid request: missing sender`));return}if(t!==e.name){this.log(`warning`,`service name mismatch -- dropped request for "${n}" (topic: "${t}", payload: "${e.name}")`,{requestId:i});return}if(this.serviceControllers.has(i)){this.log(`warning`,`duplicate service request id -- dropped request for "${n}"`,{requestId:i});return}let l=new AbortController,u=l.signal;c.add(i),this.serviceControllers.set(i,l);let d={sender:a,signal:u};e.receiver&&(d.receiver=e.receiver),e.meta&&(d.meta=e.meta);let f=`service-call-handler:${i}`,p=()=>{this.timerRefresh(f,()=>{l.abort(Error(`service "${n}" handler timeout`))})},m=()=>this.timerClear(f);p();let h;try{o&&(d.authenticated=await this.authenticated(a,e.auth,o,`service "${n}"`));let t=new Promise((e,t)=>{let n=()=>{t(G(u.reason))};u.aborted?n():(u.addEventListener(`abort`,n,{once:!0}),h=()=>u.removeEventListener(`abort`,n))}),c=await Promise.race([r(...s,d),t]),l=this.msg.makeServiceCallResponse(i,n,c,void 0,this.options.id,a),f=this.codec.encode(l),p=this.options.topicMake(n,`service-call-response`,a);await this.publishToTopic(p,f,{qos:e.qos??2})}catch(t){let r=G(t);this.error(r,`handler for service "${n}" failed`);let o=this.msg.makeServiceCallResponse(i,n,void 0,r.message,this.options.id,a);try{let t=this.codec.encode(o),r=this.options.topicMake(n,`service-call-response`,a);await this.publishToTopic(r,t,{qos:e.qos??2})}catch(e){this.error(G(e),`sending error response for service "${n}" failed`)}}finally{h?.(),m(),l.abort(),c.delete(i),this.serviceControllers.delete(i)}}),s.roll(()=>{this.onRequest.delete(`service-call-request:${n}`)}),await this.subscribeTopicAndSpool(s,u,i),await this.subscribeTopicAndSpool(s,d,i),this.makeRegistration(s,`service`,n)}async call(e,...t){if(this.destroyed)throw Error(`call: instance already destroyed`);let n,r,i,a={},o;typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.params,i=e.receiver,a=e.options??{},o=e.meta):(n=e,r=t);let s=new je,c=R();for(let e=0;e<10&&this.onResponse.has(`service-call-response:${c}`);e++)c=R();if(this.onResponse.has(`service-call-response:${c}`))throw Error(`failed to generate unique request id`);let l=this.options.topicMake(n,`service-call-response`,this.options.id);await this.subscribeTopicAndSpool(s,l,{qos:a.qos??2});let u=`service-call:${c}`,d,f=!1,p=()=>f?!1:(f=!0,s.unroll(),!0),m=new Promise((e,t)=>{d=t,this.timerRefresh(u,()=>{p()&&t(Error(`communication timeout`))}),s.roll(()=>{this.timerClear(u)}),this.pendingCalls.set(c,e=>{p()&&t(e)}),s.roll(()=>{this.pendingCalls.delete(c)}),this.onResponse.set(`service-call-response:${c}`,r=>{if(!(i!==void 0&&r.sender!==i)){if(r.sender===void 0||r.sender===``){if(!p())return;t(Error(`received service response without sender`));return}if(r.name!==n){if(!p())return;t(Error(`received service response with name mismatch (expected: "${n}", received: "${r.name}")`));return}p()&&(r.error===void 0?e(r.result):t(Error(r.error)))}}),s.roll(()=>{this.onResponse.delete(`service-call-response:${c}`)})}),h=this.authenticate(),g=this.metaStore(o),_=a.qos??2,v=this.msg.makeServiceCallRequest(c,n,r,this.options.id,i,h,g,_),y=this.codec.encode(v),b=this.options.topicMake(n,`service-call-request`,i);try{await Pe(`publish service request as MQTT message to topic "${b}"`,()=>this.publishToTopic(b,y,{...a,qos:_}))}catch(e){return p()&&d(e),m}return m}},uo=class extends lo{constructor(){super(...arguments),this.sourceCreditGates=new Map,this.sourceControllers=new Map,this.sourceSpools=new Map,this.sourceRequests=new Map}async destroy(){for(let e of this.sourceControllers.values())e.abort(Error(`source destroyed`));for(let e of this.sourceCreditGates.values())e.abort();for(let e of[...this.sourceSpools.values()])await e.unroll();this.sourceSpools.clear(),this.sourceControllers.clear(),this.sourceCreditGates.clear(),this.sourceRequests.clear(),await super.destroy()}async source(e,...t){if(this.destroyed)throw Error(`source: instance already destroyed`);let n,r,i={},a=this.options.share,o;if(typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.callback,i=e.options??{},a=e.share??this.options.share,o=e.auth):(n=e,r=t[0]),typeof r!=`function`)throw Error(`source: callback argument is required and must be a function`);let s=new je;if(this.onRequest.has(`source-fetch-request:${n}`))throw Error(`source: source "${n}" already registered`);let c=a===``?n:`$share/${a}/${n}`,l=this.options.topicMake(c,`source-fetch-request`),u=this.options.topicMake(n,`source-fetch-request`,this.options.id);return this.onRequest.set(`source-fetch-request:${n}`,async(e,t)=>{if(e.receiver&&e.receiver!==this.options.id)return;let a=e.id,s=e.params??[],c=e.sender,l=e.receiver,u=new je;if(this.sourceSpools.set(a,u),u.roll(()=>{this.sourceSpools.delete(a)}),c===void 0||c===``){this.error(Error(`invalid request: missing sender`)),await u.unroll();return}let d=this.options.topicMake(n,`source-fetch-response`,c),f=(e,t)=>Error(`${e} name mismatch (expected "${n}", got "${t}")`),p=async(t,r)=>{let o=this.metaStore(r),s=this.msg.makeSourceFetchResponse(a,n,t,this.options.id,c,o),l=this.codec.encode(s);await this.publishToTopic(d,l,{qos:e.qos??i.qos??2})},m=new AbortController,h=m.signal;if(this.sourceControllers.has(a)){let e=Error(`source: duplicate request id "${a}"`);this.error(e),await p(e.message).catch(()=>{}),await u.unroll();return}this.sourceControllers.set(a,m),u.roll(()=>{this.sourceControllers.delete(a)});let g={sender:c,signal:h};l&&(g.receiver=l),e.meta&&(g.meta=e.meta);let _=this.sourceRequests.get(n);_||(_=new Set,this.sourceRequests.set(n,_)),_.add(a),u.roll(()=>{let e=this.sourceRequests.get(n);e===_&&(e.delete(a),e.size===0&&this.sourceRequests.delete(n))}),h.addEventListener(`abort`,()=>{g.stream instanceof L.Readable&&!g.stream.destroyed&&g.stream.destroy(G(h.reason))},{once:!0}),u.roll(()=>{h.aborted&&g.stream instanceof L.Readable&&!g.stream.destroyed&&g.stream.destroy(G(h.reason))});let v,y=new Promise((e,t)=>{v=t});y.catch(()=>{});let b=()=>{v(G(h.reason))};h.aborted?b():h.addEventListener(`abort`,b,{once:!0}),u.roll(()=>{h.removeEventListener(`abort`,b)});let x=`source-fetch-send:${a}`,S=()=>{h.aborted||this.timerRefresh(x,()=>{m.abort(Error(`source fetch "${n}" timed out`))})},C=()=>this.timerClear(x);S(),u.roll(()=>{C()});let w=async(t,r,o)=>{S();let s=this.msg.makeSourceFetchChunk(a,n,t,r,o,this.options.id,c),l=this.codec.encode(s);await this.publishToTopic(d,l,{qos:e.qos??i.qos??2})},T=!1,E,D=!1;try{if(t!==e.name)throw Error(`source name mismatch (topic: "${t}", payload: "${e.name}")`);if(o&&(g.authenticated=await this.authenticated(c,e.auth,o,`source "${n}"`)),this.onResponse.set(`source-fetch-credit:${a}`,e=>{if(!h.aborted){if(e.name!==n){m.abort(f(`source credit`,e.name));return}if(e.sender===void 0||e.sender===``){m.abort(Error(`source credit for "${n}" missing sender`));return}if(e.sender===c){if(e.credit===0){D=!0,m.abort(Error(`source fetch "${n}" cancelled by fetcher`));return}E&&(E.replenish(e.credit),S())}}}),u.roll(()=>{this.onResponse.delete(`source-fetch-credit:${a}`)}),await Promise.race([Promise.resolve(r(...s,g)),y]),!(g.stream instanceof L.Readable)&&!(g.buffer instanceof Promise)&&!(g.buffer instanceof Uint8Array))throw Error(`handler did not provide data via info.stream or info.buffer fields`);if(g.stream instanceof L.Readable&&(g.buffer instanceof Promise||g.buffer instanceof Uint8Array))throw Error(`handler has set both info.stream and info.buffer fields`);let i=e.credit;if(E=i!==void 0&&i>0?new Ce(i):void 0,E){let e=E;this.sourceCreditGates.set(a,e),u.roll(()=>{e.abort(),this.sourceCreditGates.delete(a)})}if(await p(void 0,g.meta),T=!0,g.stream instanceof L.Readable)await ke(g.stream,this.options.chunkSize,w,E,h);else if(g.buffer instanceof Promise||g.buffer instanceof Uint8Array){let e=g.buffer instanceof Promise?await Promise.race([g.buffer,y]):g.buffer;h.throwIfAborted(),await Oe(e,this.options.chunkSize,w,E,h)}}catch(e){let t=G(e,`handler for source "${n}" failed`);m.abort(t),D||(this.error(t),T?await w(void 0,t.message,!0).catch(()=>{}):await p(t.message).catch(()=>{}))}finally{await u.unroll()}}),s.roll(()=>{this.onRequest.delete(`source-fetch-request:${n}`)}),s.roll(()=>{let e=this.sourceRequests.get(n);if(e){for(let t of e){let e=this.sourceControllers.get(t);e&&e.abort(Error(`source "${n}" destroyed`));let r=this.sourceCreditGates.get(t);r&&r.abort()}this.sourceRequests.delete(n)}}),await this.subscribeTopicAndSpool(s,l,i),await this.subscribeTopicAndSpool(s,u,i),this.makeRegistration(s,`source`,n)}async fetch(e,...t){if(this.destroyed)throw Error(`fetch: instance already destroyed`);let n,r,i,a={},o;typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.params,i=e.receiver,a=e.options??{},o=e.meta):(n=e,r=t);let s=new je,c=R();for(let e=0;e<10&&(this.onResponse.has(`source-fetch-response:${c}`)||this.onResponse.has(`source-fetch-chunk:${c}`));e++)c=R();if(this.onResponse.has(`source-fetch-response:${c}`)||this.onResponse.has(`source-fetch-chunk:${c}`))throw Error(`failed to generate unique request id`);let l=this.options.topicMake(n,`source-fetch-response`,this.options.id),u=(e,t)=>Error(`${e} name mismatch (expected "${n}", got "${t}")`);await this.subscribeTopicAndSpool(s,l,{qos:a.qos??2});let d=this.options.chunkCredit,f=d>0?d*this.options.chunkSize:16*1024,p=0,m=d,h=i,g=!1,_=!1,v=[],y=(e,t)=>{if(t===void 0||t===``){_=!0;let t=Error(`received ${e} without sender`);return x(t),w?.destroy(t),s.unroll()?.catch(()=>{}),!1}if(h===void 0)h=t;else if(t!==h)return!1;return!0},b,x,S=new Promise((e,t)=>{b=e,x=t});S.catch(()=>{}),s.roll(()=>{b(void 0)});let C=`source-fetch-recv:${c}`,w,T=()=>{_||w&&w.destroyed||this.timerRefresh(C,()=>{let e=Error(`communication timeout`);x(e),w?.destroy(e)})};s.roll(()=>{this.timerClear(C)}),w=new De({highWaterMark:f,read:e=>{if(d<=0||_)return;let t=h;if(!t)return;let r=m-p,i=Math.max(0,f-(w?.readableLength??0)),o=Math.floor(i/this.options.chunkSize),s=Math.max(0,o-r);if(s>0){m+=s;let e=this.msg.makeSourceFetchCredit(c,n,s,this.options.id,t),r=this.codec.encode(e),i=this.options.topicMake(n,`source-fetch-request`,t);this.publishToTopic(i,r,{qos:a.qos??2}).catch(e=>{let t=G(e,`sending source fetch credit failed`);this.error(t),D(t)}),T()}}});let E=w.buffer;T();let D=e=>{_=!0,x(e),w.destroy(e)},O=!1,ee=e=>{if(!O&&!_){O=!0;let e=h;if(e){let t=this.msg.makeSourceFetchCredit(c,n,0,this.options.id,e),r=this.codec.encode(t),i=this.options.topicMake(n,`source-fetch-request`,e);this.publishToTopic(i,r,{qos:a.qos??2}).catch(e=>this.error(G(e,`sending source fetch cancel failed`)))}}_||x(e===void 0?Error(`stream aborted`):G(e)),s.unroll()?.catch(()=>{})};w.once(`close`,()=>ee()),w.once(`error`,e=>ee(e));let k=e=>{if(e.error)D(Error(e.error));else{if(T(),e.chunk!==void 0){if(d>0&&p>=m){D(Error(`flow control violation`));return}p++,w.destroyed||w.push(e.chunk)}e.final&&(_=!0,w.destroyed||w.push(null),s.unroll()?.catch(()=>{}))}};this.onResponse.set(`source-fetch-response:${c}`,e=>{if(!_){if(e.name!==n){D(u(`source response`,e.name));return}if(y(`source response`,e.sender))if(e.error)D(Error(e.error));else{if(g)return;g=!0,b(e.meta),T();for(let e of v){if(_)break;k(e)}v.length=0}}}),this.onResponse.set(`source-fetch-chunk:${c}`,e=>{if(!_){if(e.name!==n){D(u(`source chunk`,e.name));return}if(y(`source chunk`,e.sender)){if(!g){if(d>0&&v.length>=m){D(Error(`flow control violation`));return}v.push(e);return}k(e)}}}),s.roll(()=>{this.onResponse.delete(`source-fetch-response:${c}`),this.onResponse.delete(`source-fetch-chunk:${c}`)});let te=this.authenticate(),A=this.metaStore(o),ne=d>0?d:void 0,re=this.msg.makeSourceFetchRequest(c,n,r,this.options.id,i,te,A,ne,a.qos),ie=this.codec.encode(re),j=this.options.topicMake(n,`source-fetch-request`,i);await Pe(`publish fetch request as MQTT message to topic "${j}"`,()=>this.publishToTopic(j,ie,{qos:2,...a})).catch(e=>{let t=G(e);x(t),w.destroy(t)});let M={stream:w,buffer:E,meta:S};return Ae(M,`stream`,`buffer`,e=>{e===`stream`?w.stopCollecting():e===`buffer`&&w.resume()}),M}},fo=class extends uo{constructor(){super(...arguments),this.pushStreams=new Map,this.pushSpools=new Map,this.pushRecvControllers=new Map,this.destroying=!1,this.pushControllers=new Map,this.pushCreditGates=new Map,this.pushSenderSpools=new Map}async destroy(){this.destroying=!0;for(let e of this.pushSenderSpools.keys())this.timerClear(`sink-push-send:${e}`);for(let e of this.pushSpools.keys())this.timerClear(`sink-push-recv:${e}`);for(let e of this.pushControllers.values())e.abort(Error(`sink destroyed`));for(let e of this.pushCreditGates.values())e.abort();for(let e of[...this.pushSenderSpools.values()])await e.unroll();this.pushSenderSpools.clear(),this.pushControllers.clear(),this.pushCreditGates.clear();for(let e of this.pushRecvControllers.values())e.abort(Error(`sink destroyed`));this.pushRecvControllers.clear();for(let e of this.pushStreams.values())e.destroy(Error(`sink destroyed`));this.pushStreams.clear();for(let e of[...this.pushSpools.values()])await e.unroll();this.pushSpools.clear(),await super.destroy()}async sink(e,...t){if(this.destroyed)throw Error(`sink: instance already destroyed`);let n,r,i={},a=this.options.share,o;if(typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.callback,i=e.options??{},a=e.share??this.options.share,o=e.auth):(n=e,r=t[0]),typeof r!=`function`)throw Error(`sink: callback argument is required and must be a function`);let s=new je;if(this.onRequest.has(`sink-push-request:${n}`))throw Error(`sink: sink "${n}" already registered`);let c=a===``?n:`$share/${a}/${n}`,l=this.options.topicMake(c,`sink-push-request`),u=this.options.topicMake(n,`sink-push-request`,this.options.id);return this.onRequest.set(`sink-push-request:${n}`,async(e,t)=>{if(e.receiver&&e.receiver!==this.options.id)return;let a=e.id,s=e.params??[],c=e.sender,l=e.receiver,u=new je;if(this.pushSpools.set(a,u),u.roll(()=>{this.pushSpools.delete(a)}),c===void 0||c===``){this.error(Error(`invalid request: missing sender`)),await u.unroll();return}let d=this.options.topicMake(n,`sink-push-response`,c),f=e=>Error(`sink name mismatch (expected "${n}", got "${e}")`),p=this.options.chunkCredit,m=async(t,r=!1)=>{let o=t===void 0&&r&&p>0?p:void 0,s=this.msg.makeSinkPushResponse(a,n,t,this.options.id,c,o),l=this.codec.encode(s);await this.publishToTopic(d,l,{qos:e.qos??i.qos??2})},h=new AbortController,g=h.signal,_,v=new Promise((e,t)=>{_=t});v.catch(()=>{});let y=()=>{_(G(g.reason))};if(g.aborted?y():g.addEventListener(`abort`,y,{once:!0}),u.roll(()=>{g.removeEventListener(`abort`,y)}),this.pushRecvControllers.has(a)){let e=Error(`sink: duplicate request id "${a}"`);this.error(e),await m(e.message).catch(()=>{}),await u.unroll();return}this.pushRecvControllers.set(a,h),u.roll(()=>{this.pushRecvControllers.delete(a)});let b=!1,x=!1,S=!1,C,w=()=>{};try{if(t!==e.name)throw Error(`sink name mismatch (topic: "${t}", payload: "${e.name}")`);let _;o&&(_=await this.authenticated(c,e.auth,o,`sink "${n}"`));let y=p>0?{chunksReceived:0,creditGranted:p}:void 0,T=p>0?p*this.options.chunkSize:16*1024,E=!1,D=`sink-push-recv:${a}`,O=()=>this.timerRefresh(D,()=>{if(!(E||this.destroying)&&(h.abort(Error(`push stream timeout`)),c&&!S)){S=!0;let t=this.msg.makeSinkPushCredit(a,n,0,this.options.id,c),r=this.codec.encode(t);this.publishToTopic(d,r,{qos:e.qos??i.qos??2}).catch(()=>{})}}),ee=()=>this.timerClear(D),k=new De({highWaterMark:T,read:t=>{if(!y||!this.pushSpools.has(a))return;let r=y.creditGranted-y.chunksReceived,o=Math.max(0,T-k.readableLength),s=Math.floor(o/this.options.chunkSize),l=Math.max(0,s-r);if(l>0){y.creditGranted+=l;let t=this.msg.makeSinkPushCredit(a,n,l,this.options.id,c),r=this.codec.encode(t);this.publishToTopic(d,r,{qos:e.qos??i.qos??2}).catch(e=>{let t=G(e,`sending sink push credit failed`);this.error(t),k.destroy(t)}),O()}}});C=k,this.pushStreams.set(a,k),u.roll(()=>{this.pushStreams.delete(a)}),k.on(`error`,w),u.roll(()=>{if(!b&&!g.aborted&&!this.destroying&&h.abort(Error(`push stream closed`)),!b&&!this.destroying&&!S&&c){let t=this.msg.makeSinkPushCredit(a,n,0,this.options.id,c),r=this.codec.encode(t);this.publishToTopic(d,r,{qos:e.qos??i.qos??2}).catch(e=>this.error(G(e,`sending sink push cancel failed`)))}}),this.onResponse.set(`sink-push-chunk:${a}`,async e=>{if(!E){if(e.name!==n){E=!0,ee(),k.destroy(f(e.name));return}if(e.sender===void 0||e.sender===``){E=!0,ee(),k.destroy(Error(`sink chunk for "${n}" missing sender`));return}if(e.sender===c)if(e.error!==void 0)E=!0,ee(),k.destroy(Error(e.error));else{if(O(),e.chunk!==void 0){if(y){if(y.chunksReceived>=y.creditGranted){E=!0,ee(),k.destroy(Error(`flow control violation`));return}y.chunksReceived++}k.destroyed||k.push(e.chunk)}e.final&&(E=!0,ee(),k.destroyed||k.push(null))}}}),u.roll(()=>{this.onResponse.delete(`sink-push-chunk:${a}`)}),O(),u.roll(()=>{ee()});let te=k.buffer,A=!1,ne,re,ie=()=>{A||(A=!0,ne())},j=()=>{A||(A=!0,E||k.readableEnded?ne():re(Error(`push stream closed before end`)))},M=e=>{A||(A=!0,re(e))},ae=new Promise((e,t)=>{ne=e,re=t,k.once(`end`,ie),k.once(`close`,j),k.once(`error`,M)});ae.finally(()=>{k.removeListener(`end`,ie),k.removeListener(`close`,j),k.removeListener(`error`,M)}).catch(()=>{});let oe={sender:c,signal:g,stream:k,buffer:te};if(l&&(oe.receiver=l),_!==void 0&&(oe.authenticated=_),e.meta&&(oe.meta=e.meta),Ae(oe,`stream`,`buffer`,e=>{e===`stream`?k.stopCollecting():e===`buffer`&&k.resume()}),await m(void 0,!0),x=!0,await Promise.race([Promise.resolve(r(...s,oe)),v]),k.readableFlowing!==!0&&!k.destroyed&&k.resume(),await ae.catch(e=>{throw this.error(G(e),`stream drain after sink "${n}" callback failed`),e}),k.collecting&&k.stopCollecting(),!g.aborted)try{b=!0,await m()}catch(e){this.error(G(e),`sending terminal response for sink "${n}" failed`)}}catch(e){let t=G(e,`handler for sink "${n}" failed`);if(h.abort(t),x&&!this.destroying){let e=this.pushStreams.get(a);e!==void 0&&!e.destroyed&&e.destroy(t)}this.error(t),S=!0,await m(t.message).catch(()=>{})}finally{let e=this.pushStreams.get(a);e!==void 0&&!e.destroyed&&!b&&!S&&e.destroy(g.aborted?G(g.reason):Error(`sink push aborted without cause`)),await u.unroll(),C!==void 0&&C.removeListener(`error`,w)}}),s.roll(()=>{this.onRequest.delete(`sink-push-request:${n}`)}),await this.subscribeTopicAndSpool(s,l,i),await this.subscribeTopicAndSpool(s,u,i),this.makeRegistration(s,`sink`,n)}async push(e,...t){if(this.destroyed)throw Error(`push: instance already destroyed`);let n,r,i,a,o={},s;if(typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.data,i=e.params,a=e.receiver,o=e.options??{},s=e.meta):(n=e,r=t[0],i=t.slice(1)),!(r instanceof L.Readable)&&!(r instanceof Uint8Array))throw Error(`invalid data type: expected Readable or Uint8Array`);let c=new je,l=R();for(let e=0;e<10&&(this.onResponse.has(`sink-push-response:${l}`)||this.onResponse.has(`sink-push-credit:${l}`));e++)l=R();if(this.onResponse.has(`sink-push-response:${l}`)||this.onResponse.has(`sink-push-credit:${l}`))throw Error(`failed to generate unique request id`);this.pushSenderSpools.set(l,c),c.roll(()=>{this.pushSenderSpools.delete(l)});let u=this.options.topicMake(n,`sink-push-response`,this.options.id);await this.subscribeTopicAndSpool(c,u,{qos:o.qos??2});let d=new AbortController,f=d.signal;if(this.pushControllers.set(l,d),c.roll(()=>{this.pushControllers.delete(l)}),r instanceof L.Readable){let e=r;f.addEventListener(`abort`,()=>{e.destroyed||e.destroy(G(f.reason))},{once:!0})}let p=`sink-push-send:${l}`,m=()=>{f.aborted||this.timerRefresh(p,()=>{let e=Error(`push to sink "${n}" timed out`);d.abort(e)})};c.roll(()=>{this.timerClear(p)}),m();let h,g,_=0,v,y=!1,b=!1,x=!1,S=!1,C=!1,w=!1,T=!1,E=a,D,O,ee=new Promise((e,t)=>{D=e,O=t});ee.catch(()=>{});let k=(e,t)=>{if(t===void 0||t===``){let t=Error(`received ${e} without sender`);return v=t,d.abort(t),b?S||(S=!0,O(t)):(x=!0,A(t)),!1}if(E===void 0)E=t;else if(t!==E)return!1;return!0},te,A,ne=new Promise((e,t)=>{te=e,A=t});this.onResponse.set(`sink-push-response:${l}`,e=>{if(e.name!==n){let t=Error(`sink response name mismatch (expected "${n}", got "${e.name}")`);v=t,d.abort(t),b?S||(S=!0,O(t)):(x=!0,A(t));return}if(k(`sink response`,e.sender))if(e.error){let t=Error(e.error);v=t,d.abort(t),b?S||(S=!0,O(t)):(x=!0,A(t))}else b?S||(w?(S=!0,D()):T=!0):(h=e.credit,b=!0,x=!0,te())}),c.roll(()=>{this.onResponse.delete(`sink-push-response:${l}`)}),this.onResponse.set(`sink-push-credit:${l}`,e=>{if(e.name!==n){let t=Error(`sink credit name mismatch (expected "${n}", got "${e.name}")`);v=t,d.abort(t),b?S||(S=!0,O(t)):(x=!0,A(t));return}if(k(`sink credit`,e.sender)){if(e.credit===0){y=!0;let e=Error(`push to sink "${n}" cancelled by receiver`);d.abort(e),b?S||(S=!0,O(e)):(x=!0,A(e));return}g===void 0?_+=e.credit:(g.replenish(e.credit),m())}}),c.roll(()=>{this.onResponse.delete(`sink-push-credit:${l}`)});try{let e=()=>{let e=G(f.reason);x||(x=!0,A(e)),S||(S=!0,O(e))};f.addEventListener(`abort`,e,{once:!0}),c.roll(()=>{f.removeEventListener(`abort`,e)});let t=this.authenticate(),u=this.metaStore(s),d=this.msg.makeSinkPushRequest(l,n,i,this.options.id,a,t,u,o.qos),p=this.codec.encode(d),v=this.options.topicMake(n,`sink-push-request`,a);if(await Pe(`publish push request as MQTT message to topic "${v}"`,()=>this.publishToTopic(v,p,{qos:2,...o})),await ne,h!==void 0&&h>0&&(g=new Ce(h+_),_>0&&m(),_=0),g&&(this.pushCreditGates.set(l,g),c.roll(()=>{this.pushCreditGates.delete(l)})),g){let e=g;c.roll(()=>{e.abort()})}let y=E;if(y===void 0)throw Error(`push to sink "${n}" missing responder`);let b=this.options.topicMake(n,`sink-push-request`,y),k=async(e,t,r)=>{m();let i=this.msg.makeSinkPushChunk(l,n,e,t,r,this.options.id,y),a=this.codec.encode(i);await this.publishToTopic(b,a,{qos:2,...o}),t===void 0&&r&&(C=!0)};r instanceof L.Readable?await ke(r,this.options.chunkSize,k,g,f):r instanceof Uint8Array&&await Oe(r,this.options.chunkSize,k,g,f),w=!0,T&&!S&&(S=!0,D()),m(),S||await ee}catch(e){let t=G(e);if(d.abort(t),b&&!v&&!y&&!C)try{let e=E;if(e!==void 0){let r=this.options.topicMake(n,`sink-push-request`,e),i=this.msg.makeSinkPushChunk(l,n,void 0,t.message,!0,this.options.id,e),a=this.codec.encode(i);await this.publishToTopic(r,a,{qos:2,...o}).catch(()=>{})}}catch{}throw b&&!v&&await new Promise(e=>{setImmediate(e)}),v||e}finally{await c.unroll()}}};return class extends fo{}});
|
|
13
|
+
`,l+=c,l},aa.noChildren=!0,Q.registerDecoder(X.CBOR,aa),Q.registerDecoder(X.URI,e=>(Yi(e.contents),new URL(e.contents)),`URI`),$(URL,e=>[X.URI,e.toString()]),Q.registerDecoder(X.BASE64URL,e=>(Yi(e.contents),cr(e.contents)),`Base64url-encoded`),Q.registerDecoder(X.BASE64,e=>(Yi(e.contents),or(e.contents)),`Base64-encoded`),Q.registerDecoder(35,e=>(Yi(e.contents),new RegExp(e.contents)),`RegExp`),Q.registerDecoder(21065,e=>{Yi(e.contents);let t=`^(?:${e.contents})$`;return new RegExp(t,`u`)},`I-RegExp`),Q.registerDecoder(X.REGEXP,e=>{if(Zi(e.contents),e.contents.length<1||e.contents.length>2)throw Error(`Invalid RegExp Array: ${e.contents}`);return new RegExp(e.contents[0],e.contents[1])},`RegExp`),$(RegExp,e=>[X.REGEXP,[e.source,e.flags]]),Q.registerDecoder(64,e=>(Xi(e.contents),e.contents),`uint8 Typed Array`);function oa(e,t,n){Xi(e.contents);let r=e.contents.length;if(r%t.BYTES_PER_ELEMENT!==0)throw Error(`Number of bytes must be divisible by ${t.BYTES_PER_ELEMENT}, got: ${r}`);r/=t.BYTES_PER_ELEMENT;let i=new t(r),a=new DataView(e.contents.buffer,e.contents.byteOffset,e.contents.byteLength),o=a[`get${t.name.replace(/Array/,``)}`].bind(a);for(let e=0;e<r;e++)i[e]=o(e*t.BYTES_PER_ELEMENT,n);return i}function sa(e,t,n,r,i){let a=i.forceEndian??qi;if(mi(a?t:n,e,i),pi(r.byteLength,e,Y.BYTE_STRING),qi===a)e.write(new Uint8Array(r.buffer,r.byteOffset,r.byteLength));else{let t=e[`write${r.constructor.name.replace(/Array/,``)}`].bind(e);for(let e of r)t(e,a)}}Q.registerDecoder(65,e=>oa(e,Uint16Array,!1),`uint16, big endian, Typed Array`),Q.registerDecoder(66,e=>oa(e,Uint32Array,!1),`uint32, big endian, Typed Array`),Q.registerDecoder(67,e=>oa(e,BigUint64Array,!1),`uint64, big endian, Typed Array`),Q.registerDecoder(68,e=>(Xi(e.contents),new Uint8ClampedArray(e.contents)),`uint8 Typed Array, clamped arithmetic`),$(Uint8ClampedArray,e=>[68,new Uint8Array(e.buffer,e.byteOffset,e.byteLength)]),Q.registerDecoder(69,e=>oa(e,Uint16Array,!0),`uint16, little endian, Typed Array`),$(Uint16Array,(e,t,n)=>sa(t,69,65,e,n)),Q.registerDecoder(70,e=>oa(e,Uint32Array,!0),`uint32, little endian, Typed Array`),$(Uint32Array,(e,t,n)=>sa(t,70,66,e,n)),Q.registerDecoder(71,e=>oa(e,BigUint64Array,!0),`uint64, little endian, Typed Array`),$(BigUint64Array,(e,t,n)=>sa(t,71,67,e,n)),Q.registerDecoder(72,e=>(Xi(e.contents),new Int8Array(e.contents)),`sint8 Typed Array`),$(Int8Array,e=>[72,new Uint8Array(e.buffer,e.byteOffset,e.byteLength)]),Q.registerDecoder(73,e=>oa(e,Int16Array,!1),`sint16, big endian, Typed Array`),Q.registerDecoder(74,e=>oa(e,Int32Array,!1),`sint32, big endian, Typed Array`),Q.registerDecoder(75,e=>oa(e,BigInt64Array,!1),`sint64, big endian, Typed Array`),Q.registerDecoder(77,e=>oa(e,Int16Array,!0),`sint16, little endian, Typed Array`),$(Int16Array,(e,t,n)=>sa(t,77,73,e,n)),Q.registerDecoder(78,e=>oa(e,Int32Array,!0),`sint32, little endian, Typed Array`),$(Int32Array,(e,t,n)=>sa(t,78,74,e,n)),Q.registerDecoder(79,e=>oa(e,BigInt64Array,!0),`sint64, little endian, Typed Array`),$(BigInt64Array,(e,t,n)=>sa(t,79,75,e,n)),Q.registerDecoder(81,e=>oa(e,Float32Array,!1),`IEEE 754 binary32, big endian, Typed Array`),Q.registerDecoder(82,e=>oa(e,Float64Array,!1),`IEEE 754 binary64, big endian, Typed Array`),Q.registerDecoder(85,e=>oa(e,Float32Array,!0),`IEEE 754 binary32, little endian, Typed Array`),$(Float32Array,(e,t,n)=>sa(t,85,81,e,n)),Q.registerDecoder(86,e=>oa(e,Float64Array,!0),`IEEE 754 binary64, big endian, Typed Array`),$(Float64Array,(e,t,n)=>sa(t,86,82,e,n)),Q.registerDecoder(X.SET,(e,t)=>{if(Zi(e.contents),t.sortKeys){let n=Ni.decodeToEncodeOpts(t),r=null;for(let i of e.contents){let e=[i,void 0,Ti(i,n)];if(r&&t.sortKeys(r,e)>=0)throw Error(`Set items out of order in tag #${X.SET}`);r=e}}return new Set(e.contents)},`Set`),$(Set,(e,t,n)=>{let r=[...e];if(n.sortKeys){let e=r.map(e=>[e,void 0,Ti(e,n)]);e.sort(n.sortKeys),r=e.map(([e])=>e)}return[X.SET,r]}),Q.registerDecoder(X.JSON,e=>(Yi(e.contents),JSON.parse(e.contents)),`JSON-encoded`);function ca(e){return Xi(e.contents),new Yr().decode(e.contents)}ca.comment=e=>{Xi(e.contents);let t=new Yr;return`(WTF8 string): ${JSON.stringify(t.decode(e.contents))}`},Q.registerDecoder(X.WTF8,ca),Q.registerDecoder(X.SELF_DESCRIBED,e=>e.contents,`Self-Described`),Q.registerDecoder(X.INVALID_16,()=>{throw Error(`Tag always invalid: ${X.INVALID_16}`)},`Invalid`),Q.registerDecoder(X.INVALID_32,()=>{throw Error(`Tag always invalid: ${X.INVALID_32}`)},`Invalid`),Q.registerDecoder(X.INVALID_64,()=>{throw Error(`Tag always invalid: ${X.INVALID_64}`)},`Invalid`),Q.registerDecoder(X.SYMBOL,e=>{let t=e.contents;if(Array.isArray(e.contents)){if(e.contents.length!==1)throw Error(`Expected Array of size 1: ${e.contents}`);[t]=e.contents}if(Yi(t),!t.length)throw Error(`Expected non-empty string: ${e.contents}`);return Symbol.for(t)},`Symbol`);function la(e){throw Error(`Encoding ${e.constructor.name} intentionally unimplmented. It is not concrete enough to interoperate. Convert to Uint8Array first.`)}$(ArrayBuffer,la),$(DataView,la),typeof SharedArrayBuffer<`u`&&$(SharedArrayBuffer,la);function ua(e){return[NaN,e.valueOf()]}$(Boolean,ua),$(Number,ua),$(String,ua),$(BigInt,ua);function da(e){let t={...Ni.defaultDecodeOptions};if(e.dcbor?Object.assign(t,Ni.dcborDecodeOptions):e.cde&&Object.assign(t,Ni.cdeDecodeOptions),Object.assign(t,e),Object.hasOwn(t,`rejectLongNumbers`))throw TypeError(`rejectLongNumbers has changed to requirePreferred`);return t.boxed&&(t.saveOriginal=!0),t}var fa=class{parent=void 0;ret=void 0;step(e,t,n){if(this.ret=Ni.create(e,this.parent,t,n),e[2]===Kn.BREAK)if(this.parent?.isStreaming)this.parent.left=0;else throw Error(`Unexpected BREAK`);else this.parent&&this.parent.push(this.ret,n,e[3]);for(this.ret instanceof Ni&&(this.parent=this.ret);this.parent?.done;){this.ret=this.parent.convert(n);let e=this.parent.parent;e?.replaceLast(this.ret,this.parent,n),this.parent=e}}};function pa(e,t={}){let n=da(t),r=new ki(e,n),i=new fa;for(let e of r)i.step(e,n,r);return i.ret}var{cdeDecodeOptions:ma,dcborDecodeOptions:ha,defaultDecodeOptions:ga}=Ni,_a=class{constructor(e={}){this.options={id:R(),codec:`cbor`,timeout:10*1e3,share:``,chunkSize:16*1024,chunkCredit:4,topicMake:(e,t,n)=>`${e}/${t}/${n??`any`}`,topicMatch:e=>{let t=e.match(/^(.+)\/([^/]+)\/([^/]+)$/);return t?{name:t[1],operation:t[2],peerId:t[3]===`any`?void 0:t[3]}:null},...e}}},va=class{static uint8ArrayToBase64(e){return xe.from(e.buffer,e.byteOffset,e.byteLength).toString(`base64`)}static base64ToUint8Array(e){return new Uint8Array(xe.from(e,`base64`))}static stringify(e){return JSON.stringify(e,(e,t)=>t instanceof Uint8Array?{__Uint8Array:this.uint8ArrayToBase64(t)}:t)}static parse(e){return JSON.parse(e,(e,t)=>typeof t?.__Uint8Array==`string`?this.base64ToUint8Array(t.__Uint8Array):t)}},ya=class{constructor(e){this.format=e,this.types=new dr,this.tags=new Map;let t=64e3;this.types.registerEncoder(xe,e=>[t,new Uint8Array(e.buffer,e.byteOffset,e.byteLength)]),this.tags.set(t,e=>xe.from(e.contents))}encode(e){let t;if(this.format===`cbor`)try{t=Ti(e,{types:this.types})}catch(e){throw Error(`failed to encode CBOR format`,{cause:e})}else if(this.format===`json`)try{t=va.stringify(e)}catch(e){throw Error(`failed to encode JSON format`,{cause:e})}else throw Error(`invalid format "${this.format}"`);return t}decode(e){let t;if(this.format===`cbor`){if(!(e instanceof Uint8Array))throw Error(`failed to decode CBOR format (data type is not Uint8Array)`);if(e.byteLength===0)throw Error(`failed to decode CBOR format (data is empty)`);try{t=pa(e,{tags:this.tags})}catch(e){throw Error(`failed to decode CBOR format`,{cause:e})}}else if(this.format===`json`){if(typeof e!=`string`)throw Error(`failed to decode JSON format (data type is not string)`);if(e.length===0)throw Error(`failed to decode JSON format (data is empty)`);try{t=va.parse(e)}catch(e){throw Error(`failed to decode JSON format`,{cause:e})}}else throw Error(`invalid format "${this.format}"`);return t}},ba=class extends _a{constructor(e={}){super(e),this.codec=new ya(this.options.codec)}},xa=class e extends ba{static{this.encoder=new TextEncoder}static{this.decoder=new TextDecoder}str2buf(t){return e.encoder.encode(t)}buf2str(t){return e.decoder.decode(t)}arr2buf(e){let t;return t=e instanceof Uint8Array?e:new Uint8Array(e.buffer,e.byteOffset,e.byteLength),t}buf2arr(e,t){let n,r=t;if(r===xe)n=xe.from(e.buffer,e.byteOffset,e.byteLength);else if(r===Uint8Array)n=e;else if(r===Int8Array)n=new Int8Array(e.buffer,e.byteOffset,e.byteLength);else throw Error(`invalid data type`);return n}},Sa=e=>{let t=e.match(/^(\d+)\.(\d+)$/);if(t===null)throw Error(`invalid version string`);let n=parseInt(t[2],10);if(n>99)throw Error(`invalid version string: minor version exceeds 99`);return parseInt(t[1],10)*100+n},Ca=Sa(`1.4`),wa=Sa(`1.4`),Ta=Un(Bn(J(),Hn()),Dn(e=>!Array.isArray(e))),Ea=Un(Pn(Un(J(),kn(8192))),kn(8)),Da=class{constructor(e,t,n,r){this.type=e,this.id=t,this.sender=n,this.receiver=r,this.version=`MQTT+/1.4`}},Oa={version:Un(J(),jn(/^MQTT\+\/\d+\.\d+$/)),type:J(),id:J(),sender:q(J()),receiver:q(J())},ka=class extends Da{constructor(e,t,n,r,i,a,o){super(`event-emission`,e,r,i),this.name=t,this.params=n,this.auth=a,this.meta=o}},Aa=Vn({...Oa,type:Ln(`event-emission`),name:J(),params:q(Un(Pn(Hn()),kn(64))),auth:q(Ea),meta:q(Ta)}),ja=class extends Da{constructor(e,t,n,r,i,a,o,s){super(`service-call-request`,e,r,i),this.name=t,this.params=n,this.auth=a,this.meta=o,this.qos=s}},Ma=Vn({...Oa,type:Ln(`service-call-request`),name:J(),params:q(Un(Pn(Hn()),kn(64))),auth:q(Ea),meta:q(Ta),qos:q(zn([0,1,2]))}),Na=class extends Da{constructor(e,t,n,r,i,a){super(`service-call-response`,e,i,a),this.name=t,this.result=n,this.error=r}},Pa=Vn({...Oa,type:Ln(`service-call-response`),name:J(),result:q(Hn()),error:q(J())}),Fa=class extends Da{constructor(e,t,n,r,i,a,o,s){super(`sink-push-request`,e,r,i),this.name=t,this.params=n,this.auth=a,this.meta=o,this.qos=s}},Ia=Vn({...Oa,type:Ln(`sink-push-request`),name:J(),params:q(Un(Pn(Hn()),kn(64))),auth:q(Ea),meta:q(Ta),qos:q(zn([0,1,2]))}),La=class extends Da{constructor(e,t,n,r,i,a){super(`sink-push-response`,e,r,i),this.name=t,this.error=n,this.credit=a}},Ra=Vn({...Oa,type:Ln(`sink-push-response`),name:J(),error:q(J()),credit:q(Un(Rn(),On(),An(1)))}),za=class extends Da{constructor(e,t,n,r,i,a,o){super(`sink-push-chunk`,e,a,o),this.name=t,this.chunk=n,this.error=r,this.final=i}},Ba=Vn({...Oa,type:Ln(`sink-push-chunk`),name:J(),chunk:q(In(Uint8Array)),error:q(J()),final:q(Fn())}),Va=class extends Da{constructor(e,t,n,r,i){super(`sink-push-credit`,e,r,i),this.name=t,this.credit=n}},Ha=Vn({...Oa,type:Ln(`sink-push-credit`),name:J(),credit:Un(Rn(),On(),An(0))}),Ua=class extends Da{constructor(e,t,n,r,i,a,o,s,c){super(`source-fetch-request`,e,r,i),this.name=t,this.params=n,this.auth=a,this.meta=o,this.credit=s,this.qos=c}},Wa=Vn({...Oa,type:Ln(`source-fetch-request`),name:J(),params:q(Un(Pn(Hn()),kn(64))),auth:q(Ea),meta:q(Ta),credit:q(Un(Rn(),On(),An(1))),qos:q(zn([0,1,2]))}),Ga=class extends Da{constructor(e,t,n,r,i,a){super(`source-fetch-response`,e,r,i),this.name=t,this.error=n,this.meta=a}},Ka=Vn({...Oa,type:Ln(`source-fetch-response`),name:J(),error:q(J()),meta:q(Ta)}),qa=class extends Da{constructor(e,t,n,r,i,a,o){super(`source-fetch-chunk`,e,a,o),this.name=t,this.chunk=n,this.error=r,this.final=i}},Ja=Vn({...Oa,type:Ln(`source-fetch-chunk`),name:J(),chunk:q(In(Uint8Array)),error:q(J()),final:q(Fn())}),Ya=class extends Da{constructor(e,t,n,r,i){super(`source-fetch-credit`,e,r,i),this.name=t,this.credit=n}},Xa=Vn({...Oa,type:Ln(`source-fetch-credit`),name:J(),credit:Un(Rn(),On(),An(0))}),Za=class{makeEventEmission(e,t,n,r,i,a,o){return new ka(e,t,n,r,i,a,o)}makeServiceCallRequest(e,t,n,r,i,a,o,s){return new ja(e,t,n,r,i,a,o,s)}makeServiceCallResponse(e,t,n,r,i,a){return new Na(e,t,n,r,i,a)}makeSinkPushRequest(e,t,n,r,i,a,o,s){return new Fa(e,t,n,r,i,a,o,s)}makeSinkPushResponse(e,t,n,r,i,a){return new La(e,t,n,r,i,a)}makeSinkPushChunk(e,t,n,r,i,a,o){return new za(e,t,n,r,i,a,o)}makeSinkPushCredit(e,t,n,r,i){return new Va(e,t,n,r,i)}makeSourceFetchRequest(e,t,n,r,i,a,o,s,c){return new Ua(e,t,n,r,i,a,o,s,c)}makeSourceFetchResponse(e,t,n,r,i,a){return new Ga(e,t,n,r,i,a)}makeSourceFetchChunk(e,t,n,r,i,a,o){return new qa(e,t,n,r,i,a,o)}makeSourceFetchCredit(e,t,n,r,i){return new Ya(e,t,n,r,i)}parse(e){if(typeof e!=`object`||!e)throw Error(`invalid argument: not an object`);if(typeof e.version!=`string`)throw Error(`invalid object: missing or invalid "version" field`);let t=e.version.match(/^MQTT\+\/(\d+\.\d+)$/),n=t===null?0:Sa(t[1]);if(Math.floor(n/100)!==Math.floor(Ca/100)||n<wa)throw Error(`protocol version mismatch (expected version 1.4...1.4, got version ${e.version})`);let r=(e,t,n)=>{let r=Wn(n,e);if(!r.success){let e=r.issues.map(e=>e.message).join(`; `);throw Error(`invalid ${t} object: ${e}`)}return r.output};if(typeof e.type!=`string`)throw Error(`invalid object: missing or invalid "type" field`);if(e.type===`event-emission`){let t=r(e,`EventEmission`,Aa);return this.makeEventEmission(t.id,t.name,t.params,t.sender,t.receiver,t.auth,t.meta)}else if(e.type===`service-call-request`){let t=r(e,`ServiceCallRequest`,Ma);return this.makeServiceCallRequest(t.id,t.name,t.params,t.sender,t.receiver,t.auth,t.meta,t.qos)}else if(e.type===`service-call-response`){let t=r(e,`ServiceCallResponse`,Pa);return this.makeServiceCallResponse(t.id,t.name,t.result,t.error,t.sender,t.receiver)}else if(e.type===`sink-push-request`){let t=r(e,`SinkPushRequest`,Ia);return this.makeSinkPushRequest(t.id,t.name,t.params,t.sender,t.receiver,t.auth,t.meta,t.qos)}else if(e.type===`sink-push-response`){let t=r(e,`SinkPushResponse`,Ra);return this.makeSinkPushResponse(t.id,t.name,t.error,t.sender,t.receiver,t.credit)}else if(e.type===`sink-push-chunk`){let t=r(e,`SinkPushChunk`,Ba);return this.makeSinkPushChunk(t.id,t.name,t.chunk,t.error,t.final,t.sender,t.receiver)}else if(e.type===`sink-push-credit`){let t=r(e,`SinkPushCredit`,Ha);return this.makeSinkPushCredit(t.id,t.name,t.credit,t.sender,t.receiver)}else if(e.type===`source-fetch-request`){let t=r(e,`SourceFetchRequest`,Wa);return this.makeSourceFetchRequest(t.id,t.name,t.params,t.sender,t.receiver,t.auth,t.meta,t.credit,t.qos)}else if(e.type===`source-fetch-response`){let t=r(e,`SourceFetchResponse`,Ka);return this.makeSourceFetchResponse(t.id,t.name,t.error,t.sender,t.receiver,t.meta)}else if(e.type===`source-fetch-chunk`){let t=r(e,`SourceFetchChunk`,Ja);return this.makeSourceFetchChunk(t.id,t.name,t.chunk,t.error,t.final,t.sender,t.receiver)}else if(e.type===`source-fetch-credit`){let t=r(e,`SourceFetchCredit`,Xa);return this.makeSourceFetchCredit(t.id,t.name,t.credit,t.sender,t.receiver)}else throw Error(`invalid object: not of any known type`)}isRequest(e){return e instanceof ka||e instanceof ja||e instanceof Ua||e instanceof Fa}isResponse(e){return e instanceof Na||e instanceof La||e instanceof za||e instanceof Va||e instanceof Ga||e instanceof qa||e instanceof Ya}},Qa=class extends xa{constructor(){super(...arguments),this.msg=new Za}},$a=class{constructor(e,t,n,r){this.timestamp=e,this.level=t,this.msg=n,this.data=r}async resolve(){if(this.msg instanceof Promise&&(this.msg=await this.msg.catch(()=>`<resolve-failed>`)),this.data)for(let e of Object.keys(this.data))this.data[e]instanceof Promise&&(this.data[e]=await this.data[e].catch(()=>`<resolve-failed>`))}toString(){let e=new Date(this.timestamp),t=`${e.getFullYear()}-${(e.getMonth()+1).toString().padStart(2,`0`)}-${e.getDate().toString().padStart(2,`0`)} ${e.getHours().toString().padStart(2,`0`)}:${e.getMinutes().toString().padStart(2,`0`)}:${e.getSeconds().toString().padStart(2,`0`)}.${e.getMilliseconds().toString().padStart(3,`0`)}`,n=this.msg instanceof Promise?`<unresolved>`:this.msg,r=``;if(this.data!==void 0){let e=this.data;r=` (${Object.keys(e).map(t=>{let n=e[t]instanceof Promise?`<unresolved>`:e[t];return`${t}: ${va.stringify(n)}`}).join(`, `)})`}return`[${t}] ${this.level}: ${n}${r}`}},eo=class extends Qa{constructor(){super(...arguments),this._events=new pn.EventEmitter}on(...e){this._events.on(...e)}off(...e){this._events.off(...e)}emitEvent(...e){return this._events.listenerCount(e[0])===0?!1:this._events.emit(...e)}log(e,t,n){let r=new $a(Date.now(),e,t,n);this.emitEvent(`log`,r)}error(e,t){let n=e;t!==void 0&&(n=Error(`${t}: ${e.message}`,{cause:e}),n.stack=e.stack);try{this.emitEvent(`error`,n),this.log(`error`,n.message)}catch{}}},to=class extends eo{constructor(e,t={}){super(t),this.destroyed=!1,this.onRequest=new Map,this.onResponse=new Map,e===null&&(this.log(`info`,`establishing proxy MQTT client`),e=new Proxy({},{get(e,t,n){return t===`isFakeProxy`?!0:typeof t==`string`&&[`on`,`off`,`once`].includes(t)?()=>{}:()=>{throw Error(`Underlying MQTT operation "${String(t)}" called on a null MQTT client -- only MQTT+ "emit({ ..., dry: true })" is supported in this special operation mode`)}}})),this.mqtt=e,this.log(`info`,`hooking into MQTT client`),this.messageHandler=(e,t,n)=>{if(this.destroyed)return;let r;if(this.options.codec===`json`)r=t.toString();else if(this.options.codec===`cbor`)r=be.isBuffer(t)?new Uint8Array(t.buffer,t.byteOffset,t.byteLength):t;else throw Error(`invalid codec configured`);this._onMessage(e,r,n)},this.mqtt.on(`message`,this.messageHandler)}async destroy(){this.destroyed=!0,this.log(`info`,`un-hooking from MQTT client`),this.mqtt.off(`message`,this.messageHandler),this.onRequest.clear(),this.onResponse.clear()}makeRegistration(e,t,n){let r=!1;return{destroy:async()=>{r||(r=!0,await e.unroll(!1)?.catch(e=>{let r=G(e,`destroy: ${t} "${n}" failed to cleanup`);throw this.error(r),r}))}}}async subscribeTopic(e,t={}){return this.log(`info`,`subscribing to MQTT topic "${e}"`),new Promise((n,r)=>{this.mqtt.subscribe(e,{qos:2,...t},(t,i)=>{t?(this.error(t,`subscribing to MQTT topic "${e}" failed`),r(t)):n()})})}async unsubscribeTopic(e){return this.log(`info`,`unsubscribing from MQTT topic "${e}"`),new Promise((t,n)=>{this.mqtt.unsubscribe(e,(r,i)=>{r?(this.error(r,`unsubscribing from MQTT topic "${e}" failed`),n(r)):t()})})}async publishToTopic(e,t,n={}){typeof t==`string`?this.log(`info`,`publishing to MQTT topic "${e}" (type: string, length: ${t.length} chars)`):this.log(`info`,`publishing to MQTT topic "${e}" (type: buffer, length: ${t.byteLength} bytes)`);let r=new Se((e,n)=>{let r;try{let e=this.codec.decode(t);r=this.msg.parse(e)}catch(e){return n(e)}e(r)});return this.log(`debug`,`publishing to MQTT topic "${e}"`,{message:r}),new Promise((r,i)=>{let a=typeof t==`string`?t:be.from(t.buffer,t.byteOffset,t.byteLength);this.mqtt.publish(e,a,n,t=>{t?(this.error(t,`publishing to MQTT topic "${e}" failed`),i(t)):r()})})}_onMessage(e,t,n){if(this.destroyed)return;let r=this.options.topicMatch(e);if(r===null)return;typeof t==`string`?this.log(`info`,`received from MQTT topic "${e}" (type: string, length: ${t.length} chars)`):this.log(`info`,`received from MQTT topic "${e}" (type: buffer, length: ${t.byteLength} bytes)`);let i;try{i=this.codec.decode(t)}catch(e){this.error(G(e,`failed to parse message into object`));return}let a;try{a=this.msg.parse(i)}catch(e){this.error(G(e,`failed to parse object into typed message object`));return}if(this.log(`debug`,`received from MQTT topic "${e}"`,{message:a}),r.peerId!==void 0&&a.receiver!==void 0&&a.receiver!==r.peerId&&this.log(`warning`,`receiver mismatch on direct topic "${e}" (expected "${r.peerId}", got "${a.receiver}")`),this.msg.isRequest(a)){let t=this.onRequest.get(`${a.type}:${a.name}`);t!==void 0&&Promise.resolve().then(()=>{if(!this.destroyed)return t(a,r.name)}).catch(t=>{this.error(G(t,`dispatching request message from MQTT topic "${e}" (type: ${a.type}, id: ${a.id}, name: ${a.name}) failed`))})}else if(this.msg.isResponse(a)){let t=this.onResponse.get(`${a.type}:${a.id}`);t!==void 0&&Promise.resolve().then(()=>{if(!this.destroyed)return t(a,r.name)}).catch(t=>{this.error(G(t,`dispatching response message from MQTT topic "${e}" (type: ${a.type}, id: ${a.id}, name: ${a.name}) failed`))})}}},no=class{constructor(e,t,n=30*1e3){this.subscribeFn=e,this.unsubscribeFn=t,this.lingerMs=n,this.counts=new Map,this.pending=new Map,this.lingers=new Map,this.unsubbing=new Map}incrementCount(e){let t=this.counts.get(e)??0;return this.counts.set(e,t+1),t}decrementCount(e){let t=this.counts.get(e);if(t!==void 0)return t<=1?(this.counts.delete(e),0):(this.counts.set(e,t-1),t-1)}clearCount(e){this.counts.delete(e)}async subscribe(e,t={qos:2}){if(this.incrementCount(e)===0){let n=this.lingers.get(e);if(n){clearTimeout(n),this.lingers.delete(e);return}let r,i,a=new Promise((e,t)=>{r=e,i=t});a.catch(()=>{}),this.pending.set(e,a);let o=this.unsubbing.get(e);return o&&await o,this.subscribeFn(e,t).then(()=>{this.pending.delete(e),r()},t=>{this.pending.delete(e),this.clearCount(e),i(t)}),a}else{let t=this.pending.get(e);if(t)return t}}async unsubscribe(e){if(this.decrementCount(e)===0)if(this.lingerMs>0){let t=setTimeout(()=>{this.lingers.delete(e);let t=this.unsubscribeFn(e).catch(()=>{}).finally(()=>{this.unsubbing.delete(e)});this.unsubbing.set(e,t)},this.lingerMs);this.lingers.set(e,t)}else{let t=this.unsubscribeFn(e).catch(()=>{}).finally(()=>{this.unsubbing.delete(e)});this.unsubbing.set(e,t),await t}}async flush(){let e=new Set([...this.counts.keys(),...this.lingers.keys(),...this.pending.keys(),...this.unsubbing.keys()]);for(let e of this.lingers.values())clearTimeout(e);this.lingers.clear(),this.counts.clear(),await Promise.allSettled([...this.pending.values(),...this.unsubbing.values()]),await Promise.allSettled([...e].map(e=>this.unsubscribeFn(e).catch(()=>{}))),this.pending.clear(),this.unsubbing.clear()}},ro=class extends to{constructor(){super(...arguments),this.subscriptions=new no((e,t)=>this.subscribeTopic(e,t),e=>this.unsubscribeTopic(e))}async subscribeTopicAndSpool(e,t,n={}){await Pe(`subscribe to MQTT topic "${t}"`,e,()=>this.subscriptions.subscribe(t,{qos:2,...n})),e.roll(()=>this.subscriptions.unsubscribe(t))}async destroy(){await this.subscriptions.flush(),await super.destroy()}},io=class extends ro{constructor(){super(...arguments),this.timers=new Map}async destroy(){for(let e of this.timers.values())clearTimeout(e);this.timers.clear(),await super.destroy()}timerRefresh(e,t){let n=this.timers.get(e);n!==void 0&&clearTimeout(n),this.timers.set(e,setTimeout(async()=>{if(!this.destroyed){this.timers.delete(e);try{await t()}catch(t){this.error(G(t),`timer "${e}" failed`)}}},this.options.timeout))}timerClear(e){let t=this.timers.get(e);t!==void 0&&(clearTimeout(t),this.timers.delete(e))}sleep(e,t){return new Promise(n=>{if(t?.aborted){n();return}let r=t===void 0?void 0:new AbortController,i=setTimeout(()=>{i=null,r!==void 0&&r.abort(),n()},e);i.unref(),t!==void 0&&r!==void 0&&t.addEventListener(`abort`,()=>{i!==null&&clearTimeout(i),n()},{once:!0,signal:r.signal})})}timeout(e,t=`timeout`,n){return new Promise((r,i)=>{if(n?.aborted){i(Error(`aborted`));return}let a=n===void 0?void 0:new AbortController,o=setTimeout(()=>{o=null,a!==void 0&&a.abort(),i(Error(t))},e);o.unref(),n!==void 0&&a!==void 0&&n.addEventListener(`abort`,()=>{o!==null&&clearTimeout(o),i(Error(`aborted`))},{once:!0,signal:a.signal})})}},ao=class extends io{constructor(){super(...arguments),this._meta=new Map}meta(...e){let[t,n]=e;if(e.length===0)return Object.fromEntries(this._meta);if(e.length===1)return this._meta.get(t);n==null?this._meta.delete(t):this._meta.set(t,n)}metaStore(e){let t=e===void 0||Object.keys(e).length===0;if(!(this._meta.size===0&&t))return this._meta.size>0&&t?Object.fromEntries(this._meta):this._meta.size===0&&!t?e:{...Object.fromEntries(this._meta),...e}}},oo=new TextEncoder,so=class extends ao{constructor(){super(...arguments),this._credential=null,this._tokens=new Set}credential(e){if(e.length===0)throw Error(`credential must not be empty`);this._credential=ln(un,oo.encode(e),oo.encode(`mqtt-plus`),6e5,32)}async issue(e){if(this._credential===null)throw Error(`credential has to be provided before issuing tokens`);if(e.roles.length===0)throw Error(`payload.roles must be a non-empty array`);if(e.roles.length>64)throw Error(`payload.roles must not exceed 64 roles`);let t=new Qt(e);return t.setProtectedHeader({alg:`HS256`,typ:`JWT`}),await t.sign(this._credential)}authenticate(e,t){if(e===void 0){let e=Array.from(this._tokens);return e.length>0?e:void 0}else if(t===!0)this._tokens.delete(e);else{if(e.length>8192)throw Error(`token must not exceed 8192 characters`);if(!this._tokens.has(e)&&this._tokens.size>=8)throw Error(`at most 8 tokens can be authenticated at once`);this._tokens.add(e)}}async validateToken(e){if(this._credential===null)throw Error(`credential has to be provided before validating tokens`);try{return(await nn(e,this._credential)).payload}catch(e){let t=e?.code??e?.name??`unknown`,n=e?.message??``;return this.log(`warning`,`token validation failed`,{code:t,reason:n}),null}}async authenticated(e,t,n,r){let i=!1,a=typeof n==`string`?[n]:n.roles;if(t!==void 0)for(let n of t.slice(0,8)){if(n.length>8192)continue;let t=await this.validateToken(n);if(t!==null&&!(t.id&&t.id!==e)&&Array.isArray(t.roles)&&!(t.roles.length>64)){for(let e of a)if(t.roles.includes(e)){i=!0;break}if(i)break}}let o=typeof n==`string`||n.mode===`require`;if(!i&&o)throw Error(`${r} failed authentication`);return i}},co=class extends so{constructor(){super(...arguments),this.eventControllers=new Map}async destroy(){for(let e of this.eventControllers.values())e.abort(Error(`event destroyed`));this.eventControllers.clear(),await super.destroy()}async event(e,...t){if(this.destroyed)throw Error(`event: instance already destroyed`);let n,r,i={},a=this.options.share,o;if(typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.callback,i=e.options??{},a=e.share??this.options.share,o=e.auth):(n=e,r=t[0]),typeof r!=`function`)throw Error(`event: callback argument is required and must be a function`);let s=new je;if(this.onRequest.has(`event-emission:${n}`))throw Error(`event: event "${n}" already registered`);let c=new Set;s.roll(()=>{for(let e of c)this.eventControllers.get(e)?.abort(Error(`event "${n}" destroyed`)),this.eventControllers.delete(e);c.clear()});let l=a===``?n:`$share/${a}/${n}`,u=this.options.topicMake(l,`event-emission`),d=this.options.topicMake(n,`event-emission`,this.options.id);return this.onRequest.set(`event-emission:${n}`,async(e,t)=>{if(e.receiver&&e.receiver!==this.options.id)return;let i=e.id,a=e.sender;if(a===void 0||a===``){this.error(Error(`invalid request: missing sender`));return}if(t!==e.name){this.log(`warning`,`event name mismatch -- dropped request for "${n}" (topic: "${t}", payload: "${e.name}")`,{requestId:i});return}let s=e.params??[];if(this.eventControllers.has(i)){this.log(`warning`,`duplicate event request id -- dropped request for event "${n}"`,{requestId:i});return}let l=new AbortController,u=l.signal;c.add(i),this.eventControllers.set(i,l);let d={sender:a,signal:u};e.receiver&&(d.receiver=e.receiver),e.meta&&(d.meta=e.meta);try{o&&(d.authenticated=await this.authenticated(a,e.auth,o,`event "${n}"`)),await r(...s,d)}catch(e){let t=G(e);this.error(t,`handler for event "${n}" failed`)}finally{l.abort(),c.delete(i),this.eventControllers.delete(i)}}),s.roll(()=>{this.onRequest.delete(`event-emission:${n}`)}),await this.subscribeTopicAndSpool(s,u,i),await this.subscribeTopicAndSpool(s,d,i),this.makeRegistration(s,`event`,n)}emit(e,...t){if(this.destroyed)throw Error(`emit: instance already destroyed`);let n,r,i,a={},o,s;typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.params,i=e.receiver,a=e.options??{},o=e.meta,s=e.dry):(n=e,r=t);let c=R(),l=this.authenticate(),u=this.metaStore(o),d=this.msg.makeEventEmission(c,n,r,this.options.id,i,l,u),f=this.codec.encode(d),p=this.options.topicMake(n,`event-emission`,i);return s?{topic:p,payload:f,options:{qos:2,...a}}:Pe(`publish event as MQTT message to topic "${p}"`,()=>this.publishToTopic(p,f,{qos:2,...a}))}},lo=class extends co{constructor(){super(...arguments),this.serviceControllers=new Map,this.pendingCalls=new Map}async destroy(){for(let e of this.serviceControllers.values())e.abort(Error(`service destroyed`));this.serviceControllers.clear();let e=[...this.pendingCalls.values()];this.pendingCalls.clear();for(let t of e)t(Error(`instance destroyed`));await super.destroy()}async service(e,...t){if(this.destroyed)throw Error(`service: instance already destroyed`);let n,r,i={},a=this.options.share,o;if(typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.callback,i=e.options??{},a=e.share??this.options.share,o=e.auth):(n=e,r=t[0]),typeof r!=`function`)throw Error(`service: callback argument is required and must be a function`);let s=new je;if(this.onRequest.has(`service-call-request:${n}`))throw Error(`service: service "${n}" already registered`);let c=new Set;s.roll(()=>{for(let e of c)this.serviceControllers.get(e)?.abort(Error(`service "${n}" destroyed`)),this.serviceControllers.delete(e);c.clear()});let l=a===``?n:`$share/${a}/${n}`,u=this.options.topicMake(l,`service-call-request`),d=this.options.topicMake(n,`service-call-request`,this.options.id);return this.onRequest.set(`service-call-request:${n}`,async(e,t)=>{if(e.receiver&&e.receiver!==this.options.id)return;let i=e.id,a=e.sender,s=e.params??[];if(a===void 0||a===``){this.error(Error(`invalid request: missing sender`));return}if(t!==e.name){this.log(`warning`,`service name mismatch -- dropped request for "${n}" (topic: "${t}", payload: "${e.name}")`,{requestId:i});return}if(this.serviceControllers.has(i)){this.log(`warning`,`duplicate service request id -- dropped request for "${n}"`,{requestId:i});return}let l=new AbortController,u=l.signal;c.add(i),this.serviceControllers.set(i,l);let d={sender:a,signal:u};e.receiver&&(d.receiver=e.receiver),e.meta&&(d.meta=e.meta);let f=`service-call-handler:${i}`,p=()=>{this.timerRefresh(f,()=>{l.abort(Error(`service "${n}" handler timeout`))})},m=()=>this.timerClear(f);p();let h;try{o&&(d.authenticated=await this.authenticated(a,e.auth,o,`service "${n}"`));let t=new Promise((e,t)=>{let n=()=>{t(G(u.reason))};u.aborted?n():(u.addEventListener(`abort`,n,{once:!0}),h=()=>u.removeEventListener(`abort`,n))}),c=await Promise.race([r(...s,d),t]),l=this.msg.makeServiceCallResponse(i,n,c,void 0,this.options.id,a),f=this.codec.encode(l),p=this.options.topicMake(n,`service-call-response`,a);await this.publishToTopic(p,f,{qos:e.qos??2})}catch(t){let r=G(t);this.error(r,`handler for service "${n}" failed`);let o=this.msg.makeServiceCallResponse(i,n,void 0,r.message,this.options.id,a);try{let t=this.codec.encode(o),r=this.options.topicMake(n,`service-call-response`,a);await this.publishToTopic(r,t,{qos:e.qos??2})}catch(e){this.error(G(e),`sending error response for service "${n}" failed`)}}finally{h?.(),m(),l.abort(),c.delete(i),this.serviceControllers.delete(i)}}),s.roll(()=>{this.onRequest.delete(`service-call-request:${n}`)}),await this.subscribeTopicAndSpool(s,u,i),await this.subscribeTopicAndSpool(s,d,i),this.makeRegistration(s,`service`,n)}async call(e,...t){if(this.destroyed)throw Error(`call: instance already destroyed`);let n,r,i,a={},o;typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.params,i=e.receiver,a=e.options??{},o=e.meta):(n=e,r=t);let s=new je,c=R();for(let e=0;e<10&&this.onResponse.has(`service-call-response:${c}`);e++)c=R();if(this.onResponse.has(`service-call-response:${c}`))throw Error(`failed to generate unique request id`);let l=this.options.topicMake(n,`service-call-response`,this.options.id);await this.subscribeTopicAndSpool(s,l,{qos:a.qos??2});let u=`service-call:${c}`,d,f=!1,p=()=>f?!1:(f=!0,s.unroll(),!0),m=new Promise((e,t)=>{d=t,this.timerRefresh(u,()=>{p()&&t(Error(`communication timeout`))}),s.roll(()=>{this.timerClear(u)}),this.pendingCalls.set(c,e=>{p()&&t(e)}),s.roll(()=>{this.pendingCalls.delete(c)}),this.onResponse.set(`service-call-response:${c}`,r=>{if(!(i!==void 0&&r.sender!==i)){if(r.sender===void 0||r.sender===``){if(!p())return;t(Error(`received service response without sender`));return}if(r.name!==n){if(!p())return;t(Error(`received service response with name mismatch (expected: "${n}", received: "${r.name}")`));return}p()&&(r.error===void 0?e(r.result):t(Error(r.error)))}}),s.roll(()=>{this.onResponse.delete(`service-call-response:${c}`)})}),h=this.authenticate(),g=this.metaStore(o),_=a.qos??2,v=this.msg.makeServiceCallRequest(c,n,r,this.options.id,i,h,g,_),y=this.codec.encode(v),b=this.options.topicMake(n,`service-call-request`,i);try{await Pe(`publish service request as MQTT message to topic "${b}"`,()=>this.publishToTopic(b,y,{...a,qos:_}))}catch(e){return p()&&d(e),m}return m}},uo=class extends lo{constructor(){super(...arguments),this.sourceCreditGates=new Map,this.sourceControllers=new Map,this.sourceSpools=new Map,this.sourceRequests=new Map}async destroy(){for(let e of this.sourceControllers.values())e.abort(Error(`source destroyed`));for(let e of this.sourceCreditGates.values())e.abort();for(let e of[...this.sourceSpools.values()])await e.unroll();this.sourceSpools.clear(),this.sourceControllers.clear(),this.sourceCreditGates.clear(),this.sourceRequests.clear(),await super.destroy()}async source(e,...t){if(this.destroyed)throw Error(`source: instance already destroyed`);let n,r,i={},a=this.options.share,o;if(typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.callback,i=e.options??{},a=e.share??this.options.share,o=e.auth):(n=e,r=t[0]),typeof r!=`function`)throw Error(`source: callback argument is required and must be a function`);let s=new je;if(this.onRequest.has(`source-fetch-request:${n}`))throw Error(`source: source "${n}" already registered`);let c=a===``?n:`$share/${a}/${n}`,l=this.options.topicMake(c,`source-fetch-request`),u=this.options.topicMake(n,`source-fetch-request`,this.options.id);return this.onRequest.set(`source-fetch-request:${n}`,async(e,t)=>{if(e.receiver&&e.receiver!==this.options.id)return;let a=e.id,s=e.params??[],c=e.sender,l=e.receiver,u=new je;if(this.sourceSpools.set(a,u),u.roll(()=>{this.sourceSpools.delete(a)}),c===void 0||c===``){this.error(Error(`invalid request: missing sender`)),await u.unroll();return}let d=this.options.topicMake(n,`source-fetch-response`,c),f=(e,t)=>Error(`${e} name mismatch (expected "${n}", got "${t}")`),p=async(t,r)=>{let o=this.metaStore(r),s=this.msg.makeSourceFetchResponse(a,n,t,this.options.id,c,o),l=this.codec.encode(s);await this.publishToTopic(d,l,{qos:e.qos??i.qos??2})},m=new AbortController,h=m.signal;if(this.sourceControllers.has(a)){let e=Error(`source: duplicate request id "${a}"`);this.error(e),await p(e.message).catch(()=>{}),await u.unroll();return}this.sourceControllers.set(a,m),u.roll(()=>{this.sourceControllers.delete(a)});let g={sender:c,signal:h};l&&(g.receiver=l),e.meta&&(g.meta=e.meta);let _=this.sourceRequests.get(n);_||(_=new Set,this.sourceRequests.set(n,_)),_.add(a),u.roll(()=>{let e=this.sourceRequests.get(n);e===_&&(e.delete(a),e.size===0&&this.sourceRequests.delete(n))}),h.addEventListener(`abort`,()=>{g.stream instanceof L.Readable&&!g.stream.destroyed&&g.stream.destroy(G(h.reason))},{once:!0}),u.roll(()=>{h.aborted&&g.stream instanceof L.Readable&&!g.stream.destroyed&&g.stream.destroy(G(h.reason))});let v,y=new Promise((e,t)=>{v=t});y.catch(()=>{});let b=()=>{v(G(h.reason))};h.aborted?b():h.addEventListener(`abort`,b,{once:!0}),u.roll(()=>{h.removeEventListener(`abort`,b)});let x=`source-fetch-send:${a}`,S=()=>{h.aborted||this.timerRefresh(x,()=>{m.abort(Error(`source fetch "${n}" timed out`))})},C=()=>this.timerClear(x);S(),u.roll(()=>{C()});let w=async(t,r,o)=>{S();let s=this.msg.makeSourceFetchChunk(a,n,t,r,o,this.options.id,c),l=this.codec.encode(s);await this.publishToTopic(d,l,{qos:e.qos??i.qos??2})},T=!1,E,D=!1;try{if(t!==e.name)throw Error(`source name mismatch (topic: "${t}", payload: "${e.name}")`);this.onResponse.set(`source-fetch-credit:${a}`,e=>{if(!h.aborted){if(e.name!==n){m.abort(f(`source credit`,e.name));return}if(e.sender===void 0||e.sender===``){m.abort(Error(`source credit for "${n}" missing sender`));return}if(e.sender===c){if(e.credit===0){D=!0,m.abort(Error(`source fetch "${n}" cancelled by fetcher`));return}E&&(E.replenish(e.credit),S())}}}),u.roll(()=>{this.onResponse.delete(`source-fetch-credit:${a}`)}),o&&(g.authenticated=await this.authenticated(c,e.auth,o,`source "${n}"`));let i=Promise.resolve(r(...s,g));if(i.catch(()=>{}),await Promise.race([i,y]),!(g.stream instanceof L.Readable)&&!(g.buffer instanceof Promise)&&!(g.buffer instanceof Uint8Array))throw Error(`handler did not provide data via info.stream or info.buffer fields`);if(g.stream instanceof L.Readable&&(g.buffer instanceof Promise||g.buffer instanceof Uint8Array))throw Error(`handler has set both info.stream and info.buffer fields`);let l=e.credit;if(E=l!==void 0&&l>0?new Ce(l):void 0,E){let e=E;this.sourceCreditGates.set(a,e),u.roll(()=>{e.abort(),this.sourceCreditGates.delete(a)})}if(await p(void 0,g.meta),T=!0,g.stream instanceof L.Readable)await ke(g.stream,this.options.chunkSize,w,E,h);else if(g.buffer instanceof Promise||g.buffer instanceof Uint8Array){let e;if(g.buffer instanceof Promise){let t=g.buffer;t.catch(()=>{}),e=await Promise.race([t,y])}else e=g.buffer;h.throwIfAborted(),await Oe(e,this.options.chunkSize,w,E,h)}}catch(e){let t=G(e,`handler for source "${n}" failed`);m.abort(t),D||(this.error(t),T?await w(void 0,t.message,!0).catch(()=>{}):await p(t.message).catch(()=>{}))}finally{await u.unroll()}}),s.roll(()=>{this.onRequest.delete(`source-fetch-request:${n}`)}),s.roll(()=>{let e=this.sourceRequests.get(n);if(e){for(let t of e){let e=this.sourceControllers.get(t);e&&e.abort(Error(`source "${n}" destroyed`));let r=this.sourceCreditGates.get(t);r&&r.abort()}this.sourceRequests.delete(n)}}),await this.subscribeTopicAndSpool(s,l,i),await this.subscribeTopicAndSpool(s,u,i),this.makeRegistration(s,`source`,n)}async fetch(e,...t){if(this.destroyed)throw Error(`fetch: instance already destroyed`);let n,r,i,a={},o;typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.params,i=e.receiver,a=e.options??{},o=e.meta):(n=e,r=t);let s=new je,c=R();for(let e=0;e<10&&(this.onResponse.has(`source-fetch-response:${c}`)||this.onResponse.has(`source-fetch-chunk:${c}`));e++)c=R();if(this.onResponse.has(`source-fetch-response:${c}`)||this.onResponse.has(`source-fetch-chunk:${c}`))throw Error(`failed to generate unique request id`);let l=this.options.topicMake(n,`source-fetch-response`,this.options.id),u=(e,t)=>Error(`${e} name mismatch (expected "${n}", got "${t}")`);await this.subscribeTopicAndSpool(s,l,{qos:a.qos??2});let d=this.options.chunkCredit,f=d>0?d*this.options.chunkSize:16*1024,p=0,m=d,h=i,g=!1,_=!1,v=[],y=(e,t)=>{if(t===void 0||t===``){_=!0;let t=Error(`received ${e} without sender`);return x(t),w?.destroy(t),s.unroll()?.catch(()=>{}),!1}if(h===void 0)h=t;else if(t!==h)return!1;return!0},b,x,S=new Promise((e,t)=>{b=e,x=t});S.catch(()=>{}),s.roll(()=>{b(void 0)});let C=`source-fetch-recv:${c}`,w,T=()=>{_||w&&w.destroyed||this.timerRefresh(C,()=>{let e=Error(`communication timeout`);x(e),w?.destroy(e)})};s.roll(()=>{this.timerClear(C)}),w=new De({highWaterMark:f,read:e=>{if(d<=0||_)return;let t=h;if(!t)return;let r=m-p,i=Math.max(0,f-(w?.readableLength??0)),o=Math.floor(i/this.options.chunkSize),s=Math.max(0,o-r);if(s>0){m+=s;let e=this.msg.makeSourceFetchCredit(c,n,s,this.options.id,t),r=this.codec.encode(e),i=this.options.topicMake(n,`source-fetch-request`,t);this.publishToTopic(i,r,{qos:a.qos??2}).catch(e=>{let t=G(e,`sending source fetch credit failed`);this.error(t),D(t)}),T()}}});let E=w.buffer;T();let D=e=>{_=!0,x(e),w.destroy(e)},O=!1,ee=e=>{if(!O&&!_){O=!0;let e=h;if(e){let t=this.msg.makeSourceFetchCredit(c,n,0,this.options.id,e),r=this.codec.encode(t),i=this.options.topicMake(n,`source-fetch-request`,e);this.publishToTopic(i,r,{qos:a.qos??2}).catch(e=>this.error(G(e,`sending source fetch cancel failed`)))}}_||x(e===void 0?Error(`stream aborted`):G(e)),s.unroll()?.catch(()=>{})};w.once(`close`,()=>ee()),w.once(`error`,e=>ee(e));let k=e=>{if(e.error)D(Error(e.error));else{if(T(),e.chunk!==void 0){if(d>0&&p>=m){D(Error(`flow control violation`));return}p++,w.destroyed||w.push(e.chunk)}e.final&&(_=!0,w.destroyed||w.push(null),s.unroll()?.catch(()=>{}))}};this.onResponse.set(`source-fetch-response:${c}`,e=>{if(!_){if(e.name!==n){D(u(`source response`,e.name));return}if(y(`source response`,e.sender))if(e.error)D(Error(e.error));else{if(g)return;g=!0,b(e.meta),T();for(let e of v){if(_)break;k(e)}v.length=0}}}),this.onResponse.set(`source-fetch-chunk:${c}`,e=>{if(!_){if(e.name!==n){D(u(`source chunk`,e.name));return}if(y(`source chunk`,e.sender)){if(!g){if(d>0&&v.length>=m){D(Error(`flow control violation`));return}v.push(e);return}k(e)}}}),s.roll(()=>{this.onResponse.delete(`source-fetch-response:${c}`),this.onResponse.delete(`source-fetch-chunk:${c}`)});let te=this.authenticate(),A=this.metaStore(o),ne=d>0?d:void 0,re=this.msg.makeSourceFetchRequest(c,n,r,this.options.id,i,te,A,ne,a.qos),ie=this.codec.encode(re),j=this.options.topicMake(n,`source-fetch-request`,i);await Pe(`publish fetch request as MQTT message to topic "${j}"`,()=>this.publishToTopic(j,ie,{qos:2,...a})).catch(e=>{let t=G(e);x(t),w.destroy(t)});let M={stream:w,buffer:E,meta:S};return Ae(M,`stream`,`buffer`,e=>{e===`stream`?w.stopCollecting():e===`buffer`&&w.resume()}),M}},fo=class extends uo{constructor(){super(...arguments),this.pushStreams=new Map,this.pushSpools=new Map,this.pushRecvControllers=new Map,this.destroying=!1,this.pushControllers=new Map,this.pushCreditGates=new Map,this.pushSenderSpools=new Map}async destroy(){this.destroying=!0;for(let e of this.pushSenderSpools.keys())this.timerClear(`sink-push-send:${e}`);for(let e of this.pushSpools.keys())this.timerClear(`sink-push-recv:${e}`);for(let e of this.pushControllers.values())e.abort(Error(`sink destroyed`));for(let e of this.pushCreditGates.values())e.abort();for(let e of[...this.pushSenderSpools.values()])await e.unroll();this.pushSenderSpools.clear(),this.pushControllers.clear(),this.pushCreditGates.clear();for(let e of this.pushRecvControllers.values())e.abort(Error(`sink destroyed`));this.pushRecvControllers.clear();for(let e of this.pushStreams.values())e.destroy(Error(`sink destroyed`));this.pushStreams.clear();for(let e of[...this.pushSpools.values()])await e.unroll();this.pushSpools.clear(),await super.destroy()}async sink(e,...t){if(this.destroyed)throw Error(`sink: instance already destroyed`);let n,r,i={},a=this.options.share,o;if(typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.callback,i=e.options??{},a=e.share??this.options.share,o=e.auth):(n=e,r=t[0]),typeof r!=`function`)throw Error(`sink: callback argument is required and must be a function`);let s=new je;if(this.onRequest.has(`sink-push-request:${n}`))throw Error(`sink: sink "${n}" already registered`);let c=a===``?n:`$share/${a}/${n}`,l=this.options.topicMake(c,`sink-push-request`),u=this.options.topicMake(n,`sink-push-request`,this.options.id);return this.onRequest.set(`sink-push-request:${n}`,async(e,t)=>{if(e.receiver&&e.receiver!==this.options.id)return;let a=e.id,s=e.params??[],c=e.sender,l=e.receiver,u=new je;if(c===void 0||c===``){this.error(Error(`invalid request: missing sender`)),await u.unroll();return}let d=this.options.topicMake(n,`sink-push-response`,c),f=e=>Error(`sink name mismatch (expected "${n}", got "${e}")`),p=this.options.chunkCredit,m=async(t,r=!1)=>{let o=t===void 0&&r&&p>0?p:void 0,s=this.msg.makeSinkPushResponse(a,n,t,this.options.id,c,o),l=this.codec.encode(s);await this.publishToTopic(d,l,{qos:e.qos??i.qos??2})},h=new AbortController,g=h.signal,_,v=new Promise((e,t)=>{_=t});v.catch(()=>{});let y=()=>{_(G(g.reason))};if(g.aborted?y():g.addEventListener(`abort`,y,{once:!0}),u.roll(()=>{g.removeEventListener(`abort`,y)}),this.pushRecvControllers.has(a)){let e=Error(`sink: duplicate request id "${a}"`);this.error(e),await m(e.message).catch(()=>{}),await u.unroll();return}this.pushRecvControllers.set(a,h),u.roll(()=>{this.pushRecvControllers.delete(a)}),this.pushSpools.set(a,u),u.roll(()=>{this.pushSpools.delete(a)});let b=!1,x=!1,S=!1,C,w=()=>{};try{if(t!==e.name)throw Error(`sink name mismatch (topic: "${t}", payload: "${e.name}")`);let _;o&&(_=await this.authenticated(c,e.auth,o,`sink "${n}"`));let y=p>0?{chunksReceived:0,creditGranted:p}:void 0,T=p>0?p*this.options.chunkSize:16*1024,E=!1,D=`sink-push-recv:${a}`,O=()=>this.timerRefresh(D,()=>{if(!(E||this.destroying)&&(h.abort(Error(`push stream timeout`)),c&&!S)){S=!0;let t=this.msg.makeSinkPushCredit(a,n,0,this.options.id,c),r=this.codec.encode(t);this.publishToTopic(d,r,{qos:e.qos??i.qos??2}).catch(()=>{})}}),ee=()=>this.timerClear(D),k=new De({highWaterMark:T,read:t=>{if(!y||!this.pushSpools.has(a))return;let r=y.creditGranted-y.chunksReceived,o=Math.max(0,T-k.readableLength),s=Math.floor(o/this.options.chunkSize),l=Math.max(0,s-r);if(l>0){y.creditGranted+=l;let t=this.msg.makeSinkPushCredit(a,n,l,this.options.id,c),r=this.codec.encode(t);this.publishToTopic(d,r,{qos:e.qos??i.qos??2}).catch(e=>{let t=G(e,`sending sink push credit failed`);this.error(t),k.destroy(t)}),O()}}});C=k,this.pushStreams.set(a,k),u.roll(()=>{this.pushStreams.delete(a)}),k.on(`error`,w),u.roll(()=>{if(!b&&!g.aborted&&!this.destroying&&h.abort(Error(`push stream closed`)),!b&&!this.destroying&&!S&&c){let t=this.msg.makeSinkPushCredit(a,n,0,this.options.id,c),r=this.codec.encode(t);this.publishToTopic(d,r,{qos:e.qos??i.qos??2}).catch(e=>this.error(G(e,`sending sink push cancel failed`)))}}),this.onResponse.set(`sink-push-chunk:${a}`,async e=>{if(!E){if(e.name!==n){E=!0,ee(),k.destroy(f(e.name));return}if(e.sender===void 0||e.sender===``){E=!0,ee(),k.destroy(Error(`sink chunk for "${n}" missing sender`));return}if(e.sender===c)if(e.error!==void 0)E=!0,ee(),k.destroy(Error(e.error));else{if(O(),e.chunk!==void 0){if(y){if(y.chunksReceived>=y.creditGranted){E=!0,ee(),k.destroy(Error(`flow control violation`));return}y.chunksReceived++}k.destroyed||k.push(e.chunk)}e.final&&(E=!0,ee(),k.destroyed||k.push(null))}}}),u.roll(()=>{this.onResponse.delete(`sink-push-chunk:${a}`)}),O(),u.roll(()=>{ee()});let te=k.buffer,A=!1,ne,re,ie=()=>{A||(A=!0,ne())},j=()=>{A||(A=!0,E||k.readableEnded?ne():re(Error(`push stream closed before end`)))},M=e=>{A||(A=!0,re(e))},ae=new Promise((e,t)=>{ne=e,re=t,k.once(`end`,ie),k.once(`close`,j),k.once(`error`,M)});ae.finally(()=>{k.removeListener(`end`,ie),k.removeListener(`close`,j),k.removeListener(`error`,M)}).catch(()=>{});let oe={sender:c,signal:g,stream:k,buffer:te};l&&(oe.receiver=l),_!==void 0&&(oe.authenticated=_),e.meta&&(oe.meta=e.meta),Ae(oe,`stream`,`buffer`,e=>{e===`stream`?k.stopCollecting():e===`buffer`&&k.resume()}),await m(void 0,!0),x=!0;let N=Promise.resolve(r(...s,oe));if(N.catch(()=>{}),await Promise.race([N,v]),k.readableFlowing!==!0&&!k.destroyed&&k.resume(),await ae.catch(e=>{throw this.error(G(e),`stream drain after sink "${n}" callback failed`),e}),k.collecting&&k.stopCollecting(),!g.aborted)try{b=!0,await m()}catch(e){this.error(G(e),`sending terminal response for sink "${n}" failed`)}}catch(e){let t=G(e,`handler for sink "${n}" failed`);if(h.abort(t),x&&!this.destroying){let e=this.pushStreams.get(a);e!==void 0&&!e.destroyed&&e.destroy(t)}this.error(t),S||(S=!0,await m(t.message).catch(()=>{}))}finally{let e=this.pushStreams.get(a);e!==void 0&&!e.destroyed&&!b&&!S&&e.destroy(g.aborted?G(g.reason):Error(`sink push aborted without cause`)),await u.unroll(),C!==void 0&&C.removeListener(`error`,w)}}),s.roll(()=>{this.onRequest.delete(`sink-push-request:${n}`)}),await this.subscribeTopicAndSpool(s,l,i),await this.subscribeTopicAndSpool(s,u,i),this.makeRegistration(s,`sink`,n)}async push(e,...t){if(this.destroyed)throw Error(`push: instance already destroyed`);let n,r,i,a,o={},s;if(typeof e==`object`&&e&&`name`in e?(n=e.name,r=e.data,i=e.params,a=e.receiver,o=e.options??{},s=e.meta):(n=e,r=t[0],i=t.slice(1)),!(r instanceof L.Readable)&&!(r instanceof Uint8Array))throw Error(`invalid data type: expected Readable or Uint8Array`);let c=new je,l=R();for(let e=0;e<10&&(this.onResponse.has(`sink-push-response:${l}`)||this.onResponse.has(`sink-push-credit:${l}`));e++)l=R();if(this.onResponse.has(`sink-push-response:${l}`)||this.onResponse.has(`sink-push-credit:${l}`))throw Error(`failed to generate unique request id`);this.pushSenderSpools.set(l,c),c.roll(()=>{this.pushSenderSpools.delete(l)});let u=this.options.topicMake(n,`sink-push-response`,this.options.id);await this.subscribeTopicAndSpool(c,u,{qos:o.qos??2});let d=new AbortController,f=d.signal;if(this.pushControllers.set(l,d),c.roll(()=>{this.pushControllers.delete(l)}),r instanceof L.Readable){let e=r;f.addEventListener(`abort`,()=>{e.destroyed||e.destroy(G(f.reason))},{once:!0})}let p=`sink-push-send:${l}`,m=()=>{f.aborted||this.timerRefresh(p,()=>{let e=Error(`push to sink "${n}" timed out`);d.abort(e)})};c.roll(()=>{this.timerClear(p)}),m();let h,g,_=0,v,y=!1,b=!1,x=!1,S=!1,C=!1,w=!1,T=!1,E=a,D,O,ee=new Promise((e,t)=>{D=e,O=t});ee.catch(()=>{});let k=(e,t)=>{if(t===void 0||t===``){let t=Error(`received ${e} without sender`);return v=t,d.abort(t),b?S||(S=!0,O(t)):(x=!0,A(t)),!1}if(E===void 0)E=t;else if(t!==E)return!1;return!0},te,A,ne=new Promise((e,t)=>{te=e,A=t});this.onResponse.set(`sink-push-response:${l}`,e=>{if(e.name!==n){let t=Error(`sink response name mismatch (expected "${n}", got "${e.name}")`);v=t,d.abort(t),b?S||(S=!0,O(t)):(x=!0,A(t));return}if(k(`sink response`,e.sender))if(e.error){let t=Error(e.error);v=t,d.abort(t),b?S||(S=!0,O(t)):(x=!0,A(t))}else b?S||(w?(S=!0,D()):T=!0):(h=e.credit,b=!0,x=!0,te())}),c.roll(()=>{this.onResponse.delete(`sink-push-response:${l}`)}),this.onResponse.set(`sink-push-credit:${l}`,e=>{if(e.name!==n){let t=Error(`sink credit name mismatch (expected "${n}", got "${e.name}")`);v=t,d.abort(t),b?S||(S=!0,O(t)):(x=!0,A(t));return}if(k(`sink credit`,e.sender)){if(e.credit===0){y=!0;let e=Error(`push to sink "${n}" cancelled by receiver`);d.abort(e),b?S||(S=!0,O(e)):(x=!0,A(e));return}if(g!==void 0)g.replenish(e.credit),m();else if(b&&h===void 0){let e=Error(`push to sink "${n}" received unsolicited credit (credit-flow disabled)`);v=e,d.abort(e),S||(S=!0,O(e))}else _+=e.credit}}),c.roll(()=>{this.onResponse.delete(`sink-push-credit:${l}`)});try{let e=()=>{let e=G(f.reason);x||(x=!0,A(e)),S||(S=!0,O(e))};f.addEventListener(`abort`,e,{once:!0}),c.roll(()=>{f.removeEventListener(`abort`,e)});let t=this.authenticate(),u=this.metaStore(s),d=this.msg.makeSinkPushRequest(l,n,i,this.options.id,a,t,u,o.qos),p=this.codec.encode(d),v=this.options.topicMake(n,`sink-push-request`,a);if(await Pe(`publish push request as MQTT message to topic "${v}"`,()=>this.publishToTopic(v,p,{qos:2,...o})),await ne,h!==void 0&&h>0)g=new Ce(h+_),_>0&&m(),_=0;else if(_>0)throw Error(`push to sink "${n}" received unsolicited credit (credit-flow disabled)`);if(g&&(this.pushCreditGates.set(l,g),c.roll(()=>{this.pushCreditGates.delete(l)})),g){let e=g;c.roll(()=>{e.abort()})}let y=E;if(y===void 0)throw Error(`push to sink "${n}" missing responder`);let b=this.options.topicMake(n,`sink-push-request`,y),k=async(e,t,r)=>{m();let i=this.msg.makeSinkPushChunk(l,n,e,t,r,this.options.id,y),a=this.codec.encode(i);await this.publishToTopic(b,a,{qos:2,...o}),t===void 0&&r&&(C=!0)};r instanceof L.Readable?await ke(r,this.options.chunkSize,k,g,f):r instanceof Uint8Array&&await Oe(r,this.options.chunkSize,k,g,f),w=!0,T&&!S&&(S=!0,D()),m(),S||await ee}catch(e){let t=G(e);if(d.abort(t),b&&!v&&!y&&!C)try{let e=E;if(e!==void 0){let r=this.options.topicMake(n,`sink-push-request`,e),i=this.msg.makeSinkPushChunk(l,n,void 0,t.message,!0,this.options.id,e),a=this.codec.encode(i);await this.publishToTopic(r,a,{qos:2,...o}).catch(()=>{})}}catch{}throw b&&!v&&await new Promise(e=>{setImmediate(e)}),v||e}finally{await c.unroll()}}};return class extends fo{}});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mqtt-plus",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.19",
|
|
4
4
|
"description": "MQTT Communication Patterns",
|
|
5
5
|
"keywords": [ "mqtt",
|
|
6
6
|
"event", "emit",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"patch-package": "8.0.1",
|
|
34
34
|
"@rse/stx": "1.1.5",
|
|
35
35
|
"shx": "0.4.0",
|
|
36
|
-
"eslint": "10.2.
|
|
36
|
+
"eslint": "10.2.1",
|
|
37
37
|
"globals": "17.5.0",
|
|
38
38
|
"@eslint/js": "10.0.1",
|
|
39
39
|
"@eslint/eslintrc": "3.3.5",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"sinon": "21.1.2",
|
|
47
47
|
"c8": "11.0.0",
|
|
48
48
|
"source-map-support": "0.5.21",
|
|
49
|
-
"typescript": "6.0.
|
|
49
|
+
"typescript": "6.0.3",
|
|
50
50
|
"typescript-eslint": "8.58.2",
|
|
51
51
|
"@typescript-eslint/parser": "8.58.2",
|
|
52
52
|
"mqtt": "5.15.1",
|
package/src/mqtt-plus-sink.ts
CHANGED
|
@@ -169,8 +169,6 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
|
|
|
169
169
|
|
|
170
170
|
/* create a resource spool for request cleanup */
|
|
171
171
|
const reqSpool = new Spool()
|
|
172
|
-
this.pushSpools.set(requestId, reqSpool)
|
|
173
|
-
reqSpool.roll(() => { this.pushSpools.delete(requestId) })
|
|
174
172
|
|
|
175
173
|
/* sanity check sender */
|
|
176
174
|
if (sender === undefined || sender === "") {
|
|
@@ -218,6 +216,8 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
|
|
|
218
216
|
}
|
|
219
217
|
this.pushRecvControllers.set(requestId, abortController)
|
|
220
218
|
reqSpool.roll(() => { this.pushRecvControllers.delete(requestId) })
|
|
219
|
+
this.pushSpools.set(requestId, reqSpool)
|
|
220
|
+
reqSpool.roll(() => { this.pushSpools.delete(requestId) })
|
|
221
221
|
|
|
222
222
|
/* check authentication and prepare stream */
|
|
223
223
|
let dataCompleted = false
|
|
@@ -432,10 +432,9 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
|
|
|
432
432
|
ackSent = true
|
|
433
433
|
|
|
434
434
|
/* call handler */
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
])
|
|
435
|
+
const callbackPromise = Promise.resolve(callback(...params, info))
|
|
436
|
+
callbackPromise.catch(() => {}) /* guard against unhandled rejection if abort wins the race */
|
|
437
|
+
await Promise.race([ callbackPromise, abortPromise ])
|
|
439
438
|
|
|
440
439
|
/* ensure stream is consumed or destroyed to prevent hang */
|
|
441
440
|
if (readable.readableFlowing !== true && !readable.destroyed)
|
|
@@ -475,10 +474,14 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
|
|
|
475
474
|
stream.destroy(error)
|
|
476
475
|
}
|
|
477
476
|
|
|
478
|
-
/* send error as nak response or as mid-stream error response
|
|
477
|
+
/* send error as nak response or as mid-stream error response
|
|
478
|
+
(skip when a terminal signal was already emitted, e.g. the
|
|
479
|
+
pre-emptive credit=0 cancel published by the timeout handler) */
|
|
479
480
|
this.error(error)
|
|
480
|
-
errorResponseSent
|
|
481
|
-
|
|
481
|
+
if (!errorResponseSent) {
|
|
482
|
+
errorResponseSent = true
|
|
483
|
+
await sendResponse(error.message).catch(() => {})
|
|
484
|
+
}
|
|
482
485
|
}
|
|
483
486
|
finally {
|
|
484
487
|
/* cleanup resources */
|
|
@@ -749,6 +752,17 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
|
|
|
749
752
|
creditGate.replenish(response.credit)
|
|
750
753
|
refreshTimeout()
|
|
751
754
|
}
|
|
755
|
+
else if (pushAcked && initialCredit === undefined) {
|
|
756
|
+
/* protocol violation: receiver sent credit despite
|
|
757
|
+
not granting initial credit during ack */
|
|
758
|
+
const error = new Error(`push to sink "${name}" received unsolicited credit (credit-flow disabled)`)
|
|
759
|
+
remoteErrorObject = error
|
|
760
|
+
abortController.abort(error)
|
|
761
|
+
if (!pushFinalized) {
|
|
762
|
+
pushFinalized = true
|
|
763
|
+
pushFinalizeReject(error)
|
|
764
|
+
}
|
|
765
|
+
}
|
|
752
766
|
else
|
|
753
767
|
pendingCredit += response.credit
|
|
754
768
|
})
|
|
@@ -790,6 +804,9 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
|
|
|
790
804
|
refreshTimeout()
|
|
791
805
|
pendingCredit = 0
|
|
792
806
|
}
|
|
807
|
+
else if (pendingCredit > 0)
|
|
808
|
+
/* protocol violation: receiver sent credit before ack despite not granting initial credit */
|
|
809
|
+
throw new Error(`push to sink "${name}" received unsolicited credit (credit-flow disabled)`)
|
|
793
810
|
|
|
794
811
|
/* register credit gate at instance level */
|
|
795
812
|
if (creditGate) {
|
package/src/mqtt-plus-source.ts
CHANGED
|
@@ -263,10 +263,9 @@ export class SourceTrait<T extends APISchema = APISchema> extends ServiceTrait<T
|
|
|
263
263
|
try {
|
|
264
264
|
if (topicName !== request.name)
|
|
265
265
|
throw new Error(`source name mismatch (topic: "${topicName}", payload: "${request.name}")`)
|
|
266
|
-
if (auth)
|
|
267
|
-
info.authenticated = await this.authenticated(sender, request.auth, auth, `source "${name}"`)
|
|
268
266
|
|
|
269
|
-
/* register credit/cancel handler (
|
|
267
|
+
/* register credit/cancel handler early (before any await) so cancel
|
|
268
|
+
signals arriving during async authentication are not lost */
|
|
270
269
|
this.onResponse.set(`source-fetch-credit:${requestId}`, (creditParsed: SourceFetchCredit) => {
|
|
271
270
|
if (abortSignal.aborted)
|
|
272
271
|
return
|
|
@@ -295,10 +294,14 @@ export class SourceTrait<T extends APISchema = APISchema> extends ServiceTrait<T
|
|
|
295
294
|
this.onResponse.delete(`source-fetch-credit:${requestId}`)
|
|
296
295
|
})
|
|
297
296
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
297
|
+
/* check for authentication */
|
|
298
|
+
if (auth)
|
|
299
|
+
info.authenticated = await this.authenticated(sender, request.auth, auth, `source "${name}"`)
|
|
300
|
+
|
|
301
|
+
/* finally call the handler callback */
|
|
302
|
+
const callbackPromise = Promise.resolve(callback(...params, info))
|
|
303
|
+
callbackPromise.catch(() => {}) /* guard against unhandled rejection if abort wins the race */
|
|
304
|
+
await Promise.race([ callbackPromise, abortPromise ])
|
|
302
305
|
|
|
303
306
|
/* check for valid data source */
|
|
304
307
|
if (!(info.stream instanceof Readable) && !(info.buffer instanceof Promise) && !(info.buffer instanceof Uint8Array))
|
|
@@ -329,9 +332,14 @@ export class SourceTrait<T extends APISchema = APISchema> extends ServiceTrait<T
|
|
|
329
332
|
await sendStreamAsChunks(info.stream, this.options.chunkSize, sendChunk, creditGate, abortSignal)
|
|
330
333
|
else if (info.buffer instanceof Promise || info.buffer instanceof Uint8Array) {
|
|
331
334
|
/* handle Buffer result */
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
+
let buffer: Uint8Array
|
|
336
|
+
if (info.buffer instanceof Promise) {
|
|
337
|
+
const bufferPromise = info.buffer
|
|
338
|
+
bufferPromise.catch(() => {}) /* guard against unhandled rejection if abort wins the race */
|
|
339
|
+
buffer = await Promise.race([ bufferPromise, abortPromise ])
|
|
340
|
+
}
|
|
341
|
+
else
|
|
342
|
+
buffer = info.buffer
|
|
335
343
|
|
|
336
344
|
/* re-check abort: a late info.buffer resolution could win the race */
|
|
337
345
|
/* by a microtask margin even after abort fired -- discard silently */
|