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
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
* - `write(chunk)` resolves once the chunk has been accepted by the
|
|
11
11
|
* sink (after awaiting `drain` for Node-style sinks under
|
|
12
12
|
* backpressure, or `writer.ready` for Web streams).
|
|
13
|
-
* - `end()` finalises the sink and resolves
|
|
13
|
+
* - `end()` finalises the sink and resolves once it has really finished
|
|
14
|
+
* (`'finish'` for a Node-style sink — a `'close'` before it is a
|
|
15
|
+
* premature close). Calling it again returns the same promise.
|
|
16
|
+
* - `abort(reason)` gives the sink up after a producer failure.
|
|
17
|
+
* - A Web sink's writer is released once the sink is closed, aborted or
|
|
18
|
+
* errored, so the stream is not left locked.
|
|
14
19
|
* - The first sink error is captured and re-thrown from the next
|
|
15
20
|
* `write` / `end` so callers can't lose it.
|
|
16
21
|
*
|
|
@@ -39,13 +44,19 @@ export class SinkAdapter {
|
|
|
39
44
|
constructor(sink) {
|
|
40
45
|
this._error = null;
|
|
41
46
|
this._ended = false;
|
|
47
|
+
this._ending = null;
|
|
48
|
+
this._writerReleased = false;
|
|
42
49
|
if (isWebWritable(sink)) {
|
|
43
50
|
this._kind = "web";
|
|
44
51
|
this._webWriter = sink.getWriter();
|
|
45
52
|
// Web writers expose .closed; surface termination errors through
|
|
46
53
|
// the same _error channel so producers see them.
|
|
47
54
|
this._webWriter.closed.catch((err) => {
|
|
48
|
-
|
|
55
|
+
// Releasing our own lock rejects `closed` too; that is not a sink error.
|
|
56
|
+
if (!this._writerReleased) {
|
|
57
|
+
this._captureError(err);
|
|
58
|
+
this._releaseWriter();
|
|
59
|
+
}
|
|
49
60
|
});
|
|
50
61
|
}
|
|
51
62
|
else if (isNodeWritable(sink)) {
|
|
@@ -109,53 +120,70 @@ export class SinkAdapter {
|
|
|
109
120
|
drain?.cancel();
|
|
110
121
|
}
|
|
111
122
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
123
|
+
/** Finish the sink. Every call returns the same promise. */
|
|
124
|
+
end() {
|
|
125
|
+
if (!this._ending) {
|
|
126
|
+
this._ended = true;
|
|
127
|
+
this._ending = this._finish();
|
|
115
128
|
}
|
|
129
|
+
return this._ending;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Give the sink up after a producer failure: a Web sink is aborted and its
|
|
133
|
+
* lock released, a Node-style one destroyed with `reason`.
|
|
134
|
+
*/
|
|
135
|
+
async abort(reason) {
|
|
116
136
|
this._ended = true;
|
|
137
|
+
const error = reason instanceof Error ? reason : new Error(String(reason ?? "aborted"));
|
|
138
|
+
this._captureError(error);
|
|
139
|
+
if (this._kind === "web") {
|
|
140
|
+
if (!this._writerReleased) {
|
|
141
|
+
await this._webWriter.abort(error).catch(() => { });
|
|
142
|
+
this._releaseWriter();
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const sink = (this._kind === "node" ? this._nodeSink : this._duckSink);
|
|
147
|
+
if (typeof sink.destroy === "function" && !sink.destroyed) {
|
|
148
|
+
sink.destroy(error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async _finish() {
|
|
117
152
|
this._throwIfErrored();
|
|
118
153
|
if (this._kind === "web") {
|
|
119
154
|
// close() rejects if the stream has already errored — we let that
|
|
120
155
|
// propagate so callers see the underlying failure.
|
|
121
|
-
await this._webWriter.close();
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
if (this._kind === "node") {
|
|
125
|
-
const sink = this._nodeSink;
|
|
126
|
-
// Prefer the close event when available; some Node writables emit
|
|
127
|
-
// 'finish' first and only emit 'close' on `destroy()`. We listen
|
|
128
|
-
// for both.
|
|
129
|
-
const finished = createEventWaiter(sink, ["close", "finish"]);
|
|
130
156
|
try {
|
|
131
|
-
await
|
|
132
|
-
await finished.promise;
|
|
157
|
+
await this._webWriter.close();
|
|
133
158
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
throw error;
|
|
159
|
+
finally {
|
|
160
|
+
this._releaseWriter();
|
|
137
161
|
}
|
|
138
|
-
this._throwIfErrored();
|
|
139
162
|
return;
|
|
140
163
|
}
|
|
141
|
-
|
|
142
|
-
const sink = this._duckSink;
|
|
164
|
+
const sink = this._kind === "node" ? this._nodeSink : this._duckSink;
|
|
143
165
|
if (hasEvents(sink)) {
|
|
144
|
-
const finished =
|
|
166
|
+
const finished = waitForFinish(sink);
|
|
145
167
|
try {
|
|
146
168
|
await sink.end();
|
|
147
|
-
await finished.promise;
|
|
148
169
|
}
|
|
149
170
|
catch (error) {
|
|
150
171
|
finished.cancel();
|
|
151
172
|
throw error;
|
|
152
173
|
}
|
|
174
|
+
await finished.promise;
|
|
153
175
|
}
|
|
154
176
|
else {
|
|
155
177
|
await sink.end();
|
|
156
178
|
}
|
|
157
179
|
this._throwIfErrored();
|
|
158
180
|
}
|
|
181
|
+
_releaseWriter() {
|
|
182
|
+
if (!this._writerReleased) {
|
|
183
|
+
this._writerReleased = true;
|
|
184
|
+
this._webWriter.releaseLock();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
159
187
|
_captureError(err) {
|
|
160
188
|
if (this._error) {
|
|
161
189
|
return;
|
|
@@ -184,3 +212,45 @@ export class SinkAdapter {
|
|
|
184
212
|
function hasEvents(sink) {
|
|
185
213
|
return typeof sink.once === "function" || typeof sink.on === "function";
|
|
186
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Resolve on `'finish'`; reject on `'error'`, or on a `'close'` that comes
|
|
217
|
+
* first while the sink reports it has not finished — that is a premature
|
|
218
|
+
* close, not a successful end.
|
|
219
|
+
*/
|
|
220
|
+
function waitForFinish(sink) {
|
|
221
|
+
let cleanup = () => { };
|
|
222
|
+
const promise = new Promise((resolve, reject) => {
|
|
223
|
+
const onFinish = () => {
|
|
224
|
+
cleanup();
|
|
225
|
+
resolve();
|
|
226
|
+
};
|
|
227
|
+
const onError = (err) => {
|
|
228
|
+
cleanup();
|
|
229
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
230
|
+
};
|
|
231
|
+
const onClose = () => {
|
|
232
|
+
cleanup();
|
|
233
|
+
// A sink that reports no finished state (a duck sink signalling with
|
|
234
|
+
// 'close' alone) is taken at its word; one that does must have finished.
|
|
235
|
+
const finished = sink.writableFinished;
|
|
236
|
+
if (typeof finished !== "boolean" || finished) {
|
|
237
|
+
resolve();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const err = new Error("Premature close");
|
|
241
|
+
err.code = "ERR_STREAM_PREMATURE_CLOSE";
|
|
242
|
+
reject(err);
|
|
243
|
+
};
|
|
244
|
+
const on = (sink.on ?? sink.once).bind(sink);
|
|
245
|
+
on("finish", onFinish);
|
|
246
|
+
on("error", onError);
|
|
247
|
+
on("close", onClose);
|
|
248
|
+
cleanup = () => {
|
|
249
|
+
const off = (sink.off ?? sink.removeListener)?.bind(sink);
|
|
250
|
+
off?.("finish", onFinish);
|
|
251
|
+
off?.("error", onError);
|
|
252
|
+
off?.("close", onClose);
|
|
253
|
+
};
|
|
254
|
+
});
|
|
255
|
+
return { promise, cancel: () => cleanup() };
|
|
256
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream helpers shared by the Node and browser entry points.
|
|
3
|
+
*
|
|
4
|
+
* Everything here works on the public shape of a stream — async iteration,
|
|
5
|
+
* a Web `ReadableStream`'s reader, the `readable*` / `writable*` getters — so
|
|
6
|
+
* it is the same code on both platforms. A Web stream is read through its own
|
|
7
|
+
* reader; it is not wrapped in a Node-style stream first.
|
|
8
|
+
*/
|
|
9
|
+
import { toStreamBytes } from "./binary-chunk.js";
|
|
10
|
+
import { isAsyncIterable, isReadableStream } from "./type-guards.js";
|
|
11
|
+
import { UnsupportedStreamTypeError } from "../errors.js";
|
|
12
|
+
import { concatUint8Arrays, createTextDecoder } from "../../../utils/binary.js";
|
|
13
|
+
/** `true` for an object with a Web `ReadableStream`'s `getReader()`. */
|
|
14
|
+
export const isReadableStreamLike = isReadableStream;
|
|
15
|
+
/**
|
|
16
|
+
* Read a Web `ReadableStream` with `for await`, whether or not the platform's
|
|
17
|
+
* `ReadableStream` is async-iterable itself. Every value is yielded as read —
|
|
18
|
+
* `null` and `undefined` included, as `ReadableStream.prototype.values()` does —
|
|
19
|
+
* so the caller decides what a value it cannot use means. Leaving the loop
|
|
20
|
+
* early cancels the stream; the lock is released either way.
|
|
21
|
+
*/
|
|
22
|
+
export async function* readableStreamToAsyncIterable(stream) {
|
|
23
|
+
const reader = stream.getReader();
|
|
24
|
+
let done = false;
|
|
25
|
+
try {
|
|
26
|
+
while (true) {
|
|
27
|
+
const result = await reader.read();
|
|
28
|
+
if (result.done) {
|
|
29
|
+
done = true;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
yield result.value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
if (!done) {
|
|
37
|
+
await Promise.resolve(reader.cancel?.()).catch(() => { });
|
|
38
|
+
}
|
|
39
|
+
reader.releaseLock?.();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Every chunk of a stream, in an array. */
|
|
43
|
+
export async function collect(stream) {
|
|
44
|
+
const result = [];
|
|
45
|
+
for await (const chunk of stream) {
|
|
46
|
+
result.push(chunk);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
/** A whole stream decoded as UTF-8 text. */
|
|
51
|
+
export function text(stream) {
|
|
52
|
+
return streamToString(stream);
|
|
53
|
+
}
|
|
54
|
+
/** A whole stream parsed as JSON. */
|
|
55
|
+
export async function json(stream) {
|
|
56
|
+
return JSON.parse(await text(stream));
|
|
57
|
+
}
|
|
58
|
+
/** A whole stream's bytes. */
|
|
59
|
+
export function bytes(stream) {
|
|
60
|
+
return streamToUint8Array(stream);
|
|
61
|
+
}
|
|
62
|
+
function toAsyncIterable(stream, name) {
|
|
63
|
+
if (isReadableStream(stream)) {
|
|
64
|
+
return readableStreamToAsyncIterable(stream);
|
|
65
|
+
}
|
|
66
|
+
if (isAsyncIterable(stream)) {
|
|
67
|
+
return stream;
|
|
68
|
+
}
|
|
69
|
+
throw new UnsupportedStreamTypeError(name, typeof stream);
|
|
70
|
+
}
|
|
71
|
+
/** Collect every byte of a stream. */
|
|
72
|
+
export async function streamToUint8Array(stream) {
|
|
73
|
+
const chunks = [];
|
|
74
|
+
let totalLength = 0;
|
|
75
|
+
for await (const chunk of toAsyncIterable(stream, "streamToUint8Array")) {
|
|
76
|
+
const bytes = toStreamBytes(chunk);
|
|
77
|
+
if (!bytes) {
|
|
78
|
+
throw new UnsupportedStreamTypeError("streamToUint8Array", typeof chunk);
|
|
79
|
+
}
|
|
80
|
+
chunks.push(bytes);
|
|
81
|
+
totalLength += bytes.byteLength;
|
|
82
|
+
}
|
|
83
|
+
return concatUint8Arrays(chunks, totalLength);
|
|
84
|
+
}
|
|
85
|
+
/** Alias of {@link streamToUint8Array}, named after Node's `Buffer`. */
|
|
86
|
+
export function streamToBuffer(stream) {
|
|
87
|
+
return streamToUint8Array(stream);
|
|
88
|
+
}
|
|
89
|
+
/** Decode a whole stream as text. */
|
|
90
|
+
export async function streamToString(stream, encoding) {
|
|
91
|
+
const decoder = createTextDecoder(encoding);
|
|
92
|
+
let text = "";
|
|
93
|
+
for await (const chunk of toAsyncIterable(stream, "streamToString")) {
|
|
94
|
+
const bytes = toStreamBytes(chunk);
|
|
95
|
+
if (!bytes) {
|
|
96
|
+
throw new UnsupportedStreamTypeError("streamToString", typeof chunk);
|
|
97
|
+
}
|
|
98
|
+
text += decoder.decode(bytes, { stream: true });
|
|
99
|
+
}
|
|
100
|
+
return text + decoder.decode();
|
|
101
|
+
}
|
|
102
|
+
/** Consume a stream to its end, discarding the data. */
|
|
103
|
+
export async function drainStream(stream) {
|
|
104
|
+
for await (const _chunk of toAsyncIterable(stream, "drainStream")) {
|
|
105
|
+
// discard
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** `true` for a Node-style readable stream that can still be read from. */
|
|
109
|
+
export function isReadable(stream) {
|
|
110
|
+
const s = stream;
|
|
111
|
+
return (!!s &&
|
|
112
|
+
typeof s.read === "function" &&
|
|
113
|
+
typeof s.pipe === "function" &&
|
|
114
|
+
!s.destroyed &&
|
|
115
|
+
s.readable !== false &&
|
|
116
|
+
s.readableEnded !== true);
|
|
117
|
+
}
|
|
118
|
+
/** `true` for a Node-style writable stream that can still be written to. */
|
|
119
|
+
export function isWritable(stream) {
|
|
120
|
+
const s = stream;
|
|
121
|
+
return (!!s &&
|
|
122
|
+
typeof s.write === "function" &&
|
|
123
|
+
typeof s.end === "function" &&
|
|
124
|
+
!s.destroyed &&
|
|
125
|
+
s.writable !== false &&
|
|
126
|
+
s.writableEnded !== true);
|
|
127
|
+
}
|
|
128
|
+
const kIsDisturbed = Symbol.for("nodejs.stream.disturbed");
|
|
129
|
+
/**
|
|
130
|
+
* `true` once a readable stream has been read from or aborted.
|
|
131
|
+
*
|
|
132
|
+
* Node's own Web streams answer through an internal symbol, which is consulted
|
|
133
|
+
* first. Elsewhere a Web `ReadableStream` has no public "disturbed" flag, but
|
|
134
|
+
* the Fetch spec makes it observable: `new Response(stream)` must throw for a
|
|
135
|
+
* stream that is disturbed or locked, and does nothing to one that is neither —
|
|
136
|
+
* it takes no lock and reads nothing. So an unlocked stream gets an exact
|
|
137
|
+
* answer. A locked one cannot be told apart (locked-and-read and
|
|
138
|
+
* locked-but-unread both throw) and reports `false`, as Node does for a stream
|
|
139
|
+
* that is locked but not yet read.
|
|
140
|
+
*/
|
|
141
|
+
export function isDisturbed(stream) {
|
|
142
|
+
const s = stream;
|
|
143
|
+
if (!s) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
const known = s[kIsDisturbed] ?? (s.readableDidRead || s.readableAborted);
|
|
147
|
+
if (known !== undefined) {
|
|
148
|
+
return !!known;
|
|
149
|
+
}
|
|
150
|
+
if (isReadableStream(s) && !s.locked && typeof Response === "function") {
|
|
151
|
+
try {
|
|
152
|
+
new Response(s);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* Platform-neutral utility functions shared by Node.js and Browser implementations:
|
|
5
5
|
* - Stream state inspection (isDestroyed, isErrored)
|
|
6
6
|
* - Callback-to-promise conversion (promisify)
|
|
7
|
-
* - High water mark management (getDefaultHighWaterMark, setDefaultHighWaterMark)
|
|
8
7
|
*/
|
|
9
8
|
// =============================================================================
|
|
10
9
|
// State Inspection
|
|
@@ -39,25 +38,3 @@ export function promisify(fn) {
|
|
|
39
38
|
});
|
|
40
39
|
});
|
|
41
40
|
}
|
|
42
|
-
// =============================================================================
|
|
43
|
-
// High Water Mark
|
|
44
|
-
// =============================================================================
|
|
45
|
-
let _defaultHighWaterMark = 65536; // 64KB default (matches Node's own on every supported line)
|
|
46
|
-
let _defaultHighWaterMarkObjectMode = 16; // 16 objects default
|
|
47
|
-
/**
|
|
48
|
-
* Get the default high water mark for streams
|
|
49
|
-
*/
|
|
50
|
-
export function getDefaultHighWaterMark(objectMode) {
|
|
51
|
-
return objectMode ? _defaultHighWaterMarkObjectMode : _defaultHighWaterMark;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Set the default high water mark for streams
|
|
55
|
-
*/
|
|
56
|
-
export function setDefaultHighWaterMark(objectMode, value) {
|
|
57
|
-
if (objectMode) {
|
|
58
|
-
_defaultHighWaterMarkObjectMode = value;
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
_defaultHighWaterMark = value;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Streams conversions with the module's guarantees, on both entry points.
|
|
3
|
+
*
|
|
4
|
+
* `Readable.fromWeb` / `.toWeb` and friends stay exactly what each platform's
|
|
5
|
+
* classes provide — on the Node entry, Node's own, with Node's known defects
|
|
6
|
+
* (see the README). These functions run the module's adapters instead, with
|
|
7
|
+
* that platform's classes, so the same call behaves the same on both:
|
|
8
|
+
*
|
|
9
|
+
* - no chunk is lost: a write made while `'drain'` is pending still reaches the
|
|
10
|
+
* Node-style stream, and a `null` read from a Web stream fails it rather than
|
|
11
|
+
* ending it early;
|
|
12
|
+
* - strings from `setEncoding()` can be queued;
|
|
13
|
+
* - the lock on a Web stream is handed back once the adapted stream ends,
|
|
14
|
+
* errors or is destroyed, and is never taken at all when adapting fails;
|
|
15
|
+
* - the adapters remove their own listeners when they are done.
|
|
16
|
+
*/
|
|
17
|
+
import { readableFromWeb as adaptReadableFromWeb, readableToWeb as adaptReadableToWeb, writableFromWeb as adaptWritableFromWeb, writableToWeb as adaptWritableToWeb } from "../kernel/web-adapters.js";
|
|
18
|
+
export function makeReadableFromWeb(Readable) {
|
|
19
|
+
return (stream, options) => adaptReadableFromWeb(Readable, stream, options);
|
|
20
|
+
}
|
|
21
|
+
export function makeWritableFromWeb(Writable) {
|
|
22
|
+
return (stream, options) => adaptWritableFromWeb(Writable, stream, options);
|
|
23
|
+
}
|
|
24
|
+
/** A Node-style Readable as a Web `ReadableStream`. */
|
|
25
|
+
export function readableToWeb(stream, options) {
|
|
26
|
+
return adaptReadableToWeb(stream, options);
|
|
27
|
+
}
|
|
28
|
+
/** A Node-style Writable as a Web `WritableStream`. */
|
|
29
|
+
export function writableToWeb(stream) {
|
|
30
|
+
return adaptWritableToWeb(stream);
|
|
31
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An all-Web pipeline, run by the platform.
|
|
3
|
+
*
|
|
4
|
+
* When every stage is a Web stream, both entry points hand the pipeline to the
|
|
5
|
+
* platform's own `pipeThrough` / `pipeTo` instead of pumping chunks in
|
|
6
|
+
* JavaScript. That carries backpressure, cancellation and abort natively — and
|
|
7
|
+
* on Node it also avoids `stream.pipeline`, which never settles when such a
|
|
8
|
+
* pipeline is aborted while its source is waiting for data.
|
|
9
|
+
*/
|
|
10
|
+
import { isReadableStream, isTransformStream, isWritableStream } from "../kernel/stream-utils.js";
|
|
11
|
+
import { createAbortError } from "../../../utils/errors.js";
|
|
12
|
+
/** `ReadableStream → TransformStream* → WritableStream`: nothing but Web streams. */
|
|
13
|
+
export function isWebOnlyPipeline(streams) {
|
|
14
|
+
if (!isReadableStream(streams[0]) || !isWritableStream(streams[streams.length - 1])) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
for (let i = 1; i < streams.length - 1; i++) {
|
|
18
|
+
if (!isTransformStream(streams[i])) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Run an all-Web pipeline with the platform's own `pipeTo`.
|
|
26
|
+
*
|
|
27
|
+
* The data moves natively, but the pipeline is managed here: every stage is
|
|
28
|
+
* checked for a lock before anything starts, each connection is its own
|
|
29
|
+
* `pipeTo`, and the promise settles only once every connection has finished
|
|
30
|
+
* tearing down — with the first error that occurred.
|
|
31
|
+
*
|
|
32
|
+
* A failure travels along the chain by the Streams spec's own propagation
|
|
33
|
+
* (error forwards, cancel backwards); only the caller's abort uses a signal.
|
|
34
|
+
* Aborting the other connections on the first failure as well looks tidier
|
|
35
|
+
* but races that propagation: Node's `pipeTo` then skips cancelling the
|
|
36
|
+
* source once there are two or more transforms.
|
|
37
|
+
*/
|
|
38
|
+
export async function pipeWebStreams(streams, options) {
|
|
39
|
+
const source = streams[0];
|
|
40
|
+
const transforms = streams.slice(1, -1);
|
|
41
|
+
const destination = streams[streams.length - 1];
|
|
42
|
+
if (source.locked ||
|
|
43
|
+
destination.locked ||
|
|
44
|
+
transforms.some(t => t.readable.locked || t.writable.locked)) {
|
|
45
|
+
throw new TypeError("pipeline: a stream is locked, so it cannot be piped");
|
|
46
|
+
}
|
|
47
|
+
// Every port must be distinct: a stage used twice would be connected twice,
|
|
48
|
+
// the second connection would fail to take its lock and never be made, and
|
|
49
|
+
// the rest would wait forever for an error that has no path to them.
|
|
50
|
+
const ports = [source, destination, ...transforms.flatMap(t => [t.readable, t.writable])];
|
|
51
|
+
if (new Set(ports).size !== ports.length) {
|
|
52
|
+
throw new TypeError("pipeline: the same stream appears more than once");
|
|
53
|
+
}
|
|
54
|
+
// The caller's abort, reported as the AbortError the rest of the module uses.
|
|
55
|
+
let signal;
|
|
56
|
+
let detach = () => { };
|
|
57
|
+
const outer = options?.signal;
|
|
58
|
+
if (outer) {
|
|
59
|
+
const controller = new AbortController();
|
|
60
|
+
const onAbort = () => controller.abort(createAbortError(outer.reason));
|
|
61
|
+
if (outer.aborted) {
|
|
62
|
+
onAbort();
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
outer.addEventListener("abort", onAbort, { once: true });
|
|
66
|
+
detach = () => outer.removeEventListener("abort", onAbort);
|
|
67
|
+
}
|
|
68
|
+
signal = controller.signal;
|
|
69
|
+
}
|
|
70
|
+
const connections = [];
|
|
71
|
+
let upstream = source;
|
|
72
|
+
for (const transform of transforms) {
|
|
73
|
+
connections.push(upstream.pipeTo(transform.writable, { signal }));
|
|
74
|
+
upstream = transform.readable;
|
|
75
|
+
}
|
|
76
|
+
connections.push(upstream.pipeTo(destination, { signal, preventClose: options?.end === false }));
|
|
77
|
+
// Wait for every connection to settle, then report the failure seen first.
|
|
78
|
+
let failed = false;
|
|
79
|
+
let firstError;
|
|
80
|
+
await Promise.all(connections.map(connection => connection.catch((err) => {
|
|
81
|
+
if (!failed) {
|
|
82
|
+
failed = true;
|
|
83
|
+
firstError = err;
|
|
84
|
+
}
|
|
85
|
+
})));
|
|
86
|
+
detach();
|
|
87
|
+
if (failed) {
|
|
88
|
+
throw firstError;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* `index.ts` (Node) and `index.browser.ts`.
|
|
6
6
|
*/
|
|
7
7
|
// Common re-exports (shared between Node.js and browser)
|
|
8
|
-
export { isDestroyed, isErrored,
|
|
8
|
+
export { isDestroyed, isErrored, promisify } from "./core/utils.js";
|
|
9
9
|
export { ChunkedBuilder, TransactionalChunkedBuilder } from "./chunked-builder.js";
|
|
10
|
-
export {
|
|
10
|
+
export { bytes, collect, isReadableStreamLike, json, readableStreamToAsyncIterable, text } from "./core/stream-helpers.js";
|
|
11
11
|
export { StreamError, StreamStateError, StreamTypeError, UnsupportedStreamTypeError, isStreamError } from "./errors.js";
|
|
12
12
|
// Internal utilities exposed for cross-module use (e.g. archive, word/excel writers)
|
|
13
13
|
export { isReadableStream, isWritableStream, isAsyncIterable, isTransformStream } from "./core/type-guards.js";
|
|
@@ -1,31 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Stream Module (browser entry)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* This file is intentionally export-only (tree-shaking friendly).
|
|
4
|
+
* The same names as `index.ts`, backed by the browser kernel in `./browser`,
|
|
5
|
+
* a port of Node's `lib/internal/streams`.
|
|
8
6
|
*/
|
|
9
7
|
// Shared type + platform-independent exports
|
|
10
8
|
export * from "./index.base.js";
|
|
11
|
-
// Core stream classes
|
|
12
|
-
|
|
13
|
-
export { Writable
|
|
14
|
-
export {
|
|
15
|
-
|
|
16
|
-
export {
|
|
17
|
-
export { Collector } from "./browser/collector.js";
|
|
18
|
-
export { Readable, Duplex };
|
|
19
|
-
// Late-binding injection: break circular Readable ↔ Duplex
|
|
20
|
-
import { registerDuplexFrom } from "./browser/_lazy.js";
|
|
21
|
-
// The registry is type-erased (`source: unknown`); re-project onto Duplex.from's
|
|
22
|
-
// accepted source union at this single boundary.
|
|
23
|
-
registerDuplexFrom(source => Duplex.from(source));
|
|
9
|
+
// Core stream classes
|
|
10
|
+
export { Readable, Duplex, Transform, PassThrough } from "./kernel/classes.js";
|
|
11
|
+
export { Writable } from "./kernel/writable.js";
|
|
12
|
+
export { getDefaultHighWaterMark, setDefaultHighWaterMark } from "./kernel/state.js";
|
|
13
|
+
// Collector
|
|
14
|
+
export { Collector, createCollector } from "./browser/collector.js";
|
|
24
15
|
// Factory functions + re-exported helpers
|
|
25
|
-
export { PullStream, BufferedStream, createStringChunk, createByteChunk, createReadable, createReadableFromAsyncIterable, createReadableFromArray, createWritable, createTransform,
|
|
16
|
+
export { PullStream, BufferedStream, createStringChunk, createByteChunk, createReadable, createReadableFromAsyncIterable, createReadableFromArray, createWritable, createTransform, createPassThrough, createPullStream, createBufferedStream, createDuplex, createReadableFromGenerator, createReadableFromPromise, createEmptyReadable, createNullWritable, filter, fromBytes, fromJSON, fromString, transform } from "./browser/factories.js";
|
|
26
17
|
// Pipeline & Finished
|
|
27
18
|
export { pipeline, finished, finishedAll } from "./browser/pipeline.js";
|
|
28
19
|
// Compose
|
|
29
20
|
export { compose } from "./browser/compose.js";
|
|
30
21
|
// Utilities
|
|
31
|
-
export { streamToPromise, streamToUint8Array, streamToBuffer, streamToString, drainStream, copyStream, isTransform, isDuplex, isStream, addAbortSignal, isDisturbed, isReadable, isWritable,
|
|
22
|
+
export { streamToPromise, streamToUint8Array, streamToBuffer, streamToString, drainStream, copyStream, isTransform, isDuplex, isStream, addAbortSignal, isDisturbed, isReadable, isWritable, consumers, promises } from "./browser/utils.js";
|
|
23
|
+
// Web Streams conversions
|
|
24
|
+
export { duplexPair } from "./browser/duplex-pair.js";
|
|
25
|
+
export { readableFromWeb, readableToWeb, toWritable, writableFromWeb, writableToWeb } from "./browser/web.js";
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Stream Module (Node.js entry)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Notes:
|
|
7
|
-
* - This file is intentionally export-only (tree-shaking friendly).
|
|
8
|
-
* - Browser builds should import from `@stream/index.browser`.
|
|
4
|
+
* Node's own `node:stream` classes, plus the module's helpers around them.
|
|
5
|
+
* `index.browser.ts` exports the same names backed by the browser kernel.
|
|
9
6
|
*
|
|
10
7
|
* @example
|
|
11
8
|
* ```ts
|
|
@@ -19,17 +16,16 @@
|
|
|
19
16
|
// Shared type + platform-independent exports
|
|
20
17
|
export * from "./index.base.js";
|
|
21
18
|
// Core stream classes (native Node.js)
|
|
22
|
-
|
|
23
|
-
export { Readable, Transform, Duplex, PassThrough };
|
|
24
|
-
// Writable (extended with browser-compatible API)
|
|
25
|
-
export { Writable, toWritable } from "./node/writable.js";
|
|
19
|
+
export { Readable, Writable, Duplex, Transform, PassThrough, getDefaultHighWaterMark, setDefaultHighWaterMark } from "stream";
|
|
26
20
|
// Collector
|
|
27
21
|
export { Collector, createCollector } from "./node/collector.js";
|
|
28
22
|
// Factory functions + re-exported helpers
|
|
29
|
-
export { PullStream, BufferedStream, createStringChunk, createByteChunk, createReadable, createReadableFromAsyncIterable, createReadableFromArray, createWritable, createTransform, createPassThrough, createPullStream, createBufferedStream, createDuplex, createReadableFromGenerator, createReadableFromPromise, createEmptyReadable, createNullWritable } from "./node/factories.js";
|
|
23
|
+
export { PullStream, BufferedStream, createStringChunk, createByteChunk, createReadable, createReadableFromAsyncIterable, createReadableFromArray, createWritable, createTransform, createPassThrough, createPullStream, createBufferedStream, createDuplex, createReadableFromGenerator, createReadableFromPromise, createEmptyReadable, createNullWritable, filter, fromBytes, fromJSON, fromString, transform } from "./node/factories.js";
|
|
30
24
|
// Pipeline & Finished
|
|
31
25
|
export { pipeline, finished, finishedAll } from "./node/pipeline.js";
|
|
32
26
|
// Compose
|
|
33
27
|
export { compose } from "./node/compose.js";
|
|
34
28
|
// Utilities
|
|
35
29
|
export { streamToPromise, streamToUint8Array, streamToBuffer, streamToString, drainStream, copyStream, isTransform, isDuplex, isStream, addAbortSignal, isDisturbed, isReadable, isWritable, duplexPair, consumers, promises } from "./node/utils.js";
|
|
30
|
+
// Web Streams conversions
|
|
31
|
+
export { readableFromWeb, readableToWeb, toWritable, writableFromWeb, writableToWeb } from "./node/web.js";
|