@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 CHANGED
@@ -1,56 +1,60 @@
1
1
  {
2
- "name": "@gjsify/stream",
3
- "version": "0.4.0",
4
- "description": "Node.js stream module for Gjs",
5
- "type": "module",
6
- "module": "lib/esm/index.js",
7
- "types": "lib/types/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./lib/types/index.d.ts",
11
- "require": "./cjs-compat.cjs",
12
- "default": "./lib/esm/index.js"
2
+ "name": "@gjsify/stream",
3
+ "version": "0.4.4",
4
+ "description": "Node.js stream module for Gjs",
5
+ "type": "module",
6
+ "module": "lib/esm/index.js",
7
+ "types": "lib/types/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/types/index.d.ts",
11
+ "require": "./cjs-compat.cjs",
12
+ "default": "./lib/esm/index.js"
13
+ },
14
+ "./consumers": {
15
+ "types": "./lib/types/consumers/index.d.ts",
16
+ "default": "./lib/esm/consumers/index.js"
17
+ },
18
+ "./promises": {
19
+ "types": "./lib/types/promises/index.d.ts",
20
+ "default": "./lib/esm/promises/index.js"
21
+ },
22
+ "./web": {
23
+ "types": "./lib/types/web/index.d.ts",
24
+ "default": "./lib/esm/web/index.js"
25
+ }
13
26
  },
