@gjsify/stream 0.4.0 → 0.4.4
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/utils/finished.ts
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
// finished + addAbortSignal + is* helpers.
|
|
2
|
-
//
|
|
3
|
-
// Reference: refs/node/lib/internal/streams/end-of-stream.js
|
|
4
|
-
// refs/node/lib/internal/streams/utils.js
|
|
5
|
-
// Reimplemented for GJS using @gjsify/utils microtask scheduling.
|
|
6
|
-
|
|
7
|
-
import { queueMicrotask } from '@gjsify/utils';
|
|
8
|
-
import type { FinishedOptions } from 'node:stream';
|
|
9
|
-
|
|
10
|
-
import { Stream_ } from '../stream-base.js';
|
|
11
|
-
import type { Readable_ } from '../readable.js';
|
|
12
|
-
import type { Writable_ } from '../writable.js';
|
|
13
|
-
|
|
14
|
-
type AnyStream = Stream_ | Readable_ | Writable_;
|
|
15
|
-
|
|
16
|
-
export function finished(stream: AnyStream, callback: (err?: Error | null) => void): () => void;
|
|
17
|
-
export function finished(stream: AnyStream, opts: FinishedOptions, callback: (err?: Error | null) => void): () => void;
|
|
18
|
-
export function finished(stream: AnyStream, optsOrCb: FinishedOptions | ((err?: Error | null) => void), callback?: (err?: Error | null) => void): () => void {
|
|
19
|
-
let cb: (err?: Error | null) => void;
|
|
20
|
-
|
|
21
|
-
if (typeof optsOrCb === 'function') {
|
|
22
|
-
cb = optsOrCb;
|
|
23
|
-
} else {
|
|
24
|
-
// opts not currently consumed — Node uses it for error/readable/writable filters.
|
|
25
|
-
cb = callback!;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
let called = false;
|
|
29
|
-
function done(err?: Error | null) {
|
|
30
|
-
if (!called) {
|
|
31
|
-
called = true;
|
|
32
|
-
cb(err);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const onFinish = () => done();
|
|
37
|
-
const onEnd = () => done();
|
|
38
|
-
const onError = (err: Error) => done(err);
|
|
39
|
-
const onClose = () => {
|
|
40
|
-
const w = stream as Writable_;
|
|
41
|
-
const r = stream as Readable_;
|
|
42
|
-
if (!w.writableFinished && !r.readableEnded) {
|
|
43
|
-
done(new Error('premature close'));
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
stream.on('finish', onFinish);
|
|
48
|
-
stream.on('end', onEnd);
|
|
49
|
-
stream.on('error', onError);
|
|
50
|
-
stream.on('close', onClose);
|
|
51
|
-
|
|
52
|
-
// Check initial state — handle already-finished/destroyed streams
|
|
53
|
-
// Reference: refs/node/lib/internal/streams/end-of-stream.js lines 228-249
|
|
54
|
-
const s = stream as unknown as Record<string, unknown>;
|
|
55
|
-
const isWritableStream = typeof (stream as Writable_).write === 'function';
|
|
56
|
-
const isReadableStream = typeof (stream as Readable_).read === 'function';
|
|
57
|
-
const writableFinished = s.writableFinished === true;
|
|
58
|
-
const readableEnded = s.readableEnded === true;
|
|
59
|
-
const destroyed = s.destroyed === true;
|
|
60
|
-
|
|
61
|
-
if (destroyed) {
|
|
62
|
-
const storedErr = s._err as Error | null | undefined;
|
|
63
|
-
if (storedErr) {
|
|
64
|
-
// Stream was destroyed with an error (may have fired before we registered listener)
|
|
65
|
-
queueMicrotask(() => done(storedErr));
|
|
66
|
-
} else if ((isWritableStream && writableFinished) || (isReadableStream && readableEnded)) {
|
|
67
|
-
// Stream was destroyed after completing normally — treat as success
|
|
68
|
-
queueMicrotask(() => done());
|
|
69
|
-
} else {
|
|
70
|
-
// Stream was destroyed without completing — premature close
|
|
71
|
-
queueMicrotask(() => done(new Error('premature close')));
|
|
72
|
-
}
|
|
73
|
-
} else if (isWritableStream && !isReadableStream && writableFinished) {
|
|
74
|
-
queueMicrotask(() => done());
|
|
75
|
-
} else if (!isWritableStream && isReadableStream && readableEnded) {
|
|
76
|
-
queueMicrotask(() => done());
|
|
77
|
-
} else if (isWritableStream && isReadableStream && writableFinished && readableEnded) {
|
|
78
|
-
queueMicrotask(() => done());
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return function cleanup() {
|
|
82
|
-
stream.removeListener('finish', onFinish);
|
|
83
|
-
stream.removeListener('end', onEnd);
|
|
84
|
-
stream.removeListener('error', onError);
|
|
85
|
-
stream.removeListener('close', onClose);
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function addAbortSignal<T extends AnyStream>(signal: AbortSignal, stream: T): T {
|
|
90
|
-
if (!(signal instanceof AbortSignal)) {
|
|
91
|
-
throw new TypeError('The first argument must be an AbortSignal');
|
|
92
|
-
}
|
|
93
|
-
if (!(stream instanceof Stream_)) {
|
|
94
|
-
throw new TypeError('The second argument must be a Stream');
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const destroyable = stream as Readable_ | Writable_;
|
|
98
|
-
|
|
99
|
-
if (signal.aborted) {
|
|
100
|
-
destroyable.destroy(new Error('The operation was aborted'));
|
|
101
|
-
} else {
|
|
102
|
-
const onAbort = () => {
|
|
103
|
-
destroyable.destroy(new Error('The operation was aborted'));
|
|
104
|
-
};
|
|
105
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
106
|
-
// Cleanup when stream closes
|
|
107
|
-
stream.once('close', () => {
|
|
108
|
-
signal.removeEventListener('abort', onAbort);
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return stream;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ---- Utility functions ----
|
|
116
|
-
|
|
117
|
-
export function isReadable(stream: unknown): boolean {
|
|
118
|
-
if (stream == null) return false;
|
|
119
|
-
const s = stream as Record<string, unknown>;
|
|
120
|
-
if (typeof s.readable !== 'boolean') return false;
|
|
121
|
-
if (typeof s.read !== 'function') return false;
|
|
122
|
-
if (s.destroyed === true) return false;
|
|
123
|
-
if (s.readableEnded === true) return false;
|
|
124
|
-
return s.readable === true;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function isWritable(stream: unknown): boolean {
|
|
128
|
-
if (stream == null) return false;
|
|
129
|
-
const s = stream as Record<string, unknown>;
|
|
130
|
-
if (typeof s.writable !== 'boolean') return false;
|
|
131
|
-
if (typeof s.write !== 'function') return false;
|
|
132
|
-
if (s.destroyed === true) return false;
|
|
133
|
-
if (s.writableEnded === true) return false;
|
|
134
|
-
return s.writable === true;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function isDestroyed(stream: unknown): boolean {
|
|
138
|
-
if (stream == null) return false;
|
|
139
|
-
return (stream as Record<string, unknown>).destroyed === true;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export function isDisturbed(stream: unknown): boolean {
|
|
143
|
-
if (stream == null) return false;
|
|
144
|
-
const s = stream as Record<string, unknown>;
|
|
145
|
-
// A stream is disturbed if data has been read from it
|
|
146
|
-
return s.readableDidRead === true || (s.readableFlowing !== null && s.readableFlowing !== undefined);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function isErrored(stream: unknown): boolean {
|
|
150
|
-
if (stream == null) return false;
|
|
151
|
-
// Check for errored state on either side
|
|
152
|
-
const s = stream as Record<string, unknown>;
|
|
153
|
-
if (s.destroyed === true && typeof s.readable === 'boolean' && s.readable === false) return true;
|
|
154
|
-
if (s.destroyed === true && typeof s.writable === 'boolean' && s.writable === false) return true;
|
|
155
|
-
return false;
|
|
156
|
-
}
|
package/src/utils/pipe.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
// Free-function pipe helper used by Stream_.pipe.
|
|
2
|
-
//
|
|
3
|
-
// Reference: refs/node/lib/internal/streams/legacy.js Stream.prototype.pipe.
|
|
4
|
-
// Reimplemented for GJS — extracted so stream-base.ts (Stream_) can stay
|
|
5
|
-
// dependency-free of the per-class files. The Readable_ instance check is
|
|
6
|
-
// resolved lazily at call time, breaking what would otherwise be a top-level
|
|
7
|
-
// import cycle (stream-base → pipe → readable → stream-base).
|
|
8
|
-
|
|
9
|
-
import { _setPipeImpl, type Stream_ } from '../stream-base.js';
|
|
10
|
-
import { Readable_ } from '../readable.js';
|
|
11
|
-
import type { Writable_ } from '../writable.js';
|
|
12
|
-
import type { StreamLike } from '../internal/types.js';
|
|
13
|
-
|
|
14
|
-
/** Tracked pipe destination for unpipe support. */
|
|
15
|
-
export interface PipeState {
|
|
16
|
-
dest: Writable_;
|
|
17
|
-
cleanup: () => void;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function pipe<T extends Writable_>(
|
|
21
|
-
sourceStream: Stream_,
|
|
22
|
-
destination: T,
|
|
23
|
-
options?: { end?: boolean },
|
|
24
|
-
): T {
|
|
25
|
-
// The source is conceptually Readable-shaped; the legacy Stream signature
|
|
26
|
-
// allows any EventEmitter that emits 'data'/'end'/'close', so we keep the
|
|
27
|
-
// structural cast and only branch on the modern Readable_ check below.
|
|
28
|
-
const source = sourceStream as unknown as Readable_;
|
|
29
|
-
const doEnd = options?.end !== false;
|
|
30
|
-
|
|
31
|
-
// Drain listener is added lazily only when backpressure occurs.
|
|
32
|
-
let drainListenerAdded = false;
|
|
33
|
-
const ondrain = () => {
|
|
34
|
-
drainListenerAdded = false;
|
|
35
|
-
destination.removeListener('drain', ondrain);
|
|
36
|
-
const s = source as StreamLike;
|
|
37
|
-
if (typeof s.resume === 'function') {
|
|
38
|
-
s.resume();
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const ondata = (chunk: unknown) => {
|
|
43
|
-
if (destination.writable) {
|
|
44
|
-
const s = source as StreamLike;
|
|
45
|
-
if (destination.write(chunk) === false && typeof s.pause === 'function') {
|
|
46
|
-
s.pause();
|
|
47
|
-
if (!drainListenerAdded) {
|
|
48
|
-
drainListenerAdded = true;
|
|
49
|
-
destination.on('drain', ondrain);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
source.on('data', ondata);
|
|
56
|
-
|
|
57
|
-
let didEnd = false;
|
|
58
|
-
|
|
59
|
-
const onend = () => {
|
|
60
|
-
if (didEnd) return;
|
|
61
|
-
didEnd = true;
|
|
62
|
-
if (doEnd) destination.end();
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const onclose = () => {
|
|
66
|
-
if (didEnd) return;
|
|
67
|
-
didEnd = true;
|
|
68
|
-
if (doEnd) {
|
|
69
|
-
// Modern Readable streams (Readable_) do NOT destroy dest on source close —
|
|
70
|
-
// only call dest.end/destroy for legacy Stream objects (no Readable_ prototype).
|
|
71
|
-
if (!(source instanceof Readable_)) {
|
|
72
|
-
const d = destination as unknown as { destroy?: () => void };
|
|
73
|
-
if (typeof d.destroy === 'function') {
|
|
74
|
-
d.destroy();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
if (doEnd) {
|
|
81
|
-
source.on('end', onend);
|
|
82
|
-
source.on('close', onclose);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const cleanup = () => {
|
|
86
|
-
source.removeListener('data', ondata);
|
|
87
|
-
if (drainListenerAdded) destination.removeListener('drain', ondrain);
|
|
88
|
-
source.removeListener('end', onend);
|
|
89
|
-
source.removeListener('close', onclose);
|
|
90
|
-
// Self-remove from both end and close
|
|
91
|
-
source.removeListener('end', cleanup);
|
|
92
|
-
source.removeListener('close', cleanup);
|
|
93
|
-
destination.removeListener('close', cleanup);
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
source.on('end', cleanup);
|
|
97
|
-
source.on('close', cleanup);
|
|
98
|
-
destination.on('close', cleanup);
|
|
99
|
-
|
|
100
|
-
// Track piped destinations for unpipe
|
|
101
|
-
if (source instanceof Readable_) {
|
|
102
|
-
source._pipeDests.push({ dest: destination, cleanup });
|
|
103
|
-
source._readableState.pipes.push(destination);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
destination.emit('pipe', source);
|
|
107
|
-
return destination;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Wire the implementation back into Stream_.prototype.pipe — see _setPipeImpl
|
|
111
|
-
// in ../stream-base.ts for why this is done at module-load time rather than
|
|
112
|
-
// via a static top-level import.
|
|
113
|
-
_setPipeImpl(pipe);
|
package/src/utils/pipeline.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
// pipeline — chain streams and propagate destroy on error.
|
|
2
|
-
//
|
|
3
|
-
// Reference: refs/node/lib/internal/streams/pipeline.js
|
|
4
|
-
// Reimplemented for GJS — minimal, callback-based variant. The promise form
|
|
5
|
-
// lives under `@gjsify/stream/promises` and wraps this.
|
|
6
|
-
|
|
7
|
-
import type { Stream_ } from '../stream-base.js';
|
|
8
|
-
import type { Writable_ } from '../writable.js';
|
|
9
|
-
|
|
10
|
-
export type PipelineCallback = (err: Error | null) => void;
|
|
11
|
-
|
|
12
|
-
/** A stream that can be destroyed (duck-typed for pipeline). */
|
|
13
|
-
export interface DestroyableStream extends Stream_ {
|
|
14
|
-
destroy?(error?: Error): void;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function pipeline(...args: [...streams: DestroyableStream[], callback: PipelineCallback] | DestroyableStream[]): DestroyableStream {
|
|
18
|
-
const argList = args as Array<DestroyableStream | PipelineCallback>;
|
|
19
|
-
const last = argList[argList.length - 1];
|
|
20
|
-
const callback = typeof last === 'function' ? (argList.pop() as PipelineCallback) : undefined;
|
|
21
|
-
const streams = argList as DestroyableStream[];
|
|
22
|
-
|
|
23
|
-
if (streams.length < 2) {
|
|
24
|
-
throw new Error('pipeline requires at least 2 streams');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let error: Error | null = null;
|
|
28
|
-
|
|
29
|
-
function onError(err: Error) {
|
|
30
|
-
if (!error) {
|
|
31
|
-
error = err;
|
|
32
|
-
// Destroy all streams
|
|
33
|
-
for (const stream of streams) {
|
|
34
|
-
if (typeof stream.destroy === 'function') {
|
|
35
|
-
stream.destroy();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if (callback) callback(err);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Pipe streams together
|
|
43
|
-
let current: Stream_ = streams[0];
|
|
44
|
-
for (let i = 1; i < streams.length; i++) {
|
|
45
|
-
const next = streams[i];
|
|
46
|
-
current.pipe(next as unknown as Writable_);
|
|
47
|
-
current.on('error', onError);
|
|
48
|
-
current = next;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Listen for end on last stream
|
|
52
|
-
const lastStream = streams[streams.length - 1];
|
|
53
|
-
lastStream.on('error', onError);
|
|
54
|
-
lastStream.on('finish', () => {
|
|
55
|
-
if (callback && !error) callback(null);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
return lastStream;
|
|
59
|
-
}
|
package/src/web/index.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// stream/web — Re-export WHATWG Streams API
|
|
2
|
-
// Uses @gjsify/web-streams polyfill (provides native on Node.js, polyfill on GJS)
|
|
3
|
-
|
|
4
|
-
export {
|
|
5
|
-
ReadableStream,
|
|
6
|
-
WritableStream,
|
|
7
|
-
TransformStream,
|
|
8
|
-
ByteLengthQueuingStrategy,
|
|
9
|
-
CountQueuingStrategy,
|
|
10
|
-
TextEncoderStream,
|
|
11
|
-
TextDecoderStream,
|
|
12
|
-
} from '@gjsify/web-streams';
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
ReadableStream,
|
|
16
|
-
WritableStream,
|
|
17
|
-
TransformStream,
|
|
18
|
-
ByteLengthQueuingStrategy,
|
|
19
|
-
CountQueuingStrategy,
|
|
20
|
-
TextEncoderStream,
|
|
21
|
-
TextDecoderStream,
|
|
22
|
-
} from '@gjsify/web-streams';
|
|
23
|
-
|
|
24
|
-
export default {
|
|
25
|
-
ReadableStream,
|
|
26
|
-
WritableStream,
|
|
27
|
-
TransformStream,
|
|
28
|
-
ByteLengthQueuingStrategy,
|
|
29
|
-
CountQueuingStrategy,
|
|
30
|
-
TextEncoderStream,
|
|
31
|
-
TextDecoderStream,
|
|
32
|
-
} as Record<string, any>;
|