@takeshape/streams 11.51.6 → 11.52.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 CHANGED
@@ -1,5 +1,3 @@
1
1
  # Streams
2
2
 
3
- ## Summary
4
-
5
- A set of utility functions for working with Node.js streams v3
3
+ A set of utility functions for working with Node.js streams.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
- export * from './streams';
2
- export * from './merge';
3
- //# sourceMappingURL=index.d.ts.map
1
+ export * from './streams.js';
2
+ export * from './merge.js';
package/dist/index.js CHANGED
@@ -1,27 +1,2 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- var _streams = require("./streams");
7
- Object.keys(_streams).forEach(function (key) {
8
- if (key === "default" || key === "__esModule") return;
9
- if (key in exports && exports[key] === _streams[key]) return;
10
- Object.defineProperty(exports, key, {
11
- enumerable: true,
12
- get: function () {
13
- return _streams[key];
14
- }
15
- });
16
- });
17
- var _merge = require("./merge");
18
- Object.keys(_merge).forEach(function (key) {
19
- if (key === "default" || key === "__esModule") return;
20
- if (key in exports && exports[key] === _merge[key]) return;
21
- Object.defineProperty(exports, key, {
22
- enumerable: true,
23
- get: function () {
24
- return _merge[key];
25
- }
26
- });
27
- });
1
+ export * from './streams.js';
2
+ export * from './merge.js';
package/dist/merge.d.ts CHANGED
@@ -6,4 +6,3 @@ declare class MergedStream extends PassThroughStream {
6
6
  remove(stream: Readable): Promise<boolean>;
7
7
  }
8
8
  export {};
9
- //# sourceMappingURL=merge.d.ts.map
package/dist/merge.js CHANGED
@@ -1,283 +1,246 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.mergeStreams = mergeStreams;
7
- var _nodeEvents = require("node:events");
8
- var _nodeStream = require("node:stream");
9
- var _promises = require("node:stream/promises");
10
1
  /* istanbul ignore file */
11
2
  /**
12
3
  * Borrowed code, due to ESM-only issues
13
4
  *
14
5
  * @link https://github.com/sindresorhus/merge-streams/blob/main/index.js
15
6
  */
