mqtt-plus 1.4.18 → 1.4.20
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 +11 -0
- package/dst-stage1/mqtt-plus-api.js +0 -1
- package/dst-stage1/mqtt-plus-auth.d.ts +2 -2
- 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.d.ts +2 -2
- 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 +11 -11
- package/package.d/{vite+8.0.8.patch → vite+8.0.12.patch} +2 -2
- package/package.json +14 -14
- package/src/mqtt-plus-auth.ts +2 -2
- package/src/mqtt-plus-options.ts +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.59.3.patch} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
ChangeLog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
1.4.20 (2026-05-13)
|
|
6
|
+
-------------------
|
|
7
|
+
|
|
8
|
+
- UPDATE: upgrade NPM dependencies
|
|
9
|
+
|
|
10
|
+
1.4.19 (2026-04-18)
|
|
11
|
+
-------------------
|
|
12
|
+
|
|
13
|
+
- IMPROVEMENT: improve error handling in source and sink traits
|
|
14
|
+
- UPDATE: upgrade NPM dependencies
|
|
15
|
+
|
|
5
16
|
1.4.18 (2026-04-17)
|
|
6
17
|
-------------------
|
|
7
18
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { APISchema } from "./mqtt-plus-api";
|
|
2
2
|
import { MetaTrait } from "./mqtt-plus-meta";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
type AuthMode = "require" | "optional";
|
|
4
|
+
type AuthRole = string;
|
|
5
5
|
export type AuthOption = AuthRole | {
|
|
6
6
|
mode: AuthMode;
|
|
7
7
|
roles: AuthRole[];
|
|
@@ -4,8 +4,8 @@ type TopicMatching = {
|
|
|
4
4
|
operation: string;
|
|
5
5
|
peerId?: string;
|
|
6
6
|
};
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
type TopicMake = (name: string, operation: string, peerId?: string) => string;
|
|
8
|
+
type TopicMatch = (topic: string) => TopicMatching | null;
|
|
9
9
|
export interface APIOptions {
|
|
10
10
|
id: string;
|
|
11
11
|
codec: "cbor" | "json";
|
|
@@ -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(() => {
|