@gjsify/stream 0.4.0 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +55 -51
- package/src/callable.spec.ts +0 -196
- package/src/callable.ts +0 -3
- package/src/consumers/index.spec.ts +0 -107
- package/src/consumers/index.ts +0 -39
- package/src/duplex.ts +0 -317
- package/src/edge-cases.spec.ts +0 -593
- package/src/index.spec.ts +0 -2252
- package/src/index.ts +0 -92
- package/src/inheritance.spec.ts +0 -315
- package/src/internal/state.ts +0 -34
- package/src/internal/types.ts +0 -31
- package/src/passthrough.ts +0 -19
- package/src/pipe.spec.ts +0 -530
- package/src/promises/index.spec.ts +0 -140
- package/src/promises/index.ts +0 -31
- package/src/readable.ts +0 -580
- package/src/spec-internals.d.ts +0 -28
- package/src/stream-base.ts +0 -37
- package/src/test.mts +0 -26
- package/src/transform.spec.ts +0 -520
- package/src/transform.ts +0 -108
- package/src/utils/finished.ts +0 -156
- package/src/utils/pipe.ts +0 -113
- package/src/utils/pipeline.ts +0 -59
- package/src/web/index.ts +0 -32
- package/src/writable.ts +0 -398
- package/tsconfig.json +0 -29
- package/tsconfig.tsbuildinfo +0 -1
package/src/pipe.spec.ts
DELETED
|
@@ -1,530 +0,0 @@
|
|
|
1
|
-
// pipe() test suite.
|
|
2
|
-
//
|
|
3
|
-
// Ported from:
|
|
4
|
-
// refs/node-test/parallel/test-stream-pipe-after-end.js
|
|
5
|
-
// refs/node-test/parallel/test-stream-pipe-cleanup.js
|
|
6
|
-
// refs/node-test/parallel/test-stream-pipe-error-handling.js
|
|
7
|
-
// refs/node-test/parallel/test-stream-pipe-event.js
|
|
8
|
-
// refs/node-test/parallel/test-stream-pipe-flow.js
|
|
9
|
-
// refs/node-test/parallel/test-stream-pipe-multiple-pipes.js
|
|
10
|
-
// refs/node-test/parallel/test-stream-pipe-needDrain.js
|
|
11
|
-
// refs/node-test/parallel/test-stream-pipe-objectmode-to-non-objectmode.js
|
|
12
|
-
// refs/node-test/parallel/test-stream-pipe-same-destination-twice.js
|
|
13
|
-
// refs/node-test/parallel/test-stream-pipe-unpipe-streams.js
|
|
14
|
-
// refs/node-test/parallel/test-stream-pipe-without-listenerCount.js
|
|
15
|
-
// Original: MIT license, Node.js contributors (and Joyent, Inc.)
|
|
16
|
-
// Modifications: adapted to @gjsify/unit, async/await.
|
|
17
|
-
|
|
18
|
-
import { describe, it, expect } from '@gjsify/unit';
|
|
19
|
-
import { Stream, Readable, Writable, PassThrough, Transform } from 'node:stream';
|
|
20
|
-
|
|
21
|
-
export default async () => {
|
|
22
|
-
|
|
23
|
-
// -------------------------------------------------------------------------
|
|
24
|
-
// pipe: 'pipe' event on destination
|
|
25
|
-
// -------------------------------------------------------------------------
|
|
26
|
-
await describe('pipe: pipe event', async () => {
|
|
27
|
-
await it('pipe event fires on writable when piped', async () => {
|
|
28
|
-
const w: any = Object.create(Stream.prototype);
|
|
29
|
-
(Stream as any).call(w);
|
|
30
|
-
w.writable = true;
|
|
31
|
-
|
|
32
|
-
const r: any = Object.create(Stream.prototype);
|
|
33
|
-
(Stream as any).call(r);
|
|
34
|
-
r.readable = true;
|
|
35
|
-
|
|
36
|
-
let passed = false;
|
|
37
|
-
w.on('pipe', (src: any) => { passed = true; });
|
|
38
|
-
r.pipe(w);
|
|
39
|
-
expect(passed).toBe(true);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// -------------------------------------------------------------------------
|
|
44
|
-
// pipe: after end
|
|
45
|
-
// -------------------------------------------------------------------------
|
|
46
|
-
await describe('pipe: after end', async () => {
|
|
47
|
-
await it('piping an already-ended readable to a writable finishes the writable', async () => {
|
|
48
|
-
class TestReadable extends Readable {
|
|
49
|
-
_read() { this.push(null); }
|
|
50
|
-
}
|
|
51
|
-
class TestWritable extends Writable {
|
|
52
|
-
public _written: Buffer[] = [];
|
|
53
|
-
_write(chunk: Buffer, _enc: string, cb: () => void) {
|
|
54
|
-
this._written.push(chunk);
|
|
55
|
-
cb();
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const piper = new TestReadable();
|
|
59
|
-
piper.read(); // triggers end
|
|
60
|
-
|
|
61
|
-
await new Promise<void>((resolve) => {
|
|
62
|
-
const w = new TestWritable();
|
|
63
|
-
w.on('finish', resolve);
|
|
64
|
-
piper.pipe(w);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
await it('readable that has not been read emits end once via pipe', async () => {
|
|
69
|
-
class TestReadable extends Readable {
|
|
70
|
-
private _called = false;
|
|
71
|
-
_read() {
|
|
72
|
-
if (this._called) this.emit('error', new Error('_read called twice'));
|
|
73
|
-
this._called = true;
|
|
74
|
-
this.push(null);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
const ender = new TestReadable();
|
|
78
|
-
await new Promise<void>((resolve) => {
|
|
79
|
-
ender.on('end', resolve);
|
|
80
|
-
const c = ender.read();
|
|
81
|
-
expect(c).toBe(null);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// -------------------------------------------------------------------------
|
|
87
|
-
// pipe: cleanup — no dangling listeners
|
|
88
|
-
// -------------------------------------------------------------------------
|
|
89
|
-
await describe('pipe: cleanup removes listeners after end/close', async () => {
|
|
90
|
-
await it('end event removes all listeners added by pipe', async () => {
|
|
91
|
-
// Legacy Stream-based test — uses Stream.call(this) pattern
|
|
92
|
-
function CustomWritable(this: any) {
|
|
93
|
-
this.writable = true;
|
|
94
|
-
this.endCalls = 0;
|
|
95
|
-
(Stream as any).call(this);
|
|
96
|
-
}
|
|
97
|
-
Object.setPrototypeOf(CustomWritable.prototype, Stream.prototype);
|
|
98
|
-
(CustomWritable.prototype as any).end = function() { this.endCalls++; };
|
|
99
|
-
(CustomWritable.prototype as any).destroy = function() { this.endCalls++; };
|
|
100
|
-
|
|
101
|
-
function CustomReadable(this: any) {
|
|
102
|
-
this.readable = true;
|
|
103
|
-
(Stream as any).call(this);
|
|
104
|
-
}
|
|
105
|
-
Object.setPrototypeOf(CustomReadable.prototype, Stream.prototype);
|
|
106
|
-
|
|
107
|
-
const w: any = new (CustomWritable as any)();
|
|
108
|
-
let r: any;
|
|
109
|
-
const limit = 10;
|
|
110
|
-
|
|
111
|
-
for (let i = 0; i < limit; i++) {
|
|
112
|
-
r = new (CustomReadable as any)();
|
|
113
|
-
r.pipe(w);
|
|
114
|
-
r.emit('end');
|
|
115
|
-
}
|
|
116
|
-
expect(r.listeners('end').length).toBe(0);
|
|
117
|
-
expect(w.endCalls).toBe(limit);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
await it('close event removes all listeners added by pipe', async () => {
|
|
121
|
-
function CustomWritable(this: any) {
|
|
122
|
-
this.writable = true;
|
|
123
|
-
this.endCalls = 0;
|
|
124
|
-
(Stream as any).call(this);
|
|
125
|
-
}
|
|
126
|
-
Object.setPrototypeOf(CustomWritable.prototype, Stream.prototype);
|
|
127
|
-
(CustomWritable.prototype as any).end = function() { this.endCalls++; };
|
|
128
|
-
(CustomWritable.prototype as any).destroy = function() { this.endCalls++; };
|
|
129
|
-
|
|
130
|
-
function CustomReadable(this: any) {
|
|
131
|
-
this.readable = true;
|
|
132
|
-
(Stream as any).call(this);
|
|
133
|
-
}
|
|
134
|
-
Object.setPrototypeOf(CustomReadable.prototype, Stream.prototype);
|
|
135
|
-
|
|
136
|
-
const w: any = new (CustomWritable as any)();
|
|
137
|
-
let r: any;
|
|
138
|
-
const limit = 10;
|
|
139
|
-
|
|
140
|
-
for (let i = 0; i < limit; i++) {
|
|
141
|
-
r = new (CustomReadable as any)();
|
|
142
|
-
r.pipe(w);
|
|
143
|
-
r.emit('close');
|
|
144
|
-
}
|
|
145
|
-
expect(r.listeners('close').length).toBe(0);
|
|
146
|
-
expect(w.endCalls).toBe(limit);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// -------------------------------------------------------------------------
|
|
151
|
-
// pipe: error handling
|
|
152
|
-
// -------------------------------------------------------------------------
|
|
153
|
-
await describe('pipe: error handling', async () => {
|
|
154
|
-
await it('source with error listener does not propagate error to dest', async () => {
|
|
155
|
-
const source = new Stream();
|
|
156
|
-
const dest = new Stream();
|
|
157
|
-
// Legacy bare Stream→Stream pipe — @types/node's pipe() expects a Writable.
|
|
158
|
-
source.pipe(dest as unknown as Writable);
|
|
159
|
-
|
|
160
|
-
let gotErr: Error | null = null;
|
|
161
|
-
source.on('error', (err) => { gotErr = err; });
|
|
162
|
-
|
|
163
|
-
const err = new Error('source error');
|
|
164
|
-
source.emit('error', err);
|
|
165
|
-
expect(gotErr).toBe(err);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
await it('source without error listener throws on emit', async () => {
|
|
169
|
-
const source = new Stream();
|
|
170
|
-
const dest = new Stream();
|
|
171
|
-
source.pipe(dest as unknown as Writable);
|
|
172
|
-
|
|
173
|
-
const err = new Error('uncaught source error');
|
|
174
|
-
let gotErr: Error | null = null;
|
|
175
|
-
try {
|
|
176
|
-
source.emit('error', err);
|
|
177
|
-
} catch (e: any) {
|
|
178
|
-
gotErr = e;
|
|
179
|
-
}
|
|
180
|
-
expect(gotErr).toBe(err);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
await it('removeListener cleans up error handler from piped dest', async () => {
|
|
184
|
-
const r = new Readable({ autoDestroy: false, read() {} });
|
|
185
|
-
const w = new Writable({ autoDestroy: false, write(_c, _e, cb) { cb(); } });
|
|
186
|
-
|
|
187
|
-
function badHandler() {
|
|
188
|
-
throw new Error('should not happen');
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
w.on('error', badHandler);
|
|
192
|
-
r.pipe(w);
|
|
193
|
-
w.removeListener('error', badHandler);
|
|
194
|
-
|
|
195
|
-
// Should not throw
|
|
196
|
-
let threw = false;
|
|
197
|
-
try {
|
|
198
|
-
w.emit('error', new Error('fail'));
|
|
199
|
-
} catch {
|
|
200
|
-
threw = true;
|
|
201
|
-
}
|
|
202
|
-
expect(threw).toBe(true); // no handler → throws
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
await it('error from destroyed dest propagates correctly', async () => {
|
|
206
|
-
const _err = new Error('dest destroyed');
|
|
207
|
-
const dest = new PassThrough();
|
|
208
|
-
const stream = new Stream();
|
|
209
|
-
stream.pipe(dest);
|
|
210
|
-
|
|
211
|
-
await new Promise<void>((resolve) => {
|
|
212
|
-
dest.once('error', (err) => {
|
|
213
|
-
expect(err).toBe(_err);
|
|
214
|
-
resolve();
|
|
215
|
-
});
|
|
216
|
-
dest.destroy(_err);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
await it('pipe without listenerCount still handles errors', async () => {
|
|
221
|
-
const r: any = new Stream();
|
|
222
|
-
r.listenerCount = undefined;
|
|
223
|
-
const w: any = new Stream();
|
|
224
|
-
w.listenerCount = undefined;
|
|
225
|
-
|
|
226
|
-
const errors: Error[] = [];
|
|
227
|
-
r.on('error', (err: Error) => errors.push(err));
|
|
228
|
-
w.on('error', (err: Error) => errors.push(err));
|
|
229
|
-
|
|
230
|
-
w.on('pipe', () => {
|
|
231
|
-
r.emit('error', new Error('Readable Error'));
|
|
232
|
-
w.emit('error', new Error('Writable Error'));
|
|
233
|
-
});
|
|
234
|
-
r.pipe(w);
|
|
235
|
-
|
|
236
|
-
expect(errors.length).toBe(2);
|
|
237
|
-
expect(errors[0].message).toBe('Readable Error');
|
|
238
|
-
expect(errors[1].message).toBe('Writable Error');
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
// -------------------------------------------------------------------------
|
|
243
|
-
// pipe: multiple destinations
|
|
244
|
-
// -------------------------------------------------------------------------
|
|
245
|
-
await describe('pipe: multiple destinations', async () => {
|
|
246
|
-
await it('pipe to 5 writables — each receives the same chunk', async () => {
|
|
247
|
-
const readable = new Readable({ read() {} });
|
|
248
|
-
const results: Buffer[][] = [];
|
|
249
|
-
const finishPromises: Promise<void>[] = [];
|
|
250
|
-
|
|
251
|
-
for (let i = 0; i < 5; i++) {
|
|
252
|
-
const out: Buffer[] = [];
|
|
253
|
-
results.push(out);
|
|
254
|
-
const w = new Writable({
|
|
255
|
-
write(chunk, _enc, cb) { out.push(chunk); cb(); },
|
|
256
|
-
});
|
|
257
|
-
readable.pipe(w);
|
|
258
|
-
finishPromises.push(new Promise<void>((resolve) => w.on('finish', resolve)));
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const input = Buffer.from([1, 2, 3, 4, 5]);
|
|
262
|
-
readable.push(input);
|
|
263
|
-
readable.push(null); // end all pipes
|
|
264
|
-
|
|
265
|
-
await Promise.all(finishPromises);
|
|
266
|
-
|
|
267
|
-
for (const out of results) {
|
|
268
|
-
expect(out.length).toBe(1);
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
await it('unpipe removes data forwarding to specific dest', async () => {
|
|
273
|
-
const readable = new Readable({ read() {} });
|
|
274
|
-
const out1: Buffer[] = [];
|
|
275
|
-
const out2: Buffer[] = [];
|
|
276
|
-
const w1 = new Writable({ write(c, _e, cb) { out1.push(c); cb(); } });
|
|
277
|
-
const w2 = new Writable({ write(c, _e, cb) { out2.push(c); cb(); } });
|
|
278
|
-
|
|
279
|
-
readable.pipe(w1);
|
|
280
|
-
readable.pipe(w2);
|
|
281
|
-
readable.unpipe(w2);
|
|
282
|
-
|
|
283
|
-
// Wait for w1 to finish — proves data reached w1 but not w2
|
|
284
|
-
await new Promise<void>((resolve) => {
|
|
285
|
-
w1.on('finish', resolve);
|
|
286
|
-
readable.push(Buffer.from('hello'));
|
|
287
|
-
readable.push(null);
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
expect(out1.length).toBe(1);
|
|
291
|
-
expect(out2.length).toBe(0);
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
// -------------------------------------------------------------------------
|
|
296
|
-
// pipe: same destination twice
|
|
297
|
-
// -------------------------------------------------------------------------
|
|
298
|
-
await describe('pipe: same destination twice', async () => {
|
|
299
|
-
await it('pipe twice to same dest registers 2 data listeners', async () => {
|
|
300
|
-
const pt = new PassThrough();
|
|
301
|
-
const dest = new Writable({ write(_c, _e, cb) { cb(); } });
|
|
302
|
-
|
|
303
|
-
pt.pipe(dest);
|
|
304
|
-
pt.pipe(dest);
|
|
305
|
-
|
|
306
|
-
expect((pt._readableState as any).pipes.length).toBe(2);
|
|
307
|
-
expect((pt._readableState as any).pipes[0]).toBe(dest);
|
|
308
|
-
expect((pt._readableState as any).pipes[1]).toBe(dest);
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
await it('unpipe once removes only one of the two pipe registrations', async () => {
|
|
312
|
-
const pt = new PassThrough();
|
|
313
|
-
const dest = new Writable({ write(_c, _e, cb) { cb(); } });
|
|
314
|
-
|
|
315
|
-
pt.pipe(dest);
|
|
316
|
-
pt.pipe(dest);
|
|
317
|
-
pt.unpipe(dest);
|
|
318
|
-
|
|
319
|
-
expect((pt._readableState as any).pipes.length).toBe(1);
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
await it('unpipe twice removes both registrations', async () => {
|
|
323
|
-
const pt = new PassThrough();
|
|
324
|
-
const dest = new Writable({ write(_c, _e, cb) { cb(); } });
|
|
325
|
-
|
|
326
|
-
pt.pipe(dest);
|
|
327
|
-
pt.pipe(dest);
|
|
328
|
-
pt.unpipe(dest);
|
|
329
|
-
pt.unpipe(dest);
|
|
330
|
-
|
|
331
|
-
expect((pt._readableState as any).pipes.length).toBe(0);
|
|
332
|
-
});
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
// -------------------------------------------------------------------------
|
|
336
|
-
// pipe: unpipe streams
|
|
337
|
-
// -------------------------------------------------------------------------
|
|
338
|
-
await describe('pipe: unpipe', async () => {
|
|
339
|
-
await it('unpipe in reverse order removes correct dest', async () => {
|
|
340
|
-
const source = new Readable({ read() {} });
|
|
341
|
-
const dest1 = new Writable({ write(_c, _e, cb) { cb(); } });
|
|
342
|
-
const dest2 = new Writable({ write(_c, _e, cb) { cb(); } });
|
|
343
|
-
|
|
344
|
-
source.pipe(dest1);
|
|
345
|
-
source.pipe(dest2);
|
|
346
|
-
|
|
347
|
-
expect((source._readableState as any).pipes[0]).toBe(dest1);
|
|
348
|
-
expect((source._readableState as any).pipes[1]).toBe(dest2);
|
|
349
|
-
|
|
350
|
-
source.unpipe(dest2);
|
|
351
|
-
expect((source._readableState as any).pipes.length).toBe(1);
|
|
352
|
-
expect((source._readableState as any).pipes[0]).toBe(dest1);
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
await it('unpipe non-connected dest is a no-op', async () => {
|
|
356
|
-
const source = new Readable({ read() {} });
|
|
357
|
-
const dest1 = new Writable({ write(_c, _e, cb) { cb(); } });
|
|
358
|
-
const dest2 = new Writable({ write(_c, _e, cb) { cb(); } });
|
|
359
|
-
|
|
360
|
-
source.pipe(dest1);
|
|
361
|
-
source.unpipe(dest2); // dest2 was never piped
|
|
362
|
-
|
|
363
|
-
expect((source._readableState as any).pipes.length).toBe(1);
|
|
364
|
-
expect((source._readableState as any).pipes[0]).toBe(dest1);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
await it('unpipe() with no args removes all piped destinations', async () => {
|
|
368
|
-
const source = new Readable({ read() {} });
|
|
369
|
-
const dest1 = new Writable({ write(_c, _e, cb) { cb(); } });
|
|
370
|
-
const dest2 = new Writable({ write(_c, _e, cb) { cb(); } });
|
|
371
|
-
|
|
372
|
-
source.pipe(dest1);
|
|
373
|
-
source.pipe(dest2);
|
|
374
|
-
|
|
375
|
-
const unpiped: Writable[] = [];
|
|
376
|
-
dest1.on('unpipe', () => unpiped.push(dest1));
|
|
377
|
-
dest2.on('unpipe', () => unpiped.push(dest2));
|
|
378
|
-
|
|
379
|
-
source.unpipe();
|
|
380
|
-
|
|
381
|
-
expect((source._readableState as any).pipes.length).toBe(0);
|
|
382
|
-
expect(unpiped.length).toBe(2);
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
await it('unpipe fires unpipe event on destination', async () => {
|
|
386
|
-
const source = new Readable({ read() {} });
|
|
387
|
-
const dest = new Writable({ write(_c, _e, cb) { cb(); } });
|
|
388
|
-
|
|
389
|
-
let fired = false;
|
|
390
|
-
dest.on('unpipe', () => { fired = true; });
|
|
391
|
-
|
|
392
|
-
source.pipe(dest);
|
|
393
|
-
source.unpipe(dest);
|
|
394
|
-
|
|
395
|
-
expect(fired).toBe(true);
|
|
396
|
-
});
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
// -------------------------------------------------------------------------
|
|
400
|
-
// pipe: needDrain — pause while dest needs drain
|
|
401
|
-
// -------------------------------------------------------------------------
|
|
402
|
-
await describe('pipe: needDrain handling', async () => {
|
|
403
|
-
await it('pipe pauses source when dest writableNeedDrain is true', async () => {
|
|
404
|
-
const w = new Writable({
|
|
405
|
-
highWaterMark: 1,
|
|
406
|
-
write(_buf, _enc, callback) {
|
|
407
|
-
Promise.resolve().then(() => callback());
|
|
408
|
-
},
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
// Fill the writable past HWM so writableNeedDrain becomes true
|
|
412
|
-
while (w.write('x')) {/* fill */}
|
|
413
|
-
expect(w.writableNeedDrain).toBe(true);
|
|
414
|
-
|
|
415
|
-
const pauses: string[] = [];
|
|
416
|
-
const r = new Readable({
|
|
417
|
-
read() {
|
|
418
|
-
this.push('asd');
|
|
419
|
-
this.push(null);
|
|
420
|
-
},
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
r.on('pause', () => pauses.push('paused'));
|
|
424
|
-
await new Promise<void>((resolve) => {
|
|
425
|
-
r.on('end', resolve);
|
|
426
|
-
r.pipe(w);
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
// r must have been paused at least once due to backpressure
|
|
430
|
-
expect(pauses.length).toBeGreaterThan(0);
|
|
431
|
-
});
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
// -------------------------------------------------------------------------
|
|
435
|
-
// pipe: objectMode to non-objectMode error
|
|
436
|
-
// -------------------------------------------------------------------------
|
|
437
|
-
await describe('pipe: objectMode to non-objectMode', async () => {
|
|
438
|
-
await it('piping objects to non-objectMode transform emits an error', async () => {
|
|
439
|
-
const objectReadable = Readable.from([{ hello: 'hello' }, { world: 'world' }]);
|
|
440
|
-
const passThrough = new Transform({
|
|
441
|
-
transform(chunk, _enc, cb) {
|
|
442
|
-
this.push(chunk);
|
|
443
|
-
cb(null);
|
|
444
|
-
},
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
await new Promise<void>((resolve) => {
|
|
448
|
-
passThrough.on('error', (err: any) => {
|
|
449
|
-
// Should get ERR_INVALID_ARG_TYPE or similar
|
|
450
|
-
expect(err).toBeDefined();
|
|
451
|
-
resolve();
|
|
452
|
-
});
|
|
453
|
-
objectReadable.pipe(passThrough);
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
await it('writable that throws in _write propagates the error via error event', async () => {
|
|
458
|
-
const stringReadable = Readable.from(['hello', 'world']);
|
|
459
|
-
const passThrough = new Transform({
|
|
460
|
-
transform(_chunk, _enc, cb) {
|
|
461
|
-
this.push(_chunk);
|
|
462
|
-
throw new Error('something went wrong');
|
|
463
|
-
},
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
await new Promise<void>((resolve) => {
|
|
467
|
-
passThrough.on('error', (err) => {
|
|
468
|
-
expect(err.message).toBe('something went wrong');
|
|
469
|
-
resolve();
|
|
470
|
-
});
|
|
471
|
-
stringReadable.pipe(passThrough);
|
|
472
|
-
});
|
|
473
|
-
});
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
// -------------------------------------------------------------------------
|
|
477
|
-
// pipe: flow — basic data flow through pipes
|
|
478
|
-
// -------------------------------------------------------------------------
|
|
479
|
-
await describe('pipe: data flow', async () => {
|
|
480
|
-
await it('basic Readable → Writable pipe delivers all data', async () => {
|
|
481
|
-
const chunks: Buffer[] = [];
|
|
482
|
-
const r = new Readable({
|
|
483
|
-
read() {
|
|
484
|
-
this.push(Buffer.from('hello'));
|
|
485
|
-
this.push(null);
|
|
486
|
-
},
|
|
487
|
-
});
|
|
488
|
-
const w = new Writable({
|
|
489
|
-
write(chunk, _enc, cb) { chunks.push(chunk); cb(); },
|
|
490
|
-
});
|
|
491
|
-
await new Promise<void>((resolve) => {
|
|
492
|
-
w.on('finish', resolve);
|
|
493
|
-
r.pipe(w);
|
|
494
|
-
});
|
|
495
|
-
expect(Buffer.concat(chunks).toString()).toBe('hello');
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
await it('chained PassThrough pipes deliver all data', async () => {
|
|
499
|
-
const items = [1, 2, 3, 4, 5];
|
|
500
|
-
const r = Readable.from(items, { objectMode: true });
|
|
501
|
-
const out: number[] = [];
|
|
502
|
-
|
|
503
|
-
const pt1 = new PassThrough({ objectMode: true });
|
|
504
|
-
const pt2 = new PassThrough({ objectMode: true });
|
|
505
|
-
const w = new Writable({
|
|
506
|
-
objectMode: true,
|
|
507
|
-
write(chunk, _enc, cb) { out.push(chunk); cb(); },
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
await new Promise<void>((resolve) => {
|
|
511
|
-
w.on('finish', resolve);
|
|
512
|
-
r.pipe(pt1).pipe(pt2).pipe(w);
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
expect(out).toEqualArray(items);
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
await it('drain listener is not added when no backpressure', async () => {
|
|
519
|
-
const rs = new Readable({ read() {} });
|
|
520
|
-
const pt = rs.pipe(new PassThrough({ objectMode: true, highWaterMark: 2 }));
|
|
521
|
-
|
|
522
|
-
expect(pt.listenerCount('drain')).toBe(0);
|
|
523
|
-
rs.push('asd');
|
|
524
|
-
// Still no drain listener as we haven't exceeded HWM on dest
|
|
525
|
-
await new Promise<void>((resolve) => Promise.resolve().then(resolve));
|
|
526
|
-
expect(pt.listenerCount('drain')).toBe(0);
|
|
527
|
-
rs.push(null);
|
|
528
|
-
});
|
|
529
|
-
});
|
|
530
|
-
};
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
// Tests for stream/promises module
|
|
2
|
-
// Reference: Node.js lib/stream/promises.js
|
|
3
|
-
|
|
4
|
-
import { describe, it, expect } from '@gjsify/unit';
|
|
5
|
-
import { Readable, Writable, Transform, PassThrough } from 'node:stream';
|
|
6
|
-
import { pipeline, finished } from 'node:stream/promises';
|
|
7
|
-
|
|
8
|
-
export default async () => {
|
|
9
|
-
await describe('stream/promises', async () => {
|
|
10
|
-
|
|
11
|
-
// ==================== pipeline() ====================
|
|
12
|
-
await describe('pipeline', async () => {
|
|
13
|
-
await it('should pipe readable to writable', async () => {
|
|
14
|
-
const chunks: string[] = [];
|
|
15
|
-
const source = Readable.from(['hello', ' ', 'pipeline']);
|
|
16
|
-
const sink = new Writable({
|
|
17
|
-
write(chunk, _enc, cb) {
|
|
18
|
-
chunks.push(chunk.toString());
|
|
19
|
-
cb();
|
|
20
|
-
},
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
await pipeline(source, sink);
|
|
24
|
-
expect(chunks.join('')).toBe('hello pipeline');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
await it('should pipe through a transform', async () => {
|
|
28
|
-
const source = Readable.from(['hello']);
|
|
29
|
-
const transform = new Transform({
|
|
30
|
-
transform(chunk, _enc, cb) {
|
|
31
|
-
cb(null, chunk.toString().toUpperCase());
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
const chunks: string[] = [];
|
|
35
|
-
const sink = new Writable({
|
|
36
|
-
write(chunk, _enc, cb) {
|
|
37
|
-
chunks.push(chunk.toString());
|
|
38
|
-
cb();
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
await pipeline(source, transform, sink);
|
|
43
|
-
expect(chunks.join('')).toBe('HELLO');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
await it('should reject on source error', async () => {
|
|
47
|
-
const source = new Readable({
|
|
48
|
-
read() {
|
|
49
|
-
this.destroy(new Error('source error'));
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
const sink = new Writable({
|
|
53
|
-
write(_chunk, _enc, cb) { cb(); },
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
let caught = false;
|
|
57
|
-
try {
|
|
58
|
-
await pipeline(source, sink);
|
|
59
|
-
} catch (err: any) {
|
|
60
|
-
caught = true;
|
|
61
|
-
expect(err.message).toBe('source error');
|
|
62
|
-
}
|
|
63
|
-
expect(caught).toBe(true);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
await it('should pipe through PassThrough', async () => {
|
|
67
|
-
const source = Readable.from(['pass', 'through']);
|
|
68
|
-
const pt = new PassThrough();
|
|
69
|
-
const chunks: string[] = [];
|
|
70
|
-
const sink = new Writable({
|
|
71
|
-
write(chunk, _enc, cb) {
|
|
72
|
-
chunks.push(chunk.toString());
|
|
73
|
-
cb();
|
|
74
|
-
},
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
await pipeline(source, pt, sink);
|
|
78
|
-
expect(chunks.join('')).toBe('passthrough');
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// ==================== finished() ====================
|
|
83
|
-
await describe('finished', async () => {
|
|
84
|
-
await it('should resolve when writable finishes', async () => {
|
|
85
|
-
const writable = new Writable({
|
|
86
|
-
write(_chunk, _enc, cb) { cb(); },
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const p = finished(writable);
|
|
90
|
-
writable.end('done');
|
|
91
|
-
await p;
|
|
92
|
-
// If we get here, it resolved successfully
|
|
93
|
-
expect(true).toBe(true);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
await it('should resolve when readable ends', async () => {
|
|
97
|
-
const readable = Readable.from(['data']);
|
|
98
|
-
|
|
99
|
-
// Consume the stream
|
|
100
|
-
readable.resume();
|
|
101
|
-
await finished(readable);
|
|
102
|
-
expect(true).toBe(true);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
await it('should reject on stream error', async () => {
|
|
106
|
-
const readable = new Readable({
|
|
107
|
-
read() {
|
|
108
|
-
this.destroy(new Error('stream error'));
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
let caught = false;
|
|
113
|
-
try {
|
|
114
|
-
readable.resume();
|
|
115
|
-
await finished(readable);
|
|
116
|
-
} catch (err: any) {
|
|
117
|
-
caught = true;
|
|
118
|
-
expect(err.message).toBe('stream error');
|
|
119
|
-
}
|
|
120
|
-
expect(caught).toBe(true);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
await it('should resolve for already destroyed stream', async () => {
|
|
124
|
-
const writable = new Writable({
|
|
125
|
-
write(_chunk, _enc, cb) { cb(); },
|
|
126
|
-
});
|
|
127
|
-
writable.end();
|
|
128
|
-
|
|
129
|
-
// Wait for finish event
|
|
130
|
-
await new Promise<void>((resolve) => {
|
|
131
|
-
writable.on('finish', resolve);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
// finished should resolve immediately or quickly for already-finished streams
|
|
135
|
-
await finished(writable);
|
|
136
|
-
expect(true).toBe(true);
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
};
|
package/src/promises/index.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// stream/promises — Promise-based stream utilities
|
|
2
|
-
|
|
3
|
-
import { pipeline as _pipeline, finished as _finished } from '../index.js';
|
|
4
|
-
import type { Stream, Readable, Writable } from '../index.js';
|
|
5
|
-
|
|
6
|
-
export function pipeline(...streams: any[]): Promise<void> {
|
|
7
|
-
return new Promise((resolve, reject) => {
|
|
8
|
-
_pipeline(...streams, (err: Error | null) => {
|
|
9
|
-
if (err) reject(err);
|
|
10
|
-
else resolve();
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function finished(stream: Stream | Readable | Writable, opts?: any): Promise<void> {
|
|
16
|
-
return new Promise((resolve, reject) => {
|
|
17
|
-
if (opts && typeof opts !== 'function') {
|
|
18
|
-
_finished(stream, opts, (err?: Error | null) => {
|
|
19
|
-
if (err) reject(err);
|
|
20
|
-
else resolve();
|
|
21
|
-
});
|
|
22
|
-
} else {
|
|
23
|
-
_finished(stream, (err?: Error | null) => {
|
|
24
|
-
if (err) reject(err);
|
|
25
|
-
else resolve();
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export default { pipeline, finished };
|