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
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream kernel - Duplex.from()
|
|
3
|
+
*
|
|
4
|
+
* Port of Node.js `lib/internal/streams/duplexify.js`: turn a stream, Web
|
|
5
|
+
* stream, iterable, async generator function, promise or `{ readable,
|
|
6
|
+
* writable }` pair into a Duplex. The stream classes are passed in so this module
|
|
7
|
+
* does not import `classes.ts`, which imports it.
|
|
8
|
+
*/
|
|
9
|
+
import { destroyer } from "./destroy.js";
|
|
10
|
+
import { eos } from "./end-of-stream.js";
|
|
11
|
+
import { createAbortError, errInvalidArgType, errInvalidReturnValue } from "./errors.js";
|
|
12
|
+
import { from } from "./from.js";
|
|
13
|
+
import { nextTick } from "./state.js";
|
|
14
|
+
import { isDuplexNodeStream, isIterable, isNodeStream, isReadable, isReadableNodeStream, isReadableStream, isWritable, isWritableNodeStream, isWritableStream } from "./stream-utils.js";
|
|
15
|
+
export function duplexify(kernel, body, name) {
|
|
16
|
+
const { Readable, Writable, Duplex } = kernel;
|
|
17
|
+
if (isDuplexNodeStream(body)) {
|
|
18
|
+
return body;
|
|
19
|
+
}
|
|
20
|
+
if (isReadableNodeStream(body)) {
|
|
21
|
+
return duplexifyPair(kernel, { readable: body });
|
|
22
|
+
}
|
|
23
|
+
if (isWritableNodeStream(body)) {
|
|
24
|
+
return duplexifyPair(kernel, { writable: body });
|
|
25
|
+
}
|
|
26
|
+
if (isNodeStream(body)) {
|
|
27
|
+
return duplexifyPair(kernel, { writable: false, readable: false });
|
|
28
|
+
}
|
|
29
|
+
if (isReadableStream(body)) {
|
|
30
|
+
return duplexifyPair(kernel, { readable: Readable.fromWeb(body) });
|
|
31
|
+
}
|
|
32
|
+
if (isWritableStream(body)) {
|
|
33
|
+
return duplexifyPair(kernel, { writable: Writable.fromWeb(body) });
|
|
34
|
+
}
|
|
35
|
+
if (typeof body === "function") {
|
|
36
|
+
const { value, write, final, destroy } = fromAsyncGen(body);
|
|
37
|
+
if (isDuplexNodeStream(value)) {
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
if (isIterable(value)) {
|
|
41
|
+
return from(Duplex, value, { objectMode: true, write, final, destroy });
|
|
42
|
+
}
|
|
43
|
+
const then = value?.then;
|
|
44
|
+
if (typeof then === "function") {
|
|
45
|
+
const promise = then.call(value, (val) => {
|
|
46
|
+
if (val != null) {
|
|
47
|
+
throw errInvalidReturnValue("nully", "body", val);
|
|
48
|
+
}
|
|
49
|
+
}, (err) => {
|
|
50
|
+
destroyer(d, err);
|
|
51
|
+
});
|
|
52
|
+
const d = new Duplex({
|
|
53
|
+
objectMode: true,
|
|
54
|
+
readable: false,
|
|
55
|
+
write,
|
|
56
|
+
final(cb) {
|
|
57
|
+
final(async () => {
|
|
58
|
+
try {
|
|
59
|
+
await promise;
|
|
60
|
+
nextTick(() => cb(null));
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
nextTick(() => cb(err));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
destroy
|
|
68
|
+
});
|
|
69
|
+
return d;
|
|
70
|
+
}
|
|
71
|
+
throw errInvalidReturnValue("Iterable, AsyncIterable or AsyncFunction", name, value);
|
|
72
|
+
}
|
|
73
|
+
if (typeof Blob !== "undefined" && body instanceof Blob) {
|
|
74
|
+
return duplexify(kernel, body.arrayBuffer(), name);
|
|
75
|
+
}
|
|
76
|
+
if (isIterable(body)) {
|
|
77
|
+
return from(Duplex, body, { objectMode: true, writable: false });
|
|
78
|
+
}
|
|
79
|
+
if (isReadableStream(body?.readable) && isWritableStream(body?.writable)) {
|
|
80
|
+
return Duplex.fromWeb(body);
|
|
81
|
+
}
|
|
82
|
+
if (typeof body?.writable === "object" || typeof body?.readable === "object") {
|
|
83
|
+
const readable = body?.readable
|
|
84
|
+
? isReadableNodeStream(body.readable)
|
|
85
|
+
? body.readable
|
|
86
|
+
: duplexify(kernel, body.readable, name)
|
|
87
|
+
: undefined;
|
|
88
|
+
const writable = body?.writable
|
|
89
|
+
? isWritableNodeStream(body.writable)
|
|
90
|
+
? body.writable
|
|
91
|
+
: duplexify(kernel, body.writable, name)
|
|
92
|
+
: undefined;
|
|
93
|
+
return duplexifyPair(kernel, { readable, writable });
|
|
94
|
+
}
|
|
95
|
+
const then = body?.then;
|
|
96
|
+
if (typeof then === "function") {
|
|
97
|
+
then.call(body, (val) => {
|
|
98
|
+
if (val != null) {
|
|
99
|
+
d.push(val);
|
|
100
|
+
}
|
|
101
|
+
d.push(null);
|
|
102
|
+
}, (err) => {
|
|
103
|
+
destroyer(d, err);
|
|
104
|
+
});
|
|
105
|
+
const d = new Duplex({ objectMode: true, writable: false, read() { } });
|
|
106
|
+
return d;
|
|
107
|
+
}
|
|
108
|
+
throw invalidDuplexBody(body, name);
|
|
109
|
+
}
|
|
110
|
+
/** The error `Duplex.from` throws for a `body` it cannot use. */
|
|
111
|
+
export function invalidDuplexBody(body, name = "body") {
|
|
112
|
+
return errInvalidArgType(name, [
|
|
113
|
+
"Blob",
|
|
114
|
+
"ReadableStream",
|
|
115
|
+
"WritableStream",
|
|
116
|
+
"Stream",
|
|
117
|
+
"Iterable",
|
|
118
|
+
"AsyncIterable",
|
|
119
|
+
"Function",
|
|
120
|
+
"{ readable, writable } pair",
|
|
121
|
+
"Promise"
|
|
122
|
+
], body);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* What `duplexify` would reject inside `body`, found without touching `body`:
|
|
126
|
+
* the branches above, in the same order, with nothing created, locked or
|
|
127
|
+
* subscribed to. `undefined` when it would accept it. A function counts as
|
|
128
|
+
* accepted — what it returns is known only by calling it.
|
|
129
|
+
*/
|
|
130
|
+
export function rejectedDuplexBody(body) {
|
|
131
|
+
if (isNodeStream(body) ||
|
|
132
|
+
isReadableStream(body) ||
|
|
133
|
+
isWritableStream(body) ||
|
|
134
|
+
typeof body === "function" ||
|
|
135
|
+
(typeof Blob !== "undefined" && body instanceof Blob) ||
|
|
136
|
+
isIterable(body) ||
|
|
137
|
+
(isReadableStream(body?.readable) && isWritableStream(body?.writable))) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
if (typeof body?.writable === "object" || typeof body?.readable === "object") {
|
|
141
|
+
const readable = body?.readable;
|
|
142
|
+
const writable = body?.writable;
|
|
143
|
+
return ((readable && !isReadableNodeStream(readable) ? rejectedDuplexBody(readable) : undefined) ??
|
|
144
|
+
(writable && !isWritableNodeStream(writable) ? rejectedDuplexBody(writable) : undefined));
|
|
145
|
+
}
|
|
146
|
+
return typeof body?.then === "function" ? undefined : { value: body };
|
|
147
|
+
}
|
|
148
|
+
function isThenable(value) {
|
|
149
|
+
return !isNodeStream(value) && typeof value?.then === "function";
|
|
150
|
+
}
|
|
151
|
+
/** Whether `duplexify` would read `body` as a `{ readable, writable }` object of its own. */
|
|
152
|
+
function isSidesObject(body) {
|
|
153
|
+
return (!isNodeStream(body) &&
|
|
154
|
+
!isReadableStream(body) &&
|
|
155
|
+
!isWritableStream(body) &&
|
|
156
|
+
typeof body !== "function" &&
|
|
157
|
+
!isIterable(body) &&
|
|
158
|
+
!(isReadableStream(body?.readable) && isWritableStream(body?.writable)) &&
|
|
159
|
+
(typeof body?.writable === "object" || typeof body?.readable === "object"));
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* `body` with every promise `duplexify` would subscribe to — the body itself,
|
|
163
|
+
* or a side of a `{ readable, writable }` object, at any depth — adopted into
|
|
164
|
+
* a native promise. A native promise always calls back later; a thenable that
|
|
165
|
+
* calls back at once reaches `duplexify` before its Duplex exists, and throws
|
|
166
|
+
* `Cannot read properties of undefined` — in Node's own `Duplex.from`, too.
|
|
167
|
+
*/
|
|
168
|
+
export function adoptThenables(body) {
|
|
169
|
+
if (isThenable(body)) {
|
|
170
|
+
return Promise.resolve(body);
|
|
171
|
+
}
|
|
172
|
+
if (isSidesObject(body)) {
|
|
173
|
+
const sides = {};
|
|
174
|
+
if ("readable" in body) {
|
|
175
|
+
sides.readable = adoptThenables(body.readable);
|
|
176
|
+
}
|
|
177
|
+
if ("writable" in body) {
|
|
178
|
+
sides.writable = adoptThenables(body.writable);
|
|
179
|
+
}
|
|
180
|
+
return sides;
|
|
181
|
+
}
|
|
182
|
+
return body;
|
|
183
|
+
}
|
|
184
|
+
/** Call `visit` on every promise `duplexify` would subscribe to inside `body`. */
|
|
185
|
+
export function forEachThenable(body, visit) {
|
|
186
|
+
if (isThenable(body)) {
|
|
187
|
+
visit(body);
|
|
188
|
+
}
|
|
189
|
+
else if (isSidesObject(body)) {
|
|
190
|
+
forEachThenable(body.readable, visit);
|
|
191
|
+
forEachThenable(body.writable, visit);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/** Feed writes into an async generator function and read what it yields. */
|
|
195
|
+
function fromAsyncGen(fn) {
|
|
196
|
+
let resolve = null;
|
|
197
|
+
let promise = new Promise(r => {
|
|
198
|
+
resolve = r;
|
|
199
|
+
});
|
|
200
|
+
const ac = new AbortController();
|
|
201
|
+
const signal = ac.signal;
|
|
202
|
+
const value = fn((async function* () {
|
|
203
|
+
while (true) {
|
|
204
|
+
const current = promise;
|
|
205
|
+
promise = null;
|
|
206
|
+
const { chunk, done, cb } = await current;
|
|
207
|
+
nextTick(cb);
|
|
208
|
+
if (done) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (signal.aborted) {
|
|
212
|
+
throw createAbortError(signal.reason);
|
|
213
|
+
}
|
|
214
|
+
promise = new Promise(r => {
|
|
215
|
+
resolve = r;
|
|
216
|
+
});
|
|
217
|
+
yield chunk;
|
|
218
|
+
}
|
|
219
|
+
})(), { signal });
|
|
220
|
+
return {
|
|
221
|
+
value,
|
|
222
|
+
write(chunk, _encoding, cb) {
|
|
223
|
+
const r = resolve;
|
|
224
|
+
resolve = null;
|
|
225
|
+
r({ chunk, done: false, cb });
|
|
226
|
+
},
|
|
227
|
+
final(cb) {
|
|
228
|
+
const r = resolve;
|
|
229
|
+
resolve = null;
|
|
230
|
+
r({ done: true, cb });
|
|
231
|
+
},
|
|
232
|
+
destroy(err, cb) {
|
|
233
|
+
ac.abort(err);
|
|
234
|
+
if (resolve !== null) {
|
|
235
|
+
const r = resolve;
|
|
236
|
+
resolve = null;
|
|
237
|
+
r({ done: true, cb() { } });
|
|
238
|
+
}
|
|
239
|
+
cb(err);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function duplexifyPair(kernel, pair) {
|
|
244
|
+
const { Readable, Duplex } = kernel;
|
|
245
|
+
const r = pair.readable && typeof pair.readable.read !== "function"
|
|
246
|
+
? Readable.wrap(pair.readable)
|
|
247
|
+
: pair.readable;
|
|
248
|
+
const w = pair.writable;
|
|
249
|
+
let readable = !!isReadable(r);
|
|
250
|
+
let writable = !!isWritable(w);
|
|
251
|
+
let ondrain = null;
|
|
252
|
+
let onfinish = null;
|
|
253
|
+
let onreadable = null;
|
|
254
|
+
// undefined: still open; null: both sides finished; a function: destroy waiting on them.
|
|
255
|
+
let onclose;
|
|
256
|
+
const d = new Duplex({
|
|
257
|
+
readableObjectMode: !!r?.readableObjectMode,
|
|
258
|
+
writableObjectMode: !!w?.writableObjectMode,
|
|
259
|
+
readable,
|
|
260
|
+
writable
|
|
261
|
+
});
|
|
262
|
+
function onfinished(err) {
|
|
263
|
+
const cb = onclose;
|
|
264
|
+
onclose = null;
|
|
265
|
+
if (cb) {
|
|
266
|
+
cb(err);
|
|
267
|
+
}
|
|
268
|
+
else if (err) {
|
|
269
|
+
d.destroy(err);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (writable) {
|
|
273
|
+
eos(w, err => {
|
|
274
|
+
writable = false;
|
|
275
|
+
if (err) {
|
|
276
|
+
destroyer(r, err);
|
|
277
|
+
}
|
|
278
|
+
onfinished(err);
|
|
279
|
+
});
|
|
280
|
+
d._write = function (chunk, encoding, callback) {
|
|
281
|
+
if (w.write(chunk, encoding)) {
|
|
282
|
+
callback();
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
ondrain = callback;
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
d._final = function (callback) {
|
|
289
|
+
w.end();
|
|
290
|
+
onfinish = callback;
|
|
291
|
+
};
|
|
292
|
+
w.on("drain", () => {
|
|
293
|
+
if (ondrain) {
|
|
294
|
+
const cb = ondrain;
|
|
295
|
+
ondrain = null;
|
|
296
|
+
cb();
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
w.on("finish", () => {
|
|
300
|
+
if (onfinish) {
|
|
301
|
+
const cb = onfinish;
|
|
302
|
+
onfinish = null;
|
|
303
|
+
cb();
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
if (readable) {
|
|
308
|
+
eos(r, err => {
|
|
309
|
+
readable = false;
|
|
310
|
+
if (err) {
|
|
311
|
+
destroyer(w, err);
|
|
312
|
+
}
|
|
313
|
+
onfinished(err);
|
|
314
|
+
});
|
|
315
|
+
r.on("readable", () => {
|
|
316
|
+
if (onreadable) {
|
|
317
|
+
const cb = onreadable;
|
|
318
|
+
onreadable = null;
|
|
319
|
+
cb();
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
r.on("end", () => {
|
|
323
|
+
d.push(null);
|
|
324
|
+
});
|
|
325
|
+
d._read = function () {
|
|
326
|
+
while (true) {
|
|
327
|
+
const buf = r.read();
|
|
328
|
+
if (buf === null) {
|
|
329
|
+
onreadable = d._read;
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (!d.push(buf)) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
d._destroy = function (err, callback) {
|
|
339
|
+
if (!err && onclose !== null) {
|
|
340
|
+
err = createAbortError();
|
|
341
|
+
}
|
|
342
|
+
onreadable = null;
|
|
343
|
+
ondrain = null;
|
|
344
|
+
onfinish = null;
|
|
345
|
+
if (onclose === null) {
|
|
346
|
+
callback(err);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
onclose = callback;
|
|
350
|
+
destroyer(w, err);
|
|
351
|
+
destroyer(r, err);
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
return d;
|
|
355
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream kernel - end-of-stream detection
|
|
3
|
+
*
|
|
4
|
+
* Port of Node.js `lib/internal/streams/end-of-stream.js`: one place that
|
|
5
|
+
* decides when a stream is done and whether it ended cleanly or prematurely.
|
|
6
|
+
* `finished`, `pipeline`, `compose`, async iteration and the Web adapters all
|
|
7
|
+
* rely on it, so they cannot disagree about completion.
|
|
8
|
+
*/
|
|
9
|
+
import { createAbortError, errInvalidArgType, errStreamPrematureClose } from "./errors.js";
|
|
10
|
+
import { nextTick } from "./state.js";
|
|
11
|
+
import { isClosed, isNodeStream, isReadable, isReadableErrored, isReadableFinished, isReadableNodeStream, isWritable, isWritableErrored, isWritableFinished, isWritableNodeStream, willEmitClose as streamWillEmitClose } from "./stream-utils.js";
|
|
12
|
+
const nop = () => { };
|
|
13
|
+
function getEosErrored(stream) {
|
|
14
|
+
return isWritableErrored(stream) || isReadableErrored(stream) || null;
|
|
15
|
+
}
|
|
16
|
+
function getEosOnCloseError(stream, readable, readableFinished, writable, writableFinished) {
|
|
17
|
+
const errored = getEosErrored(stream);
|
|
18
|
+
if (errored) {
|
|
19
|
+
return errored;
|
|
20
|
+
}
|
|
21
|
+
if (readable && !readableFinished && isReadableNodeStream(stream, true)) {
|
|
22
|
+
if (!isReadableFinished(stream, false)) {
|
|
23
|
+
return errStreamPrematureClose();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (writable && !writableFinished) {
|
|
27
|
+
if (!isWritableFinished(stream, false)) {
|
|
28
|
+
return errStreamPrematureClose();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
export function eos(stream, optionsOrCallback, maybeCallback) {
|
|
34
|
+
let options;
|
|
35
|
+
let callback;
|
|
36
|
+
if (typeof optionsOrCallback === "function") {
|
|
37
|
+
options = {};
|
|
38
|
+
callback = optionsOrCallback;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
options = optionsOrCallback ?? {};
|
|
42
|
+
callback = maybeCallback;
|
|
43
|
+
}
|
|
44
|
+
if (!isNodeStream(stream)) {
|
|
45
|
+
throw errInvalidArgType("stream", ["ReadableStream", "WritableStream", "Stream"], stream);
|
|
46
|
+
}
|
|
47
|
+
const readable = options.readable ?? isReadableNodeStream(stream);
|
|
48
|
+
const writable = options.writable ?? isWritableNodeStream(stream);
|
|
49
|
+
let willEmitClose = !!streamWillEmitClose(stream) &&
|
|
50
|
+
isReadableNodeStream(stream) === readable &&
|
|
51
|
+
isWritableNodeStream(stream) === writable;
|
|
52
|
+
let writableFinished = isWritableFinished(stream, false);
|
|
53
|
+
let readableFinished = isReadableFinished(stream, false);
|
|
54
|
+
const wState = stream._writableState;
|
|
55
|
+
const rState = stream._readableState;
|
|
56
|
+
// undefined: still pending; null: done cleanly; Error: done with an error.
|
|
57
|
+
let immediateResult;
|
|
58
|
+
if (isClosed(stream)) {
|
|
59
|
+
immediateResult = getEosOnCloseError(stream, readable, readableFinished, writable, writableFinished);
|
|
60
|
+
}
|
|
61
|
+
else if (wState?.errorEmitted || rState?.errorEmitted) {
|
|
62
|
+
if (!willEmitClose) {
|
|
63
|
+
immediateResult = getEosErrored(stream);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else if (!readable &&
|
|
67
|
+
(!willEmitClose || isReadable(stream)) &&
|
|
68
|
+
(writableFinished || isWritable(stream) === false) &&
|
|
69
|
+
(wState == null || wState.pendingcb === undefined || wState.pendingcb === 0)) {
|
|
70
|
+
immediateResult = getEosErrored(stream);
|
|
71
|
+
}
|
|
72
|
+
else if (!writable &&
|
|
73
|
+
(!willEmitClose || isWritable(stream)) &&
|
|
74
|
+
(readableFinished || isReadable(stream) === false)) {
|
|
75
|
+
immediateResult = getEosErrored(stream);
|
|
76
|
+
}
|
|
77
|
+
let cleanup = () => {
|
|
78
|
+
callback = nop;
|
|
79
|
+
};
|
|
80
|
+
if (immediateResult !== undefined) {
|
|
81
|
+
if (options.error !== false) {
|
|
82
|
+
stream.on("error", nop);
|
|
83
|
+
cleanup = () => {
|
|
84
|
+
callback = nop;
|
|
85
|
+
stream.removeListener("error", nop);
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else if (options.signal?.aborted) {
|
|
90
|
+
immediateResult = createAbortError(options.signal.reason);
|
|
91
|
+
}
|
|
92
|
+
if (immediateResult !== undefined) {
|
|
93
|
+
const result = immediateResult;
|
|
94
|
+
const report = () => {
|
|
95
|
+
if (result === null) {
|
|
96
|
+
callback.call(stream);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
callback.call(stream, result);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
if (options.synchronous) {
|
|
103
|
+
report();
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
nextTick(report);
|
|
107
|
+
}
|
|
108
|
+
return cleanup;
|
|
109
|
+
}
|
|
110
|
+
let called = false;
|
|
111
|
+
const userCallback = callback;
|
|
112
|
+
callback = function (err) {
|
|
113
|
+
if (called) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
called = true;
|
|
117
|
+
userCallback.call(this, err);
|
|
118
|
+
};
|
|
119
|
+
const onlegacyfinish = () => {
|
|
120
|
+
if (!stream.writable) {
|
|
121
|
+
onfinish();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const onfinish = () => {
|
|
125
|
+
writableFinished = true;
|
|
126
|
+
if (stream.destroyed) {
|
|
127
|
+
willEmitClose = false;
|
|
128
|
+
}
|
|
129
|
+
if (willEmitClose && (!stream.readable || readable)) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (!readable || readableFinished) {
|
|
133
|
+
callback.call(stream);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
const onend = () => {
|
|
137
|
+
readableFinished = true;
|
|
138
|
+
if (stream.destroyed) {
|
|
139
|
+
willEmitClose = false;
|
|
140
|
+
}
|
|
141
|
+
if (willEmitClose && (!stream.writable || writable)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (!writable || writableFinished) {
|
|
145
|
+
callback.call(stream);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
const onerror = (err) => {
|
|
149
|
+
callback.call(stream, err);
|
|
150
|
+
};
|
|
151
|
+
const onclose = () => {
|
|
152
|
+
const error = getEosOnCloseError(stream, readable, readableFinished, writable, writableFinished);
|
|
153
|
+
if (error === null) {
|
|
154
|
+
callback.call(stream);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
callback.call(stream, error);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
// Legacy streams have no writable state to say when they finished.
|
|
161
|
+
if (writable && !wState) {
|
|
162
|
+
stream.on("end", onlegacyfinish);
|
|
163
|
+
stream.on("close", onlegacyfinish);
|
|
164
|
+
}
|
|
165
|
+
stream.on("end", onend);
|
|
166
|
+
stream.on("finish", onfinish);
|
|
167
|
+
if (options.error !== false) {
|
|
168
|
+
stream.on("error", onerror);
|
|
169
|
+
}
|
|
170
|
+
stream.on("close", onclose);
|
|
171
|
+
cleanup = () => {
|
|
172
|
+
callback = nop;
|
|
173
|
+
stream.removeListener("end", onlegacyfinish);
|
|
174
|
+
stream.removeListener("close", onlegacyfinish);
|
|
175
|
+
stream.removeListener("finish", onfinish);
|
|
176
|
+
stream.removeListener("end", onend);
|
|
177
|
+
stream.removeListener("error", onerror);
|
|
178
|
+
stream.removeListener("close", onclose);
|
|
179
|
+
};
|
|
180
|
+
const signal = options.signal;
|
|
181
|
+
if (signal) {
|
|
182
|
+
const abort = () => {
|
|
183
|
+
const endCallback = callback;
|
|
184
|
+
cleanup();
|
|
185
|
+
endCallback.call(stream, createAbortError(signal.reason));
|
|
186
|
+
};
|
|
187
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
188
|
+
const originalCallback = callback;
|
|
189
|
+
callback = function (err) {
|
|
190
|
+
signal.removeEventListener("abort", abort);
|
|
191
|
+
originalCallback.call(this, err);
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return cleanup;
|
|
195
|
+
}
|
|
196
|
+
/** Destroy `stream` when `signal` aborts; detaches once the stream is done. */
|
|
197
|
+
export function addAbortSignalNoValidate(signal, stream) {
|
|
198
|
+
if (typeof signal !== "object" || !("aborted" in signal)) {
|
|
199
|
+
return stream;
|
|
200
|
+
}
|
|
201
|
+
const onAbort = () => {
|
|
202
|
+
stream.destroy(createAbortError(signal.reason));
|
|
203
|
+
};
|
|
204
|
+
if (signal.aborted) {
|
|
205
|
+
onAbort();
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
209
|
+
eos(stream, () => signal.removeEventListener("abort", onAbort));
|
|
210
|
+
}
|
|
211
|
+
return stream;
|
|
212
|
+
}
|