14
- "./consumers": {
15
- "types": "./lib/types/consumers/index.d.ts",
16
- "default": "./lib/esm/consumers/index.js"
27
+ "files": [
28
+ "lib",
29
+ "cjs-compat.cjs"
30
+ ],
31
+ "scripts": {
32
+ "clear": "rm -rf lib tsconfig.tsbuildinfo tsconfig.types.tsbuildinfo test.gjs.mjs test.node.mjs || exit 0",
33
+ "check": "tsc --noEmit",
34
+ "build": "gjsify run build:gjsify && gjsify run build:types",
35
+ "build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
36
+ "build:types": "tsc",
37
+ "build:test": "gjsify run build:test:gjs && gjsify run build:test:node",
38
+ "build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
39
+ "build:test:node": "gjsify build src/test.mts --app node --outfile test.node.mjs",
40
+ "test": "gjsify run build:gjsify && gjsify run build:test && gjsify run test:node && gjsify run test:gjs",
41
+ "test:gjs": "gjsify run test.gjs.mjs",
42
+ "test:node": "node test.node.mjs"
17
43
  },
18
- "./promises": {
19
- "types": "./lib/types/promises/index.d.ts",
20
- "default": "./lib/esm/promises/index.js"
44
+ "keywords": [
45
+ "gjs",
46
+ "node",
47
+ "stream"
48
+ ],
49
+ "devDependencies": {
50
+ "@gjsify/cli": "^0.4.4",
51
+ "@gjsify/unit": "^0.4.4",
52
+ "@types/node": "^25.6.2",
53
+ "typescript": "^6.0.3"
21
54
  },
22
- "./web": {
23
- "types": "./lib/types/web/index.d.ts",
24
- "default": "./lib/esm/web/index.js"
55
+ "dependencies": {
56
+ "@gjsify/events": "^0.4.4",
57
+ "@gjsify/utils": "^0.4.4",
58
+ "@gjsify/web-streams": "^0.4.4"
25
59
  }
26
- },
27
- "scripts": {
28
- "clear": "rm -rf lib tsconfig.tsbuildinfo tsconfig.types.tsbuildinfo test.gjs.mjs test.node.mjs || exit 0",
29
- "check": "tsc --noEmit",
30
- "build": "yarn build:gjsify && yarn build:types",
31
- "build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
32
- "build:types": "tsc",
33
- "build:test": "yarn build:test:gjs && yarn build:test:node",
34
- "build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
35
- "build:test:node": "gjsify build src/test.mts --app node --outfile test.node.mjs",
36
- "test": "yarn build:gjsify && yarn build:test && yarn test:node && yarn test:gjs",
37
- "test:gjs": "gjsify run test.gjs.mjs",
38
- "test:node": "node test.node.mjs"
39
- },
40
- "keywords": [
41
- "gjs",
42
- "node",
43
- "stream"
44
- ],
45
- "devDependencies": {
46
- "@gjsify/cli": "^0.4.0",
47
- "@gjsify/unit": "^0.4.0",
48
- "@types/node": "^25.6.2",
49
- "typescript": "^6.0.3"
50
- },
51
- "dependencies": {
52
- "@gjsify/events": "^0.4.0",
53
- "@gjsify/utils": "^0.4.0",
54
- "@gjsify/web-streams": "^0.4.0"
55
- }
56
- }
60
+ }
@@ -1,196 +0,0 @@
1
- // Legacy `.call(this)` + `util.inherits(Sub, Parent)` compatibility tests.
2
- //
3
- // Ported from refs/node-test/parallel/test-util-inherits.js
4
- // Original: MIT license, Node.js contributors
5
- // Modifications: adapted to @gjsify/unit and extended to cover all six stream
6
- // classes that are wrapped by `makeCallable` in `./callable.ts`.
7
- //
8
- // Regression coverage for the npm `send` (express.static) bug and for our own
9
- // `@gjsify/crypto` `Hash.copy()` path which both do `Stream.call(this)` /
10
- // `Transform.call(copy)` on pre-ES2015 CJS-style constructors.
11
-
12
- import { describe, it, expect } from '@gjsify/unit';
13
- import { Stream, Readable, Writable, Duplex, Transform, PassThrough } from 'node:stream';
14
- import { inherits } from 'node:util';
15
-
16
- export default async () => {
17
- await describe('makeCallable: new invocation still works', async () => {
18
- await it('new Stream() returns a valid instance', async () => {
19
- const s = new Stream();
20
- expect(typeof (s as any).pipe).toBe('function');
21
- expect(s instanceof Stream).toBe(true);
22
- });
23
-
24
- await it('new Readable() initialises readable state', async () => {
25
- const r = new Readable({ read() {} });
26
- expect(r.readable).toBe(true);
27
- expect(r instanceof Readable).toBe(true);
28
- expect(r instanceof Stream).toBe(true);
29
- });
30
-
31
- await it('new Transform() initialises both readable + writable state', async () => {
32
- const t = new Transform({ transform(chunk, _enc, cb) { cb(null, chunk); } });
33
- expect(t.readable).toBe(true);
34
- expect(t.writable).toBe(true);
35
- expect(t instanceof Transform).toBe(true);
36
- expect(t instanceof Duplex).toBe(true);
37
- expect(t instanceof Readable).toBe(true);
38
- });
39
-
40
- await it('Stream.prototype is accessible via the wrapper', async () => {
41
- expect(typeof (Stream as any).prototype).toBe('object');
42
- expect(typeof (Stream as any).prototype.pipe).toBe('function');
43
- });
44
- });
45
-
46
- await describe('makeCallable: no-new invocation (legacy stream constructors)', async () => {
47
- // Regression: Node's stream constructors guard against missing-new
48
- // with `if (!(this instanceof Cls)) return new Cls(...)`. Consumers
49
- // like `merge2` (used by `fast-glob`) call `Stream.PassThrough(opts)`
50
- // without `new` and rely on the result being a real instance. The
51
- // makeCallable apply trap must auto-construct in that case rather
52
- // than try to mutate `undefined`/`globalThis`.
53
- await it('PassThrough(opts) without new returns an instance', async () => {
54
- const pt: any = (PassThrough as any)({});
55
- expect(pt instanceof PassThrough).toBe(true);
56
- expect(pt instanceof Transform).toBe(true);
57
- expect(typeof pt.pipe).toBe('function');
58
- });
59
-
60
- await it('Readable({read}) without new returns an instance', async () => {
61
- const r: any = (Readable as any)({ read() {} });
62
- expect(r instanceof Readable).toBe(true);
63
- expect(r.readable).toBe(true);
64
- });
65
-
66
- await it('Writable({write}) without new returns an instance', async () => {
67
- const w: any = (Writable as any)({ write(_c: any, _e: any, cb: any) { cb(); } });
68
- expect(w instanceof Writable).toBe(true);
69
- expect(w.writable).toBe(true);
70
- });
71
-
72
- await it('PassThrough() pipes data through a no-new instance', async () => {
73
- const pt: any = (PassThrough as any)();
74
- const chunks: Buffer[] = [];
75
- await new Promise<void>((resolve, reject) => {
76
- pt.on('data', (c: Buffer) => chunks.push(c));
77
- pt.on('end', resolve);
78
- pt.on('error', reject);
79
- pt.end(Buffer.from('hello'));
80
- });
81
- expect(Buffer.concat(chunks).toString()).toBe('hello');
82
- });
83
- });
84
-
85
- await describe('makeCallable: legacy Stream.call(this) + util.inherits', async () => {
86
- await it('Stream.call(plainObject) assigns EventEmitter bookkeeping', async () => {
87
- // Plain object promoted to a Stream via .call()
88
- const plain: any = Object.create(Stream.prototype);
89
- (Stream as any).call(plain);
90
- // EventEmitter state is now present
91
- plain.on('ping', function(this: any, msg: string) { this.received = msg; });
92
- plain.emit('ping', 'hi');
93
- expect(plain.received).toBe('hi');
94
- });
95
-
96
- await it('legacy subclass pattern (function + inherits + .call) works', async () => {
97
- function SubStream(this: any) {
98
- (Stream as any).call(this);
99
- this.foo = 42;
100
- }
101
- inherits(SubStream as any, Stream as any);
102
- (SubStream.prototype as any).greet = function() { return 'hello'; };
103
-
104
- const s: any = new (SubStream as any)();
105
- expect(s.foo).toBe(42);
106
- expect(s.greet()).toBe('hello');
107
- // Inherited from Stream.prototype
108
- expect(typeof s.pipe).toBe('function');
109
- expect(s instanceof Stream).toBe(true);
110
- });
111
-
112
- await it('multi-level inherits (A.call -> B.call -> C) works', async () => {
113
- function A(this: any) {
114
- (Stream as any).call(this);
115
- this._a = 'a';
116
- }
117
- inherits(A as any, Stream as any);
118
- (A.prototype as any).a = function() { return this._a; };
119
-
120
- function B(this: any) {
121
- (A as any).call(this);
122
- this._b = 'b';
123
- }
124
- inherits(B as any, A as any);
125
- (B.prototype as any).b = function() { return this._b; };
126
-
127
- const b: any = new (B as any)();
128
- expect(b.a()).toBe('a');
129
- expect(b.b()).toBe('b');
130
- expect(b instanceof A).toBe(true);
131
- expect(b instanceof Stream).toBe(true);
132
- });
133
- });
134
-
135
- await describe('makeCallable: Readable / Writable / Transform .call(this)', async () => {
136
- await it('Readable.call(plain) sets up readable state', async () => {
137
- const r: any = Object.create(Readable.prototype);
138
- (Readable as any).call(r, { read() {} });
139
- expect(r.readable).toBe(true);
140
- expect(typeof r.readableHighWaterMark).toBe('number');
141
- // pipe is inherited from Stream.prototype through Readable.prototype
142
- expect(typeof r.pipe).toBe('function');
143
- });
144
-
145
- await it('Writable.call(plain) sets up writable state', async () => {
146
- const w: any = Object.create(Writable.prototype);
147
- (Writable as any).call(w, { write(_c: any, _e: any, cb: any) { cb(); } });
148
- expect(w.writable).toBe(true);
149
- expect(typeof w.writableHighWaterMark).toBe('number');
150
- });
151
-
152
- await it('Transform.call(plain) sets up both readable and writable state', async () => {
153
- // This is the exact pattern used by @gjsify/crypto Hash.copy():
154
- // const copy = Object.create(Hash.prototype);
155
- // Transform.call(copy);
156
- const copy: any = Object.create(Transform.prototype);
157
- (Transform as any).call(copy);
158
- expect(copy.readable).toBe(true);
159
- expect(copy.writable).toBe(true);
160
- // The Transform should be usable after .call() initialization —
161
- // field initializers from all ancestors ran on copy.
162
- expect(typeof copy.readableHighWaterMark).toBe('number');
163
- expect(typeof copy.writableHighWaterMark).toBe('number');
164
- });
165
-
166
- await it('PassThrough.call(plain) inherits the full chain', async () => {
167
- const pt: any = Object.create(PassThrough.prototype);
168
- (PassThrough as any).call(pt);
169
- expect(pt.readable).toBe(true);
170
- expect(pt.writable).toBe(true);
171
- });
172
- });
173
-
174
- await describe('makeCallable: integration with util.inherits', async () => {
175
- await it('inherits(Sub, Readable) + Readable.call(this) yields a working Readable subclass', async () => {
176
- function SimpleSrc(this: any, items: number[]) {
177
- (Readable as any).call(this, { objectMode: true });
178
- this._items = items;
179
- }
180
- inherits(SimpleSrc as any, Readable as any);
181
- (SimpleSrc.prototype as any)._read = function() {
182
- const item = this._items.shift();
183
- this.push(item !== undefined ? item : null);
184
- };
185
-
186
- const src: any = new (SimpleSrc as any)([1, 2, 3]);
187
- const out: number[] = [];
188
- await new Promise<void>((resolve, reject) => {
189
- src.on('data', (chunk: number) => out.push(chunk));
190
- src.on('end', resolve);
191
- src.on('error', reject);
192
- });
193
- expect(out).toEqualArray([1, 2, 3]);
194
- });
195
- });
196
- };
package/src/callable.ts DELETED
@@ -1,3 +0,0 @@
1
- // Re-exported from @gjsify/utils — second consumer (events) triggered promotion.
2
- // See packages/gjs/utils/src/callable.ts for implementation and comments.
3
- export { makeCallable } from '@gjsify/utils';
@@ -1,107 +0,0 @@
1
- // Tests for stream/consumers module
2
- // Reference: Node.js lib/stream/consumers.js
3
-
4
- import { describe, it, expect } from '@gjsify/unit';
5
- import { Readable } from 'node:stream';
6
- import { text, json, buffer, arrayBuffer, blob } from 'node:stream/consumers';
7
-
8
- export default async () => {
9
- await describe('stream/consumers', async () => {
10
-
11
- // ==================== text() ====================
12
- await describe('text', async () => {
13
- await it('should consume stream as text', async () => {
14
- const stream = Readable.from(['Hello', ' ', 'World']);
15
- const result = await text(stream);
16
- expect(result).toBe('Hello World');
17
- });
18
-
19
- await it('should handle empty stream', async () => {
20
- const stream = Readable.from([]);
21
- const result = await text(stream);
22
- expect(result).toBe('');
23
- });
24
-
25
- await it('should handle single chunk', async () => {
26
- const stream = Readable.from(['single']);
27
- const result = await text(stream);
28
- expect(result).toBe('single');
29
- });
30
- });
31
-
32
- // ==================== json() ====================
33
- await describe('json', async () => {
34
- await it('should consume stream as JSON', async () => {
35
- const stream = Readable.from(['{"key":', '"value"}']);
36
- const result = await json(stream) as { key: string };
37
- expect(result.key).toBe('value');
38
- });
39
-
40
- await it('should parse JSON array', async () => {
41
- const stream = Readable.from(['[1,2,3]']);
42
- const result = await json(stream) as number[];
43
- expect(Array.isArray(result)).toBe(true);
44
- expect(result.length).toBe(3);
45
- expect(result[0]).toBe(1);
46
- });
47
-
48
- await it('should parse JSON number', async () => {
49
- const stream = Readable.from(['42']);
50
- const result = await json(stream);
51
- expect(result).toBe(42);
52
- });
53
- });
54
-
55
- // ==================== buffer() ====================
56
- await describe('buffer', async () => {
57
- await it('should consume stream as Uint8Array', async () => {
58
- const stream = Readable.from(['Hello']);
59
- const result = await buffer(stream);
60
- expect(result instanceof Uint8Array).toBe(true);
61
- expect(result.length).toBeGreaterThan(0);
62
- });
63
-
64
- await it('should handle empty stream', async () => {
65
- const stream = Readable.from([]);
66
- const result = await buffer(stream);
67
- expect(result instanceof Uint8Array).toBe(true);
68
- expect(result.length).toBe(0);
69
- });
70
- });
71
-
72
- // ==================== arrayBuffer() ====================
73
- await describe('arrayBuffer', async () => {
74
- await it('should consume stream as ArrayBuffer', async () => {
75
- const stream = Readable.from(['Hello']);
76
- const result = await arrayBuffer(stream);
77
- expect(result instanceof ArrayBuffer).toBe(true);
78
- expect(result.byteLength).toBeGreaterThan(0);
79
- });
80
-
81
- await it('should handle multiple chunks', async () => {
82
- const stream = Readable.from(['AB', 'CD']);
83
- const result = await arrayBuffer(stream);
84
- const view = new Uint8Array(result);
85
- const str = new TextDecoder().decode(view);
86
- expect(str).toBe('ABCD');
87
- });
88
- });
89
-
90
- // ==================== blob() ====================
91
- await describe('blob', async () => {
92
- await it('should consume stream as Blob', async () => {
93
- const stream = Readable.from(['Hello Blob']);
94
- const result = await blob(stream);
95
- expect(result instanceof Blob).toBe(true);
96
- expect(result.size).toBeGreaterThan(0);
97
- });
98
-
99
- await it('should have correct content', async () => {
100
- const stream = Readable.from(['test']);
101
- const result = await blob(stream);
102
- const t = await result.text();
103
- expect(t).toBe('test');
104
- });
105
- });
106
- });
107
- };
@@ -1,39 +0,0 @@
1
- // stream/consumers — Utility functions for consuming readable streams
2
-
3
- export async function arrayBuffer(stream: ReadableStream | AsyncIterable<any>): Promise<ArrayBuffer> {
4
- const chunks: Uint8Array[] = [];
5
- for await (const chunk of stream as AsyncIterable<any>) {
6
- chunks.push(chunk instanceof Uint8Array ? chunk : new TextEncoder().encode(String(chunk)));
7
- }
8
- const totalLength = chunks.reduce((acc, c) => acc + c.length, 0);
9
- const result = new Uint8Array(totalLength);
10
- let offset = 0;
11
- for (const chunk of chunks) {
12
- result.set(chunk, offset);
13
- offset += chunk.length;
14
- }
15
- return result.buffer;
16
- }
17
-
18
- export async function blob(stream: ReadableStream | AsyncIterable<any>): Promise<Blob> {
19
- const buf = await arrayBuffer(stream);
20
- return new Blob([buf]);
21
- }
22
-
23
- export async function buffer(stream: ReadableStream | AsyncIterable<any>): Promise<any> {
24
- const buf = await arrayBuffer(stream);
25
- // Return as Uint8Array (Buffer-compatible)
26
- return new Uint8Array(buf);
27
- }
28
-
29
- export async function text(stream: ReadableStream | AsyncIterable<any>): Promise<string> {
30
- const buf = await arrayBuffer(stream);
31
- return new TextDecoder().decode(buf);
32
- }
33
-
34
- export async function json(stream: ReadableStream | AsyncIterable<any>): Promise<any> {
35
- const str = await text(stream);
36
- return JSON.parse(str);
37
- }
38
-
39
- export default { arrayBuffer, blob, buffer, text, json };