@naturalcycles/nodejs-lib 15.125.2 → 15.125.3
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.
|
@@ -116,4 +116,5 @@ export declare class Pipeline<T = unknown> {
|
|
|
116
116
|
forEach(fn: AbortableAsyncMapper<T, void>, opt?: TransformMapOptions<T, void> & TransformLogProgressOptions<T>): Promise<void>;
|
|
117
117
|
forEachSync(fn: AbortableMapper<T, void>, opt?: TransformMapSyncOptions<T, void> & TransformLogProgressOptions<T>): Promise<void>;
|
|
118
118
|
run(): Promise<void>;
|
|
119
|
+
private isGracefullyAborted;
|
|
119
120
|
}
|
package/dist/stream/pipeline.js
CHANGED
|
@@ -347,11 +347,18 @@ export class Pipeline {
|
|
|
347
347
|
});
|
|
348
348
|
}
|
|
349
349
|
catch (err) {
|
|
350
|
-
|
|
350
|
+
// The abort reason is checked on the signal rather than on `err.cause`: when the source has
|
|
351
|
+
// already emitted `end` by the time the abort lands (small `Readable.from` sources), node's
|
|
352
|
+
// pipeline reports ERR_STREAM_PREMATURE_CLOSE without a cause instead of the AbortError.
|
|
353
|
+
if (this.isGracefullyAborted()) {
|
|
351
354
|
console.log('pipeline gracefully aborted'); // todo: this message may be removed later
|
|
352
355
|
return;
|
|
353
356
|
}
|
|
354
357
|
throw err;
|
|
355
358
|
}
|
|
356
359
|
}
|
|
360
|
+
isGracefullyAborted() {
|
|
361
|
+
const { aborted, reason } = this.abortableSignal;
|
|
362
|
+
return aborted && reason instanceof Error && reason.message === PIPELINE_GRACEFUL_ABORT;
|
|
363
|
+
}
|
|
357
364
|
}
|
|
@@ -31,6 +31,7 @@ export function transformMap(mapper, opt = {}) {
|
|
|
31
31
|
let inFlight = 0;
|
|
32
32
|
let blockedCallback = null;
|
|
33
33
|
let flushBlocked = null;
|
|
34
|
+
let destroyBlocked = null;
|
|
34
35
|
// Warmup - cached concurrency to reduce Date.now() syscalls
|
|
35
36
|
let warmupComplete = warmupSeconds <= 0 || maxConcurrency <= 1;
|
|
36
37
|
let concurrency = warmupComplete ? maxConcurrency : 1;
|
|
@@ -117,7 +118,20 @@ export function transformMap(mapper, opt = {}) {
|
|
|
117
118
|
if (inFlight === 0 && flushBlocked) {
|
|
118
119
|
flushBlocked.resolve();
|
|
119
120
|
}
|
|
121
|
+
// Let a pending destroy complete once nothing is in flight any more
|
|
122
|
+
if (inFlight === 0 && destroyBlocked) {
|
|
123
|
+
destroyBlocked.resolve();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
async destroy(err, cb) {
|
|
128
|
+
// An upstream abort (e.g. `limit` or END) destroys this stream while mappers are still running.
|
|
129
|
+
// The close is held until they settle, so that the pipeline resolves only once their side effects are done.
|
|
130
|
+
if (inFlight > 0) {
|
|
131
|
+
destroyBlocked = pDefer();
|
|
132
|
+
await destroyBlocked;
|
|
120
133
|
}
|
|
134
|
+
cb(err);
|
|
121
135
|
},
|
|
122
136
|
async flush(cb) {
|
|
123
137
|
// Wait for all in-flight operations to complete
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/nodejs-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "15.125.
|
|
4
|
+
"version": "15.125.3",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@naturalcycles/js-lib": "^15",
|
|
7
7
|
"@standard-schema/spec": "^1",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"yaml": "^2"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@naturalcycles/dev-lib": "
|
|
17
|
+
"@naturalcycles/dev-lib": "20.65.1",
|
|
18
18
|
"typescript": "^7"
|
|
19
19
|
},
|
|
20
20
|
"exports": {
|
package/src/stream/pipeline.ts
CHANGED
|
@@ -466,11 +466,19 @@ export class Pipeline<T = unknown> {
|
|
|
466
466
|
signal: this.abortableSignal,
|
|
467
467
|
})
|
|
468
468
|
} catch (err) {
|
|
469
|
-
|
|
469
|
+
// The abort reason is checked on the signal rather than on `err.cause`: when the source has
|
|
470
|
+
// already emitted `end` by the time the abort lands (small `Readable.from` sources), node's
|
|
471
|
+
// pipeline reports ERR_STREAM_PREMATURE_CLOSE without a cause instead of the AbortError.
|
|
472
|
+
if (this.isGracefullyAborted()) {
|
|
470
473
|
console.log('pipeline gracefully aborted') // todo: this message may be removed later
|
|
471
474
|
return
|
|
472
475
|
}
|
|
473
476
|
throw err
|
|
474
477
|
}
|
|
475
478
|
}
|
|
479
|
+
|
|
480
|
+
private isGracefullyAborted(): boolean {
|
|
481
|
+
const { aborted, reason } = this.abortableSignal
|
|
482
|
+
return aborted && reason instanceof Error && reason.message === PIPELINE_GRACEFUL_ABORT
|
|
483
|
+
}
|
|
476
484
|
}
|
|
@@ -127,6 +127,7 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
127
127
|
let inFlight = 0
|
|
128
128
|
let blockedCallback: (() => void) | null = null
|
|
129
129
|
let flushBlocked: DeferredPromise | null = null
|
|
130
|
+
let destroyBlocked: DeferredPromise | null = null
|
|
130
131
|
|
|
131
132
|
// Warmup - cached concurrency to reduce Date.now() syscalls
|
|
132
133
|
let warmupComplete = warmupSeconds <= 0 || maxConcurrency <= 1
|
|
@@ -220,7 +221,22 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
220
221
|
if (inFlight === 0 && flushBlocked) {
|
|
221
222
|
flushBlocked.resolve()
|
|
222
223
|
}
|
|
224
|
+
|
|
225
|
+
// Let a pending destroy complete once nothing is in flight any more
|
|
226
|
+
if (inFlight === 0 && destroyBlocked) {
|
|
227
|
+
destroyBlocked.resolve()
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
async destroy(err, cb) {
|
|
232
|
+
// An upstream abort (e.g. `limit` or END) destroys this stream while mappers are still running.
|
|
233
|
+
// The close is held until they settle, so that the pipeline resolves only once their side effects are done.
|
|
234
|
+
if (inFlight > 0) {
|
|
235
|
+
destroyBlocked = pDefer()
|
|
236
|
+
await destroyBlocked
|
|
223
237
|
}
|
|
238
|
+
|
|
239
|
+
cb(err)
|
|
224
240
|
},
|
|
225
241
|
async flush(cb) {
|
|
226
242
|
// Wait for all in-flight operations to complete
|