16
-
17
- function mergeStreams(streams) {
18
- if (!Array.isArray(streams)) {
19
- throw new TypeError(`Expected an array, got \`${typeof streams}\`.`);
20
- }
21
- for (const stream of streams) {
22
- validateStream(stream);
23
- }
24
- const objectMode = streams.some(({
25
- readableObjectMode
26
- }) => readableObjectMode);
27
- const highWaterMark = getHighWaterMark(streams, objectMode);
28
- const passThroughStream = new MergedStream({
29
- objectMode,
30
- writableHighWaterMark: highWaterMark,
31
- readableHighWaterMark: highWaterMark
32
- });
33
- for (const stream of streams) {
34
- passThroughStream.add(stream);
35
- }
36
- return passThroughStream;
7
+ import { on, once } from 'node:events';
8
+ import { PassThrough as PassThroughStream, getDefaultHighWaterMark } from 'node:stream';
9
+ import { finished } from 'node:stream/promises';
10
+ export function mergeStreams(streams) {
11
+ if (!Array.isArray(streams)) {
12
+ throw new TypeError(`Expected an array, got \`${typeof streams}\`.`);
13
+ }
14
+ for (const stream of streams) {
15
+ validateStream(stream);
16
+ }
17
+ const objectMode = streams.some(({ readableObjectMode }) => readableObjectMode);
18
+ const highWaterMark = getHighWaterMark(streams, objectMode);
19
+ const passThroughStream = new MergedStream({
20
+ objectMode,
21
+ writableHighWaterMark: highWaterMark,
22
+ readableHighWaterMark: highWaterMark
23
+ });
24
+ for (const stream of streams) {
25
+ passThroughStream.add(stream);
26
+ }
27
+ return passThroughStream;
37
28
  }
38
29
  const getHighWaterMark = (streams, objectMode) => {
39
- if (streams.length === 0) {
40
- return (0, _nodeStream.getDefaultHighWaterMark)(objectMode);
41
- }
42
- const highWaterMarks = streams.filter(({
43
- readableObjectMode
44
- }) => readableObjectMode === objectMode).map(({
45
- readableHighWaterMark
46
- }) => readableHighWaterMark);
47
- return Math.max(...highWaterMarks);
48
- };
49
- class MergedStream extends _nodeStream.PassThrough {
50
- #streams = new Set([]);
51
- #ended = new Set([]);
52
- #aborted = new Set([]);
53
- #onFinished;
54
- #unpipeEvent = Symbol('unpipe');
55
- #streamPromises = new WeakMap();
56
- add(stream) {
57
- validateStream(stream);
58
- if (this.#streams.has(stream)) {
59
- return;
30
+ if (streams.length === 0) {
31
+ return getDefaultHighWaterMark(objectMode);
60
32
  }
61
- this.#streams.add(stream);
62
- this.#onFinished ??= onMergedStreamFinished(this, this.#streams, this.#unpipeEvent);
63
- const streamPromise = endWhenStreamsDone({
64
- passThroughStream: this,
65
- stream,
66
- streams: this.#streams,
67
- ended: this.#ended,
68
- aborted: this.#aborted,
69
- onFinished: this.#onFinished,
70
- unpipeEvent: this.#unpipeEvent
71
- });
72
- this.#streamPromises.set(stream, streamPromise);
73
- stream.pipe(this, {
74
- end: false
75
- });
76
- }
77
- async remove(stream) {
78
- validateStream(stream);
79
- if (!this.#streams.has(stream)) {
80
- return false;
33
+ const highWaterMarks = streams
34
+ .filter(({ readableObjectMode }) => readableObjectMode === objectMode)
35
+ .map(({ readableHighWaterMark }) => readableHighWaterMark);
36
+ return Math.max(...highWaterMarks);
37
+ };
38
+ class MergedStream extends PassThroughStream {
39
+ #streams = new Set([]);
40
+ #ended = new Set([]);
41
+ #aborted = new Set([]);
42
+ #onFinished;
43
+ #unpipeEvent = Symbol('unpipe');
44
+ #streamPromises = new WeakMap();
45
+ add(stream) {
46
+ validateStream(stream);
47
+ if (this.#streams.has(stream)) {
48
+ return;
49
+ }
50
+ this.#streams.add(stream);
51
+ this.#onFinished ??= onMergedStreamFinished(this, this.#streams, this.#unpipeEvent);
52
+ const streamPromise = endWhenStreamsDone({
53
+ passThroughStream: this,
54
+ stream,
55
+ streams: this.#streams,
56
+ ended: this.#ended,
57
+ aborted: this.#aborted,
58
+ onFinished: this.#onFinished,
59
+ unpipeEvent: this.#unpipeEvent
60
+ });
61
+ this.#streamPromises.set(stream, streamPromise);
62
+ stream.pipe(this, { end: false });
81
63
  }
82
- const streamPromise = this.#streamPromises.get(stream);
83
- if (streamPromise === undefined) {
84
- return false;
64
+ async remove(stream) {
65
+ validateStream(stream);
66
+ if (!this.#streams.has(stream)) {
67
+ return false;
68
+ }
69
+ const streamPromise = this.#streamPromises.get(stream);
70
+ if (streamPromise === undefined) {
71
+ return false;
72
+ }
73
+ this.#streamPromises.delete(stream);
74
+ stream.unpipe(this);
75
+ await streamPromise;
76
+ return true;
85
77
  }
86
- this.#streamPromises.delete(stream);
87
- stream.unpipe(this);
88
- await streamPromise;
89
- return true;
90
- }
91
78
  }
92
79
  const onMergedStreamFinished = async (passThroughStream, streams, unpipeEvent) => {
93
- updateMaxListeners(passThroughStream, PASSTHROUGH_LISTENERS_COUNT);
94
- const controller = new AbortController();
95
- try {
96
- await Promise.race([onMergedStreamEnd(passThroughStream, controller), onInputStreamsUnpipe(passThroughStream, streams, unpipeEvent, controller)]);
97
- } finally {
98
- controller.abort();
99
- updateMaxListeners(passThroughStream, -PASSTHROUGH_LISTENERS_COUNT);
100
- }
101
- };
102
- const onMergedStreamEnd = async (passThroughStream, {
103
- signal
104
- }) => {
105
- try {
106
- await (0, _promises.finished)(passThroughStream, {
107
- signal,
108
- cleanup: true
109
- });
110
- } catch (error) {
111
- errorOrAbortStream(passThroughStream, error);
112
- throw error;
113
- }
80
+ updateMaxListeners(passThroughStream, PASSTHROUGH_LISTENERS_COUNT);
81
+ const controller = new AbortController();
82
+ try {
83
+ await Promise.race([
84
+ onMergedStreamEnd(passThroughStream, controller),
85
+ onInputStreamsUnpipe(passThroughStream, streams, unpipeEvent, controller)
86
+ ]);
87
+ }
88
+ finally {
89
+ controller.abort();
90
+ updateMaxListeners(passThroughStream, -PASSTHROUGH_LISTENERS_COUNT);
91
+ }
114
92
  };
115
- const onInputStreamsUnpipe = async (passThroughStream, streams, unpipeEvent, {
116
- signal
117
- }) => {
118
- for await (const [unpipedStream] of (0, _nodeEvents.on)(passThroughStream, 'unpipe', {
119
- signal
120
- })) {
121
- if (streams.has(unpipedStream)) {
122
- unpipedStream.emit(unpipeEvent);
93
+ const onMergedStreamEnd = async (passThroughStream, { signal }) => {
94
+ try {
95
+ await finished(passThroughStream, {
96
+ signal,
97
+ cleanup: true
98
+ });
99
+ }
100
+ catch (error) {
101
+ errorOrAbortStream(passThroughStream, error);
102
+ throw error;
123
103
  }
124
- }
125
104
  };
126
- const validateStream = stream => {
127
- if (typeof stream?.pipe !== 'function') {
128
- throw new TypeError(`Expected a readable stream, got: \`${typeof stream}\`.`);
129
- }
105
+ const onInputStreamsUnpipe = async (passThroughStream, streams, unpipeEvent, { signal }) => {
106
+ for await (const [unpipedStream] of on(passThroughStream, 'unpipe', {
107
+ signal
108
+ })) {
109
+ if (streams.has(unpipedStream)) {
110
+ unpipedStream.emit(unpipeEvent);
111
+ }
112
+ }
130
113
  };
131
- const endWhenStreamsDone = async ({
132
- passThroughStream,
133
- stream,
134
- streams,
135
- ended,
136
- aborted,
137
- onFinished,
138
- unpipeEvent
139
- }) => {
140
- updateMaxListeners(passThroughStream, PASSTHROUGH_LISTENERS_PER_STREAM);
141
- const controller = new AbortController();
142
- try {
143
- await Promise.race([afterMergedStreamFinished(onFinished, stream, controller), onInputStreamEnd({
144
- passThroughStream,
145
- stream,
146
- streams,
147
- ended,
148
- aborted,
149
- controller
150
- }), onInputStreamUnpipe({
151
- stream,
152
- streams,
153
- ended,
154
- aborted,
155
- unpipeEvent,
156
- controller
157
- })]);
158
- } finally {
159
- controller.abort();
160
- updateMaxListeners(passThroughStream, -PASSTHROUGH_LISTENERS_PER_STREAM);
161
- }
162
- if (streams.size > 0 && streams.size === ended.size + aborted.size) {
163
- if (ended.size === 0 && aborted.size > 0) {
164
- abortStream(passThroughStream);
165
- } else {
166
- endStream(passThroughStream);
114
+ const validateStream = (stream) => {
115
+ if (typeof stream?.pipe !== 'function') {
116
+ throw new TypeError(`Expected a readable stream, got: \`${typeof stream}\`.`);
167
117
  }
168
- }
169
118
  };
170
- const afterMergedStreamFinished = async (onFinished, stream, {
171
- signal
172
- }) => {
173
- try {
174
- await onFinished;
175
- if (!signal.aborted) {
176
- abortStream(stream);
119
+ const endWhenStreamsDone = async ({ passThroughStream, stream, streams, ended, aborted, onFinished, unpipeEvent }) => {
120
+ updateMaxListeners(passThroughStream, PASSTHROUGH_LISTENERS_PER_STREAM);
121
+ const controller = new AbortController();
122
+ try {
123
+ await Promise.race([
124
+ afterMergedStreamFinished(onFinished, stream, controller),
125
+ onInputStreamEnd({
126
+ passThroughStream,
127
+ stream,
128
+ streams,
129
+ ended,
130
+ aborted,
131
+ controller
132
+ }),
133
+ onInputStreamUnpipe({
134
+ stream,
135
+ streams,
136
+ ended,
137
+ aborted,
138
+ unpipeEvent,
139
+ controller
140
+ })
141
+ ]);
177
142
  }
178
- } catch (error) {
179
- if (!signal.aborted) {
180
- errorOrAbortStream(stream, error);
143
+ finally {
144
+ controller.abort();
145
+ updateMaxListeners(passThroughStream, -PASSTHROUGH_LISTENERS_PER_STREAM);
146
+ }
147
+ if (streams.size > 0 && streams.size === ended.size + aborted.size) {
148
+ if (ended.size === 0 && aborted.size > 0) {
149
+ abortStream(passThroughStream);
150
+ }
151
+ else {
152
+ endStream(passThroughStream);
153
+ }
181
154
  }
182
- }
183
155
  };
184
- const onInputStreamEnd = async ({
185
- passThroughStream,
186
- stream,
187
- streams,
188
- ended,
189
- aborted,
190
- controller: {
191
- signal
192
- }
193
- }) => {
194
- try {
195
- await (0, _promises.finished)(stream, {
196
- signal,
197
- cleanup: true,
198
- readable: true,
199
- writable: false
200
- });
201
- if (streams.has(stream)) {
202
- ended.add(stream);
156
+ const afterMergedStreamFinished = async (onFinished, stream, { signal }) => {
157
+ try {
158
+ await onFinished;
159
+ if (!signal.aborted) {
160
+ abortStream(stream);
161
+ }
203
162
  }
204
- } catch (error) {
205
- if (signal.aborted || !streams.has(stream)) {
206
- return;
163
+ catch (error) {
164
+ if (!signal.aborted) {
165
+ errorOrAbortStream(stream, error);
166
+ }
207
167
  }
208
- if (isAbortError(error)) {
209
- aborted.add(stream);
210
- } else {
211
- errorStream(passThroughStream, error);
168
+ };
169
+ const onInputStreamEnd = async ({ passThroughStream, stream, streams, ended, aborted, controller: { signal } }) => {
170
+ try {
171
+ await finished(stream, {
172
+ signal,
173
+ cleanup: true,
174
+ readable: true,
175
+ writable: false
176
+ });
177
+ if (streams.has(stream)) {
178
+ ended.add(stream);
179
+ }
180
+ }
181
+ catch (error) {
182
+ if (signal.aborted || !streams.has(stream)) {
183
+ return;
184
+ }
185
+ if (isAbortError(error)) {
186
+ aborted.add(stream);
187
+ }
188
+ else {
189
+ errorStream(passThroughStream, error);
190
+ }
212
191
  }
213
- }
214
192
  };
215
- const onInputStreamUnpipe = async ({
216
- stream,
217
- streams,
218
- ended,
219
- aborted,
220
- unpipeEvent,
221
- controller: {
222
- signal
223
- }
224
- }) => {
225
- await (0, _nodeEvents.once)(stream, unpipeEvent, {
226
- signal
227
- });
228
- if (!stream.readable) {
229
- return (0, _nodeEvents.once)(signal, 'abort', {
230
- signal
231
- });
232
- }
233
- streams.delete(stream);
234
- ended.delete(stream);
235
- aborted.delete(stream);
193
+ const onInputStreamUnpipe = async ({ stream, streams, ended, aborted, unpipeEvent, controller: { signal } }) => {
194
+ await once(stream, unpipeEvent, { signal });
195
+ if (!stream.readable) {
196
+ return once(signal, 'abort', { signal });
197
+ }
198
+ streams.delete(stream);
199
+ ended.delete(stream);
200
+ aborted.delete(stream);
201
+ return;
236
202
  };
237
- const endStream = stream => {
238
- if (stream.writable) {
239
- stream.end();
240
- }
203
+ const endStream = (stream) => {
204
+ if (stream.writable) {
205
+ stream.end();
206
+ }
241
207
  };
242
208
  const errorOrAbortStream = (stream, error) => {
243
- if (isAbortError(error)) {
244
- abortStream(stream);
245
- } else {
246
- errorStream(stream, error);
247
- }
209
+ if (isAbortError(error)) {
210
+ abortStream(stream);
211
+ }
212
+ else {
213
+ errorStream(stream, error);
214
+ }
248
215
  };
249
-
250
216
  // This is the error thrown by `finished()` on `stream.destroy()`
251
- const isAbortError = error => error?.code === 'ERR_STREAM_PREMATURE_CLOSE';
252
- const abortStream = stream => {
253
- if (stream.readable || stream.writable) {
254
- stream.destroy();
255
- }
217
+ const isAbortError = (error) => error?.code === 'ERR_STREAM_PREMATURE_CLOSE';
218
+ const abortStream = (stream) => {
219
+ if (stream.readable || stream.writable) {
220
+ stream.destroy();
221
+ }
256
222
  };
257
-
258
223
  // `stream.destroy(error)` crashes the process with `uncaughtException` if no `error` event listener exists on `stream`.
259
224
  // We take care of error handling on user behalf, so we do not want this to happen.
260
225
  const errorStream = (stream, error) => {
261
- if (!stream.destroyed) {
262
- stream.once('error', noop);
263
- stream.destroy(error);
264
- }
226
+ if (!stream.destroyed) {
227
+ stream.once('error', noop);
228
+ stream.destroy(error);
229
+ }
265
230
  };
266
231
  const noop = () => {
267
- // empty
232
+ // empty
268
233
  };
269
234
  const updateMaxListeners = (passThroughStream, increment) => {
270
- const maxListeners = passThroughStream.getMaxListeners();
271
- if (maxListeners !== 0 && maxListeners !== Number.POSITIVE_INFINITY) {
272
- passThroughStream.setMaxListeners(maxListeners + increment);
273
- }
235
+ const maxListeners = passThroughStream.getMaxListeners();
236
+ if (maxListeners !== 0 && maxListeners !== Number.POSITIVE_INFINITY) {
237
+ passThroughStream.setMaxListeners(maxListeners + increment);
238
+ }
274
239
  };
275
-
276
240
  // Number of times `passThroughStream.on()` is called regardless of streams:
277
241
  // - once due to `finished(passThroughStream)`
278
242
  // - once due to `on(passThroughStream)`
279
243
  const PASSTHROUGH_LISTENERS_COUNT = 2;
280
-
281
244
  // Number of times `passThroughStream.on()` is called per stream:
282
245
  // - once due to `stream.pipe(passThroughStream)`
283
- const PASSTHROUGH_LISTENERS_PER_STREAM = 1;
246
+ const PASSTHROUGH_LISTENERS_PER_STREAM = 1;
package/dist/streams.d.ts CHANGED
@@ -1,5 +1,19 @@
1
1
  import { Readable, type ReadableOptions, Transform, Writable, type WritableOptions } from 'node:stream';
2
2
  import { pipeline } from 'node:stream/promises';
3
+ declare module 'node:stream' {
4
+ /**
5
+ * Experimental!
6
+ *
7
+ * Provisional typing for experimental feature: https://nodejs.org/dist/latest-v20.x/docs/api/stream.html#streamcomposestreams
8
+ *
9
+ * Combines two or more streams into a Duplex stream that writes to the first stream and reads from the last. Each provided stream is piped into the next, using stream.pipeline. If any of the streams error then all are destroyed, including the outer Duplex stream.
10
+ *
11
+ * Because stream.compose returns a new stream that in turn can (and should) be piped into other streams, it enables composition. In contrast, when passing streams to stream.pipeline, typically the first stream is a readable stream and the last a writable stream, forming a closed circuit.
12
+ *
13
+ * If passed a Function it must be a factory method taking a source Iterable.
14
+ */
15
+ const compose: (stream: NodeJS.ReadableStream, ...streams: Array<NodeJS.WritableStream | NodeJS.ReadWriteStream | Iterable<unknown>>) => NodeJS.ReadWriteStream;
16
+ }
3
17
  export { compose } from 'node:stream';
4
18
  export declare const pump: typeof pipeline;
5
19
  export declare function streamToPromise(stream: NodeJS.ReadableStream | NodeJS.WritableStream): Promise<void>;
@@ -13,4 +27,3 @@ export declare function readableFromArray(array: any[], options?: Partial<Readab
13
27
  export declare function collectStreamIntoArray(array?: any[], options?: Partial<WritableOptions>): Writable;
14
28
  export declare function readStreamToBuffer(stream: NodeJS.ReadableStream): Promise<Buffer>;
15
29
  export declare function batch(size: number): Transform;
16
- //# sourceMappingURL=streams.d.ts.map