documonster 0.20.2 → 0.21.0
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/README.md +1 -1
- package/README_zh.md +1 -1
- package/THIRD_PARTY_NOTICES.md +34 -2
- package/dist/esm/modules/archive/core/byte-queue.js +2 -1
- package/dist/esm/modules/archive/unzip/stream.browser.js +6 -0
- package/dist/esm/modules/csv/parse/async.js +2 -1
- package/dist/esm/modules/excel/bridge/csv-bridge.js +1 -1
- package/dist/esm/modules/excel/core/xlsx-io.browser.js +5 -9
- package/dist/esm/modules/excel/core/xlsx-io.js +1 -3
- package/dist/esm/modules/excel/core/xlsx-stream.js +3 -1
- package/dist/esm/modules/excel/stream/workbook-reader.browser.js +10 -10
- package/dist/esm/modules/excel/stream/workbook-reader.js +30 -6
- package/dist/esm/modules/stream/browser/collector.js +3 -5
- package/dist/esm/modules/stream/browser/compose.js +13 -228
- package/dist/esm/modules/stream/browser/duplex-pair.js +71 -0
- package/dist/esm/modules/stream/browser/factories.js +35 -217
- package/dist/esm/modules/stream/browser/pipeline.js +43 -478
- package/dist/esm/modules/stream/browser/utils.js +21 -247
- package/dist/esm/modules/stream/browser/web.js +25 -0
- package/dist/esm/modules/stream/buffered-stream.js +8 -6
- package/dist/esm/modules/stream/core/factories.js +166 -0
- package/dist/esm/modules/stream/core/pipeline-api.js +32 -0
- package/dist/esm/modules/stream/core/sink-adapter.js +94 -24
- package/dist/esm/modules/stream/core/stream-helpers.js +160 -0
- package/dist/esm/modules/stream/core/utils.js +0 -23
- package/dist/esm/modules/stream/core/web-conversions.js +31 -0
- package/dist/esm/modules/stream/core/web-pipeline.js +90 -0
- package/dist/esm/modules/stream/index.base.js +2 -2
- package/dist/esm/modules/stream/index.browser.js +13 -19
- package/dist/esm/modules/stream/index.js +6 -10
- package/dist/esm/modules/stream/kernel/classes.js +618 -0
- package/dist/esm/modules/stream/kernel/compose-impl.js +225 -0
- package/dist/esm/modules/stream/kernel/destroy.js +255 -0
- package/dist/esm/modules/stream/kernel/duplexify.js +355 -0
- package/dist/esm/modules/stream/kernel/end-of-stream.js +212 -0
- package/dist/esm/modules/stream/kernel/errors.js +215 -0
- package/dist/esm/modules/stream/kernel/from.js +202 -0
- package/dist/esm/modules/stream/kernel/legacy.js +35 -0
- package/dist/esm/modules/stream/kernel/operators.js +285 -0
- package/dist/esm/modules/stream/kernel/pipeline-impl.js +561 -0
- package/dist/esm/modules/stream/kernel/readable-impl.js +963 -0
- package/dist/esm/modules/stream/kernel/state.js +253 -0
- package/dist/esm/modules/stream/kernel/stream-utils.js +212 -0
- package/dist/esm/modules/stream/kernel/web-adapters.js +584 -0
- package/dist/esm/modules/stream/kernel/writable.js +651 -0
- package/dist/esm/modules/stream/node/collector.js +7 -1
- package/dist/esm/modules/stream/node/compose.js +21 -206
- package/dist/esm/modules/stream/node/factories.js +34 -238
- package/dist/esm/modules/stream/node/pipeline.js +40 -94
- package/dist/esm/modules/stream/node/utils.js +15 -239
- package/dist/esm/modules/stream/node/web.js +27 -0
- package/dist/esm/modules/stream/pull-stream.js +15 -9
- package/dist/esm/modules/word/writer/streaming-writer.js +3 -0
- package/dist/iife/THIRD_PARTY_NOTICES.md +34 -2
- package/dist/iife/documonster.archive.iife.min.js +4 -4
- package/dist/iife/documonster.csv.iife.min.js +19 -19
- package/dist/iife/documonster.draw.iife.min.js +1 -1
- package/dist/iife/documonster.excel.iife.min.js +50 -50
- package/dist/iife/documonster.formula.iife.min.js +1 -1
- package/dist/iife/documonster.markdown.iife.min.js +1 -1
- package/dist/iife/documonster.mermaid.iife.min.js +1 -1
- package/dist/iife/documonster.pdf.iife.min.js +1 -1
- package/dist/iife/documonster.stream.iife.min.js +2 -2
- package/dist/iife/documonster.word.iife.min.js +60 -60
- package/dist/iife/documonster.xml.iife.min.js +1 -1
- package/dist/types/modules/excel/core/xlsx-io.browser.d.ts +5 -9
- package/dist/types/modules/excel/core/xlsx-io.d.ts +1 -3
- package/dist/types/modules/excel/core/xlsx-stream.d.ts +3 -2
- package/dist/types/modules/excel/stream/workbook-reader.browser.d.ts +2 -1
- package/dist/types/modules/excel/stream/workbook-reader.d.ts +2 -1
- package/dist/types/modules/stream/browser/collector.d.ts +1 -1
- package/dist/types/modules/stream/browser/compose.d.ts +14 -15
- package/dist/types/modules/stream/browser/duplex-pair.d.ts +10 -0
- package/dist/types/modules/stream/browser/factories.d.ts +22 -79
- package/dist/types/modules/stream/browser/pipeline.d.ts +8 -40
- package/dist/types/modules/stream/browser/utils.d.ts +6 -47
- package/dist/types/modules/stream/browser/web.d.ts +18 -0
- package/dist/types/modules/stream/core/add-abort-signal.d.ts +2 -1
- package/dist/types/modules/stream/core/consumers.d.ts +1 -1
- package/dist/types/modules/stream/core/event-utils.d.ts +2 -1
- package/dist/types/modules/stream/core/factories.d.ts +129 -0
- package/dist/types/modules/stream/core/pipeline-api.d.ts +10 -0
- package/dist/types/modules/stream/core/sink-adapter.d.ts +18 -1
- package/dist/types/modules/stream/core/stream-helpers.d.ts +76 -0
- package/dist/types/modules/stream/core/utils.d.ts +0 -9
- package/dist/types/modules/stream/core/web-conversions.d.ts +68 -0
- package/dist/types/modules/stream/core/web-pipeline.d.ts +27 -0
- package/dist/types/modules/stream/index.base.d.ts +6 -4
- package/dist/types/modules/stream/index.browser.d.ts +13 -14
- package/dist/types/modules/stream/index.d.ts +7 -10
- package/dist/types/modules/stream/kernel/classes.d.ts +220 -0
- package/dist/types/modules/stream/kernel/compose-impl.d.ts +10 -0
- package/dist/types/modules/stream/kernel/destroy.d.ts +21 -0
- package/dist/types/modules/stream/kernel/duplexify.d.ts +31 -0
- package/dist/types/modules/stream/kernel/end-of-stream.d.ts +28 -0
- package/dist/types/modules/stream/kernel/errors.d.ts +42 -0
- package/dist/types/modules/stream/kernel/from.d.ts +17 -0
- package/dist/types/modules/stream/kernel/legacy.d.ts +20 -0
- package/dist/types/modules/stream/kernel/operators.d.ts +44 -0
- package/dist/types/modules/stream/kernel/pipeline-impl.d.ts +32 -0
- package/dist/types/modules/stream/kernel/readable-impl.d.ts +34 -0
- package/dist/types/modules/stream/kernel/state.d.ts +223 -0
- package/dist/types/modules/stream/kernel/stream-utils.d.ts +34 -0
- package/dist/types/modules/stream/kernel/web-adapters.d.ts +65 -0
- package/dist/types/modules/stream/kernel/writable.d.ts +107 -0
- package/dist/types/modules/stream/node/collector.d.ts +7 -2
- package/dist/types/modules/stream/node/compose.d.ts +31 -5
- package/dist/types/modules/stream/node/factories.d.ts +22 -85
- package/dist/types/modules/stream/node/pipeline.d.ts +4 -15
- package/dist/types/modules/stream/node/utils.d.ts +9 -49
- package/dist/types/modules/stream/node/web.d.ts +18 -0
- package/dist/types/modules/stream/pull-stream.d.ts +2 -2
- package/dist/types/modules/stream/types.d.ts +24 -6
- package/package.json +5 -3
- package/dist/esm/modules/stream/browser/_lazy.js +0 -30
- package/dist/esm/modules/stream/browser/chunk-buffer.js +0 -223
- package/dist/esm/modules/stream/browser/duplex.js +0 -979
- package/dist/esm/modules/stream/browser/helpers.js +0 -44
- package/dist/esm/modules/stream/browser/microtask-context.js +0 -60
- package/dist/esm/modules/stream/browser/passthrough.js +0 -16
- package/dist/esm/modules/stream/browser/pipe-manager.js +0 -161
- package/dist/esm/modules/stream/browser/readable.js +0 -2208
- package/dist/esm/modules/stream/browser/transform.js +0 -1241
- package/dist/esm/modules/stream/browser/writable.js +0 -1316
- package/dist/esm/modules/stream/core/end-args.js +0 -24
- package/dist/esm/modules/stream/node/writable.js +0 -125
- package/dist/esm/modules/stream/utils.base.js +0 -126
- package/dist/esm/modules/stream/utils.browser.js +0 -80
- package/dist/esm/modules/stream/utils.js +0 -81
- package/dist/types/modules/stream/browser/_lazy.d.ts +0 -20
- package/dist/types/modules/stream/browser/chunk-buffer.d.ts +0 -61
- package/dist/types/modules/stream/browser/duplex.d.ts +0 -299
- package/dist/types/modules/stream/browser/helpers.d.ts +0 -13
- package/dist/types/modules/stream/browser/microtask-context.d.ts +0 -44
- package/dist/types/modules/stream/browser/passthrough.d.ts +0 -11
- package/dist/types/modules/stream/browser/pipe-manager.d.ts +0 -38
- package/dist/types/modules/stream/browser/readable.d.ts +0 -379
- package/dist/types/modules/stream/browser/transform.d.ts +0 -314
- package/dist/types/modules/stream/browser/writable.d.ts +0 -238
- package/dist/types/modules/stream/core/end-args.d.ts +0 -20
- package/dist/types/modules/stream/node/writable.d.ts +0 -53
- package/dist/types/modules/stream/utils.base.d.ts +0 -63
- package/dist/types/modules/stream/utils.browser.d.ts +0 -23
- package/dist/types/modules/stream/utils.d.ts +0 -24
|
@@ -1,505 +1,70 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Browser Stream -
|
|
2
|
+
* Browser Stream - pipeline() and finished()
|
|
3
|
+
*
|
|
4
|
+
* The module's calling convention (always a promise, optional trailing
|
|
5
|
+
* callback, `{ signal, end }` options) over `pipelineImpl` and `eos`.
|
|
3
6
|
*/
|
|
4
|
-
import { Duplex } from "./duplex.js";
|
|
5
|
-
import { createListenerRegistry } from "./helpers.js";
|
|
6
|
-
import { Readable } from "./readable.js";
|
|
7
|
-
import { Transform } from "./transform.js";
|
|
8
|
-
import { Writable } from "./writable.js";
|
|
9
7
|
import { createFinishedAll } from "../core/finished-all.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
8
|
+
import { createPipeline } from "../core/pipeline-api.js";
|
|
9
|
+
import { isWebOnlyPipeline, pipeWebStreams } from "../core/web-pipeline.js";
|
|
10
|
+
import { kernel } from "../kernel/classes.js";
|
|
11
|
+
import { eos } from "../kernel/end-of-stream.js";
|
|
12
|
+
import { pipelineImpl } from "../kernel/pipeline-impl.js";
|
|
13
|
+
import { isNodeStream } from "../kernel/stream-utils.js";
|
|
13
14
|
export { isPipelineOptions } from "../core/options.js";
|
|
14
|
-
function asProbe(stream) {
|
|
15
|
-
return stream;
|
|
16
|
-
}
|
|
17
|
-
function supportsReadableSide(stream) {
|
|
18
|
-
// Check for readable-side properties/methods. Writable.pipe() is a no-op
|
|
19
|
-
// that emits ERR_STREAM_CANNOT_PIPE, so `typeof pipe === "function"` alone
|
|
20
|
-
// is not sufficient — we must also see an actual readable indicator.
|
|
21
|
-
return "readableEnded" in stream || "readable" in stream || typeof stream.read === "function";
|
|
22
|
-
}
|
|
23
|
-
function supportsWritableSide(stream) {
|
|
24
|
-
return "writableFinished" in stream || "writable" in stream || typeof stream.write === "function";
|
|
25
|
-
}
|
|
26
|
-
function isStreamCompleted(stream) {
|
|
27
|
-
const readableDone = !supportsReadableSide(stream) || !!stream.readableEnded;
|
|
28
|
-
const writableDone = !supportsWritableSide(stream) || !!stream.writableFinished;
|
|
29
|
-
return readableDone && writableDone;
|
|
30
|
-
}
|
|
31
|
-
function createPrematureCloseError() {
|
|
32
|
-
const err = new Error("Premature close");
|
|
33
|
-
err.code = "ERR_STREAM_PREMATURE_CLOSE";
|
|
34
|
-
err.name = "Error [ERR_STREAM_PREMATURE_CLOSE]";
|
|
35
|
-
return err;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Wait for a stream's 'close' event after it has finished/ended.
|
|
39
|
-
* If the stream will emit close (autoDestroy && emitClose), wait for it.
|
|
40
|
-
* Otherwise resolve immediately (matching Node.js pipeline behavior where
|
|
41
|
-
* autoDestroy:false streams don't wait for close).
|
|
42
|
-
*/
|
|
43
|
-
function waitForClose(stream, done, registry) {
|
|
44
|
-
// Already closed — resolve immediately.
|
|
45
|
-
if (stream.closed || stream._closed) {
|
|
46
|
-
done();
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
const hasInternals = "_emitClose" in stream && "_autoDestroy" in stream;
|
|
50
|
-
const willEmitClose = hasInternals && stream._emitClose !== false && stream._autoDestroy !== false;
|
|
51
|
-
if (willEmitClose) {
|
|
52
|
-
registry.once(stream, "close", () => done());
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
done();
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
export function toBrowserPipelineStream(stream) {
|
|
59
|
-
if (stream instanceof Readable ||
|
|
60
|
-
stream instanceof Writable ||
|
|
61
|
-
stream instanceof Transform ||
|
|
62
|
-
stream instanceof Duplex) {
|
|
63
|
-
return stream;
|
|
64
|
-
}
|
|
65
|
-
if (isTransformStream(stream)) {
|
|
66
|
-
return Transform.fromWeb(stream);
|
|
67
|
-
}
|
|
68
|
-
if (isReadableStream(stream)) {
|
|
69
|
-
return Readable.fromWeb(stream);
|
|
70
|
-
}
|
|
71
|
-
if (isWritableStream(stream)) {
|
|
72
|
-
return Writable.fromWeb(stream);
|
|
73
|
-
}
|
|
74
|
-
// A bare (async) iterable source — `pipeline([chunk], dest)`. Node's pipeline
|
|
75
|
-
// accepts one; without this the browser threw `current.pipe is not a
|
|
76
|
-
// function`. Generator STAGES are handled separately (they are functions, and
|
|
77
|
-
// consume the previous stage rather than being a source).
|
|
78
|
-
if (typeof stream !== "function" && isIterableSource(stream)) {
|
|
79
|
-
return Readable.from(stream);
|
|
80
|
-
}
|
|
81
|
-
return stream;
|
|
82
|
-
}
|
|
83
|
-
/** `true` for an (async) iterable that is not already one of our streams. */
|
|
84
|
-
function isIterableSource(value) {
|
|
85
|
-
if (value == null || (typeof value !== "object" && typeof value !== "string")) {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
const probe = value;
|
|
89
|
-
if (typeof probe.pipe === "function") {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
return (typeof probe[Symbol.asyncIterator] === "function" ||
|
|
93
|
-
typeof probe[Symbol.iterator] === "function");
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Check if a pipeline stage is a generator/async generator function.
|
|
97
|
-
* These are used as transform stages: fn(source) => AsyncIterable.
|
|
98
|
-
*/
|
|
99
|
-
function isGeneratorFunction(fn) {
|
|
100
|
-
return typeof fn === "function" && !(fn instanceof Readable) && !(fn instanceof Writable);
|
|
101
|
-
}
|
|
102
15
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* generator function, and produces a new Readable from the resulting iterable.
|
|
106
|
-
*/
|
|
107
|
-
function applyGeneratorStage(source, fn) {
|
|
108
|
-
const iterable = fn(source);
|
|
109
|
-
return Readable.from(iterable);
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Pipeline streams together with proper error handling and cleanup.
|
|
113
|
-
* Supports both callback and promise-based usage like Node.js.
|
|
114
|
-
*
|
|
115
|
-
* @example
|
|
116
|
-
* // Promise usage
|
|
117
|
-
* await pipeline(source, transform, destination);
|
|
118
|
-
*
|
|
119
|
-
* @example
|
|
120
|
-
* // With options
|
|
121
|
-
* await pipeline(source, transform, destination, { signal: controller.signal });
|
|
16
|
+
* Pipe `streams` together and settle when all of them are done, destroying
|
|
17
|
+
* every stage on the first error.
|
|
122
18
|
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* if (err) console.error('Pipeline failed', err);
|
|
127
|
-
* });
|
|
19
|
+
* Accepts Node-style streams, Web streams, (async) iterables as a source and
|
|
20
|
+
* generator functions as stages. A pipeline of Web streams only runs on the
|
|
21
|
+
* platform's `pipeTo`.
|
|
128
22
|
*/
|
|
23
|
+
const runPipeline = createPipeline((stages, options) => isWebOnlyPipeline(stages)
|
|
24
|
+
? pipeWebStreams(stages, options)
|
|
25
|
+
: new Promise((resolve, reject) => {
|
|
26
|
+
pipelineImpl(kernel, stages, err => (err ? reject(err) : resolve()), options);
|
|
27
|
+
}));
|
|
129
28
|
export function pipeline(...args) {
|
|
130
|
-
|
|
131
|
-
let streams;
|
|
132
|
-
let options = {};
|
|
133
|
-
let callback;
|
|
134
|
-
const lastArg = args[args.length - 1];
|
|
135
|
-
if (typeof lastArg === "function") {
|
|
136
|
-
callback = lastArg;
|
|
137
|
-
// Check for combined style: pipeline(s1, s2, ..., options, callback)
|
|
138
|
-
const secondToLast = args[args.length - 2];
|
|
139
|
-
if (isPipelineOptions(secondToLast)) {
|
|
140
|
-
options = secondToLast;
|
|
141
|
-
streams = args.slice(0, -2);
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
// Callback only: pipeline(s1, s2, ..., callback)
|
|
145
|
-
streams = args.slice(0, -1);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
else if (isPipelineOptions(lastArg)) {
|
|
149
|
-
// Options only: pipeline(s1, s2, ..., { signal })
|
|
150
|
-
options = lastArg;
|
|
151
|
-
streams = args.slice(0, -1);
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
// No callback or options: pipeline(s1, s2, s3)
|
|
155
|
-
streams = args;
|
|
156
|
-
}
|
|
157
|
-
const promise = new Promise((resolve, reject) => {
|
|
158
|
-
if (streams.length < 2) {
|
|
159
|
-
const err = new Error("Pipeline requires at least 2 streams");
|
|
160
|
-
reject(err);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
// Pre-process: normalize streams and resolve generator functions.
|
|
164
|
-
// Generator functions consume their source as an async iterable and produce
|
|
165
|
-
// a new Readable — they do NOT participate in .pipe() chains.
|
|
166
|
-
// We build a flat list of pipe-able stream stages.
|
|
167
|
-
const rawStages = streams.map(toBrowserPipelineStream);
|
|
168
|
-
// allStreams: every stream created (for cleanup on error).
|
|
169
|
-
// pipeStages: only the streams that need .pipe() chaining.
|
|
170
|
-
const allStreams = [];
|
|
171
|
-
const pipeStages = [];
|
|
172
|
-
let current = asProbe(rawStages[0]);
|
|
173
|
-
allStreams.push(current);
|
|
174
|
-
pipeStages.push(current);
|
|
175
|
-
for (let i = 1; i < rawStages.length; i++) {
|
|
176
|
-
const stage = rawStages[i];
|
|
177
|
-
if (isGeneratorFunction(stage)) {
|
|
178
|
-
// Generator consumes `current` internally → produces a new Readable.
|
|
179
|
-
// This Readable replaces `current` as the source for subsequent stages.
|
|
180
|
-
current = asProbe(applyGeneratorStage(current, stage));
|
|
181
|
-
allStreams.push(current);
|
|
182
|
-
// Replace the last entry in pipeStages (the consumed source) with
|
|
183
|
-
// the generator-produced Readable, so the next real stream stage
|
|
184
|
-
// will be piped FROM this Readable.
|
|
185
|
-
pipeStages[pipeStages.length - 1] = current;
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
const probe = asProbe(stage);
|
|
189
|
-
allStreams.push(probe);
|
|
190
|
-
pipeStages.push(probe);
|
|
191
|
-
current = probe;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
const source = pipeStages[0];
|
|
195
|
-
const destination = pipeStages[pipeStages.length - 1];
|
|
196
|
-
const transforms = pipeStages.slice(1, -1);
|
|
197
|
-
// Check for already-destroyed streams upfront.
|
|
198
|
-
// If a stream is already destroyed and its close event has already fired,
|
|
199
|
-
// our close listener would never fire — causing the pipeline to hang.
|
|
200
|
-
// Node.js detects this and immediately rejects with ERR_STREAM_PREMATURE_CLOSE.
|
|
201
|
-
for (const stream of allStreams) {
|
|
202
|
-
if (stream.destroyed) {
|
|
203
|
-
if (!isStreamCompleted(stream)) {
|
|
204
|
-
reject(createPrematureCloseError());
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
let completed = false;
|
|
210
|
-
const registry = createListenerRegistry();
|
|
211
|
-
let onAbort;
|
|
212
|
-
const cleanupWithSignal = (error) => {
|
|
213
|
-
if (completed) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
completed = true;
|
|
217
|
-
if (onAbort && options.signal) {
|
|
218
|
-
options.signal.removeEventListener("abort", onAbort);
|
|
219
|
-
}
|
|
220
|
-
// Node.js only destroys streams on ERROR, NOT on success.
|
|
221
|
-
// On error, pass the error to destroy(); on success, leave streams intact.
|
|
222
|
-
if (error) {
|
|
223
|
-
const noop = () => { };
|
|
224
|
-
for (const stream of allStreams) {
|
|
225
|
-
if (typeof stream.on === "function") {
|
|
226
|
-
stream.on("error", noop);
|
|
227
|
-
}
|
|
228
|
-
if (typeof stream.destroy === "function" && !stream.destroyed) {
|
|
229
|
-
stream.destroy(error);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
registry.cleanup();
|
|
234
|
-
if (error) {
|
|
235
|
-
reject(error);
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
resolve();
|
|
239
|
-
}
|
|
240
|
-
};
|
|
241
|
-
// Handle abort signal
|
|
242
|
-
if (options.signal) {
|
|
243
|
-
if (options.signal.aborted) {
|
|
244
|
-
cleanupWithSignal(createAbortError(options.signal.reason));
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
onAbort = () => cleanupWithSignal(createAbortError(options.signal.reason));
|
|
248
|
-
options.signal.addEventListener("abort", onAbort);
|
|
249
|
-
}
|
|
250
|
-
// Chain the streams
|
|
251
|
-
current = source;
|
|
252
|
-
for (const transform of transforms) {
|
|
253
|
-
current.pipe(transform);
|
|
254
|
-
current = transform;
|
|
255
|
-
}
|
|
256
|
-
// Pipe to destination
|
|
257
|
-
if (options.end !== false) {
|
|
258
|
-
current.pipe(destination);
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
// Don't end destination
|
|
262
|
-
let paused = false;
|
|
263
|
-
let waitingForDrain = false;
|
|
264
|
-
const onDrain = () => {
|
|
265
|
-
waitingForDrain = false;
|
|
266
|
-
if (paused && typeof current.resume === "function") {
|
|
267
|
-
paused = false;
|
|
268
|
-
current.resume();
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
const onData = (chunk) => {
|
|
272
|
-
const ok = destination.write(chunk);
|
|
273
|
-
if (!ok && !waitingForDrain) {
|
|
274
|
-
waitingForDrain = true;
|
|
275
|
-
if (!paused && typeof current.pause === "function") {
|
|
276
|
-
paused = true;
|
|
277
|
-
current.pause();
|
|
278
|
-
}
|
|
279
|
-
registry.once(destination, "drain", onDrain);
|
|
280
|
-
}
|
|
281
|
-
};
|
|
282
|
-
registry.add(current, "data", onData);
|
|
283
|
-
// end:false — wait for source to fully close (not just 'end').
|
|
284
|
-
// Node.js pipeline uses finished(source, {writable:false}) internally,
|
|
285
|
-
// which waits until the 'close' event when the stream will emit close.
|
|
286
|
-
registry.once(current, "end", () => {
|
|
287
|
-
// Source ended — now wait for its close event (autoDestroy → close)
|
|
288
|
-
// before resolving the pipeline. If the stream has already closed
|
|
289
|
-
// (or won't emit close), resolve immediately.
|
|
290
|
-
waitForClose(current, cleanupWithSignal, registry);
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
// Handle completion — wait for destination to fully close, not just finish.
|
|
294
|
-
// Node.js pipeline internally uses finished(destination) which waits for
|
|
295
|
-
// 'close' after 'finish' when the stream will emit close (autoDestroy &&
|
|
296
|
-
// emitClose). For streams that won't emit close (autoDestroy: false),
|
|
297
|
-
// resolve on 'finish' alone (matching Node.js behavior).
|
|
298
|
-
if (options.end !== false) {
|
|
299
|
-
registry.once(destination, "finish", () => {
|
|
300
|
-
waitForClose(destination, cleanupWithSignal, registry);
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
// Node parity: close before completion is a premature close error.
|
|
304
|
-
for (const stream of allStreams) {
|
|
305
|
-
registry.once(stream, "close", () => {
|
|
306
|
-
if (completed) {
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
if (!isStreamCompleted(stream)) {
|
|
310
|
-
cleanupWithSignal(createPrematureCloseError());
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
// Handle errors on all streams
|
|
315
|
-
for (const stream of allStreams) {
|
|
316
|
-
registry.once(stream, "error", (err) => cleanupWithSignal(err));
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
// If callback provided, use it
|
|
320
|
-
if (callback) {
|
|
321
|
-
promise.then(() => callback()).catch(err => callback(err));
|
|
322
|
-
}
|
|
323
|
-
return promise;
|
|
29
|
+
return runPipeline(...args);
|
|
324
30
|
}
|
|
325
|
-
// =============================================================================
|
|
326
|
-
// Finished
|
|
327
|
-
// =============================================================================
|
|
328
31
|
/**
|
|
329
|
-
*
|
|
330
|
-
* Node.js compatible with support for options and callbacks.
|
|
331
|
-
*
|
|
332
|
-
* @example
|
|
333
|
-
* // Promise usage
|
|
334
|
-
* await finished(stream);
|
|
335
|
-
*
|
|
336
|
-
* @example
|
|
337
|
-
* // With options
|
|
338
|
-
* await finished(stream, { readable: false }); // Only wait for writable side
|
|
32
|
+
* Settle when `stream` has finished, errored or closed early.
|
|
339
33
|
*
|
|
340
|
-
*
|
|
341
|
-
* // Callback usage
|
|
342
|
-
* finished(stream, (err) => {
|
|
343
|
-
* if (err) console.error('Stream error', err);
|
|
344
|
-
* });
|
|
34
|
+
* Web streams are adapted first; that takes their lock, as reading them does.
|
|
345
35
|
*/
|
|
346
36
|
export function finished(stream, optionsOrCallback, callback) {
|
|
347
|
-
let options
|
|
37
|
+
let options;
|
|
348
38
|
let cb;
|
|
349
39
|
if (typeof optionsOrCallback === "function") {
|
|
350
40
|
cb = optionsOrCallback;
|
|
351
41
|
}
|
|
352
42
|
else {
|
|
353
|
-
options = optionsOrCallback
|
|
43
|
+
options = optionsOrCallback;
|
|
354
44
|
cb = callback;
|
|
355
45
|
}
|
|
356
|
-
const promise =
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
const cleanup = () => {
|
|
362
|
-
registry.cleanup();
|
|
363
|
-
if (onAbort && options.signal) {
|
|
364
|
-
options.signal.removeEventListener("abort", onAbort);
|
|
365
|
-
}
|
|
366
|
-
};
|
|
367
|
-
const done = (err) => {
|
|
368
|
-
if (resolved) {
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
resolved = true;
|
|
372
|
-
cleanup();
|
|
373
|
-
if (err) {
|
|
374
|
-
reject(err);
|
|
375
|
-
}
|
|
376
|
-
else {
|
|
377
|
-
resolve();
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
// Handle abort signal
|
|
381
|
-
if (options.signal) {
|
|
382
|
-
if (options.signal.aborted) {
|
|
383
|
-
done(createAbortError(options.signal.reason));
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
onAbort = () => done(createAbortError(options.signal.reason));
|
|
387
|
-
options.signal.addEventListener("abort", onAbort);
|
|
388
|
-
}
|
|
389
|
-
// Node.js: if the stream is already destroyed, resolve/reject immediately.
|
|
390
|
-
// An already-destroyed stream with an error rejects; otherwise it's premature close.
|
|
391
|
-
const s = asProbe(normalizedStream);
|
|
392
|
-
if (s.destroyed || s._destroyed) {
|
|
393
|
-
if (s.errored || s._errored) {
|
|
394
|
-
done(s.errored ?? s._errored);
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
// Already destroyed without error — check if it finished gracefully.
|
|
398
|
-
// Respect options.readable / options.writable to match Node.js behavior:
|
|
399
|
-
// if the caller only cares about one side, only check that side.
|
|
400
|
-
const supportsReadable = "readableEnded" in s || "readable" in s || typeof s.read === "function";
|
|
401
|
-
const supportsWritable = "writableFinished" in s || "writable" in s || typeof s.write === "function";
|
|
402
|
-
const checkReadable = options.readable !== false && supportsReadable;
|
|
403
|
-
const checkWritable = options.writable !== false && supportsWritable;
|
|
404
|
-
const readableOk = !checkReadable || !!(s.readableEnded || s._endEmitted);
|
|
405
|
-
const writableOk = !checkWritable || !!(s.writableFinished || s._finished);
|
|
406
|
-
if (readableOk && writableOk) {
|
|
407
|
-
done();
|
|
408
|
-
}
|
|
409
|
-
else {
|
|
410
|
-
done(createPrematureCloseError());
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
const probe = asProbe(normalizedStream);
|
|
416
|
-
const supportsReadable = "readableEnded" in probe || "readable" in probe || typeof probe.read === "function";
|
|
417
|
-
const supportsWritable = "writableFinished" in probe || "writable" in probe || typeof probe.write === "function";
|
|
418
|
-
const checkReadable = options.readable !== false && supportsReadable;
|
|
419
|
-
const checkWritable = options.writable !== false && supportsWritable;
|
|
420
|
-
let readableDone = !checkReadable || !!probe.readableEnded;
|
|
421
|
-
let writableDone = !checkWritable || !!probe.writableFinished;
|
|
422
|
-
// Node.js finished() waits for the 'close' event before resolving when
|
|
423
|
-
// the stream will actually emit 'close'. Node.js computes willEmitClose as:
|
|
424
|
-
// state.autoDestroy && state.emitClose && state.closed === false
|
|
425
|
-
// This means: if autoDestroy is false, close won't fire automatically after
|
|
426
|
-
// finish, so finished() must NOT wait for it — otherwise it deadlocks.
|
|
427
|
-
//
|
|
428
|
-
// Guard: only consult _emitClose / _autoDestroy when the stream actually
|
|
429
|
-
// exposes them (i.e. our own Readable/Writable/Transform/Duplex instances).
|
|
430
|
-
// Third-party or duck-typed streams may lack these internals — for those we
|
|
431
|
-
// default to NOT waiting for 'close', which avoids a deadlock.
|
|
432
|
-
const s2 = probe;
|
|
433
|
-
const hasInternals = "_emitClose" in s2 && "_autoDestroy" in s2;
|
|
434
|
-
const willEmitClose = hasInternals && s2._emitClose !== false && s2._autoDestroy !== false && !s2._closed;
|
|
435
|
-
const maybeDone = () => {
|
|
436
|
-
if (readableDone && writableDone) {
|
|
437
|
-
if (willEmitClose) {
|
|
438
|
-
// Don't resolve yet — wait for 'close' event
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
done();
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
// Already finished?
|
|
445
|
-
if (readableDone && writableDone) {
|
|
446
|
-
// If the stream is already closed (or won't emit close), resolve now
|
|
447
|
-
if (!willEmitClose || probe.closed) {
|
|
448
|
-
done();
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
// Otherwise wait for 'close'
|
|
452
|
-
}
|
|
453
|
-
// Listen for events
|
|
454
|
-
if (checkWritable && !writableDone) {
|
|
455
|
-
registry.once(probe, "finish", () => {
|
|
456
|
-
writableDone = true;
|
|
457
|
-
maybeDone();
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
if (checkReadable && !readableDone) {
|
|
461
|
-
registry.once(probe, "end", () => {
|
|
462
|
-
readableDone = true;
|
|
463
|
-
maybeDone();
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
// Node.js: with error:false, don't listen for the 'error' event.
|
|
467
|
-
// Errors are still detected via stream.errored in the close handler.
|
|
468
|
-
if (options.error !== false) {
|
|
469
|
-
registry.once(probe, "error", (err) => done(err));
|
|
470
|
-
}
|
|
471
|
-
registry.once(probe, "close", () => {
|
|
472
|
-
const closedReadableDone = readableDone || !!probe.readableEnded;
|
|
473
|
-
const closedWritableDone = writableDone || !!probe.writableFinished;
|
|
474
|
-
if (closedReadableDone && closedWritableDone) {
|
|
475
|
-
readableDone = closedReadableDone;
|
|
476
|
-
writableDone = closedWritableDone;
|
|
477
|
-
// Even with error:false, check stream.errored — Node.js close handler
|
|
478
|
-
// still passes errors detected via stream.errored to the callback.
|
|
479
|
-
const streamErr = probe.errored ?? probe._errored;
|
|
480
|
-
done(streamErr ?? undefined);
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
// Premature close — stream closed before finishing.
|
|
484
|
-
// Check for stream error first (e.g. destroyed with error).
|
|
485
|
-
const streamErr = probe.errored ?? probe._errored;
|
|
486
|
-
if (streamErr) {
|
|
487
|
-
done(streamErr);
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
const err = new Error("Premature close");
|
|
491
|
-
err.code = "ERR_STREAM_PREMATURE_CLOSE";
|
|
492
|
-
err.name = "Error [ERR_STREAM_PREMATURE_CLOSE]";
|
|
493
|
-
done(err);
|
|
494
|
-
});
|
|
495
|
-
});
|
|
496
|
-
// If callback provided, use it
|
|
46
|
+
const promise = isNodeStream(stream)
|
|
47
|
+
? new Promise((resolve, reject) => {
|
|
48
|
+
eos(stream, options ?? null, err => (err ? reject(err) : resolve()));
|
|
49
|
+
})
|
|
50
|
+
: webFinished();
|
|
497
51
|
if (cb) {
|
|
498
|
-
|
|
52
|
+
const done = cb;
|
|
53
|
+
promise.then(() => done(), err => done(err));
|
|
499
54
|
}
|
|
500
55
|
return promise;
|
|
501
56
|
}
|
|
502
57
|
/**
|
|
503
|
-
*
|
|
58
|
+
* `finished()` on a Web stream is refused in the browser.
|
|
59
|
+
*
|
|
60
|
+
* The standard API has no way to observe a Web stream's completion without
|
|
61
|
+
* locking it, and nothing short of the reader or writer that drains it knows
|
|
62
|
+
* when its queue is empty — the Node-style side having ended does not mean
|
|
63
|
+
* the Web side has. Adapting the stream would take its lock and leave the
|
|
64
|
+
* caller unable to consume or close what it is waiting on.
|
|
504
65
|
*/
|
|
66
|
+
function webFinished() {
|
|
67
|
+
return Promise.reject(new TypeError("finished(): a Web stream's completion cannot be observed in the browser without " +
|
|
68
|
+
"locking it. Await the `closed` promise of the reader or writer you use instead."));
|
|
69
|
+
}
|
|
505
70
|
export const finishedAll = createFinishedAll(finished);
|