Skip to content

Commit f6e555f

Browse files
authored
Merge pull request #57
Support Node v24
2 parents 4fb76d5 + 17e4226 commit f6e555f

File tree

13 files changed

+278
-40
lines changed

13 files changed

+278
-40
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
strategy:
1212
fail-fast: false # if tests for one version fail, continue with the rest
1313
matrix:
14-
node-version: [18.x, '20.x', '22.x']
14+
node-version: [18.x, '20.x', '22.x', '24.x']
1515
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
1616

1717
name: Node integration tests (${{ matrix.node-version }})

lib/types/dom.d.ts

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/**
2+
* This file contains a partial and minimal re-declaration of the
3+
* DOM ReadableStream types (copied as-is), to be able to disambiguate them from
4+
* the ones in the Node types, which are also declared globally,
5+
* and can thus cause issues (interface override/merging) when
6+
* a TS codebase relies on both (even indirectly, e.g. if @types/node is
7+
* brought in by a dependency via triple-slash /// directive).
8+
*
9+
* Hopefully TS ships some native way of uniquely referencing the DOM
10+
* interface, see: https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/2120 .
11+
*/
12+
13+
// it'd be good to set <reference no-default-lib="true"/> (via /// directive) here to ensure the type definitions here
14+
// do not implicitly reference others, but it currently disables including libs anywhere
15+
// (see https://github.com/microsoft/TypeScript/issues/59067)
16+
/// <reference lib="dom" />
17+
/// <reference lib="es2018.asynciterable" />
18+
19+
export namespace DOM {
20+
/*! *****************************************************************************
21+
Copyright (c) Microsoft Corporation. All rights reserved.
22+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
23+
this file except in compliance with the License. You may obtain a copy of the
24+
License at http://www.apache.org/licenses/LICENSE-2.0
25+
26+
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
27+
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
28+
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
29+
MERCHANTABLITY OR NON-INFRINGEMENT.
30+
31+
See the Apache Version 2.0 License for specific language governing permissions
32+
and limitations under the License.
33+
***************************************************************************** */
34+
35+
interface WritableStream<W = any> {
36+
/**
37+
* The **`locked`** read-only property of the WritableStream interface returns a boolean indicating whether the `WritableStream` is locked to a writer.
38+
*
39+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/locked)
40+
*/
41+
readonly locked: boolean;
42+
/**
43+
* The **`abort()`** method of the WritableStream interface aborts the stream, signaling that the producer can no longer successfully write to the stream and it is to be immediately moved to an error state, with any queued writes discarded.
44+
*
45+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/abort)
46+
*/
47+
abort(reason?: any): Promise<void>;
48+
/**
49+
* The **`close()`** method of the WritableStream interface closes the associated stream.
50+
*
51+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/close)
52+
*/
53+
close(): Promise<void>;
54+
/**
55+
* The **`getWriter()`** method of the WritableStream interface returns a new instance of WritableStreamDefaultWriter and locks the stream to that instance.
56+
*
57+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/getWriter)
58+
*/
59+
getWriter(): WritableStreamDefaultWriter<W>;
60+
}
61+
/**
62+
* The `ReadableStream` interface of the Streams API represents a readable stream of byte data.
63+
*
64+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream)
65+
*/
66+
export interface ReadableStream<R = any> {
67+
/**
68+
* The **`locked`** read-only property of the ReadableStream interface returns whether or not the readable stream is locked to a reader.
69+
*
70+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/locked)
71+
*/
72+
readonly locked: boolean;
73+
/**
74+
* The **`cancel()`** method of the ReadableStream interface returns a Promise that resolves when the stream is canceled.
75+
*
76+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/cancel)
77+
*/
78+
cancel(reason?: any): Promise<void>;
79+
/**
80+
* The **`getReader()`** method of the ReadableStream interface creates a reader and locks the stream to it.
81+
*
82+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader)
83+
*/
84+
getReader(options: { mode: "byob" }): ReadableStreamBYOBReader;
85+
getReader(): ReadableStreamDefaultReader<R>;
86+
getReader(options?: globalThis.ReadableStreamGetReaderOptions): globalThis.ReadableStreamReader<R>;
87+
/**
88+
* The **`pipeThrough()`** method of the ReadableStream interface provides a chainable way of piping the current stream through a transform stream or any other writable/readable pair.
89+
*
90+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeThrough)
91+
*/
92+
pipeThrough<T>(
93+
transform: ReadableWritablePair<T, R>, options?: globalThis.StreamPipeOptions
94+
): ReadableStream<T>;
95+
/**
96+
* The **`pipeTo()`** method of the ReadableStream interface pipes the current `ReadableStream` to a given WritableStream and returns a Promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered.
97+
*
98+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeTo)
99+
*/
100+
pipeTo(destination: WritableStream<R>, options?: globalThis.StreamPipeOptions): Promise<void>;
101+
/**
102+
* The **`tee()`** method of the two-element array containing the two resulting branches as new ReadableStream instances.
103+
*
104+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/tee)
105+
*/
106+
tee(): [ReadableStream<R>, ReadableStream<R>];
107+
108+
// The AsyncIterator methods are declared as optional `?` here for informational purposes,
109+
// since the default DOM ReadableStream does not implement the AsyncIterator interface:
110+
// instead, the `dom.asyncinterable` lib needs to be explicitly included.
111+
// This is because the AsyncIterator browser support is still not widespread enough (missing Safari support).
112+
// To use the asyncIterable interface when available, users can manually cast a DOM.ReadableStream to
113+
// ReadableStream or PonyfilledReadableStream .
114+
[Symbol.asyncIterator]?(options?: globalThis.ReadableStreamIteratorOptions): ReadableStreamAsyncIterator<R>;
115+
values?(options?: globalThis.ReadableStreamIteratorOptions): ReadableStreamAsyncIterator<R>;
116+
}
117+
118+
/**
119+
* The `ReadableStreamBYOBReader` interface of the Streams API defines a reader for a ReadableStream that supports zero-copy reading from an underlying byte source.
120+
*
121+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader)
122+
*/
123+
export interface ReadableStreamBYOBReader extends globalThis.ReadableStreamGenericReader {
124+
/**
125+
* The **`read()`** method of the ReadableStreamBYOBReader interface is used to read data into a view on a user-supplied buffer from an associated readable byte stream.
126+
*
127+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/read)
128+
*/
129+
read<T extends ArrayBufferView>(view: T): Promise<globalThis.ReadableStreamReadResult<T>>;
130+
/**
131+
* The **`releaseLock()`** method of the ReadableStreamBYOBReader interface releases the reader's lock on the stream.
132+
*
133+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/releaseLock)
134+
*/
135+
releaseLock(): void;
136+
}
137+
138+
/**
139+
* The **`ReadableStreamDefaultReader`** interface of the Streams API represents a default reader that can be used to read stream data supplied from a network (such as a fetch request).
140+
*
141+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader)
142+
*/
143+
export interface ReadableStreamDefaultReader<R = any> extends globalThis.ReadableStreamGenericReader {
144+
/**
145+
* The **`read()`** method of the ReadableStreamDefaultReader interface returns a Promise providing access to the next chunk in the stream's internal queue.
146+
*
147+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/read)
148+
*/
149+
read(): Promise<globalThis.ReadableStreamReadResult<R>>;
150+
/**
151+
* The **`releaseLock()`** method of the ReadableStreamDefaultReader interface releases the reader's lock on the stream.
152+
*
153+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/releaseLock)
154+
*/
155+
releaseLock(): void;
156+
}
157+
158+
interface ReadableWritablePair<R = any, W = any> {
159+
readable: ReadableStream<R>;
160+
/**
161+
* Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use.
162+
*
163+
* Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader.
164+
*/
165+
writable: WritableStream<W>;
166+
}
167+
168+
interface ReadableStreamAsyncIterator<R> extends AsyncIterableIterator<R> {
169+
next(): Promise<IteratorResult<R, undefined>>;
170+
return?(value?: any): Promise<IteratorResult<R>>;
171+
}
172+
}
173+
174+
export type DomReadableStream<R> = DOM.ReadableStream<R>;
175+
176+

lib/types/index.d.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { ReadableStream as NodeWebReadableStream } from 'node:stream/web';
2+
import type { DomReadableStream } from './dom.d.ts';
3+
4+
type Data = Uint8Array | string;
5+
6+
export type WebStream<T extends Data> = DomReadableStream<T>
7+
export type NodeWebStream<T extends Data> = NodeWebReadableStream<T>;
8+
9+
type Stream<T extends Data> = WebStream<T> | NodeWebStream<T>;
10+
type MaybeStream<T extends Data> = T | Stream<T>;
11+
12+
export function readToEnd<T extends Data, JoinFn extends (chunks: T[]) => any = (chunks: T[]) => T>(
13+
input: MaybeStream<T>,
14+
join?: JoinFn
15+
): Promise<ReturnType<JoinFn>>;
16+
17+
export function toStream<T extends Data, InputType extends MaybeStream<T>>(
18+
input: InputType
19+
): InputType extends T ? Stream<InputType> : InputType;
20+
21+
export function isStream<T extends Data>(input: MaybeStream<T>): input is Stream<T>;
22+
23+
export function slice<T extends Data, InputType extends MaybeStream<T>>(
24+
input: InputType,
25+
begin: number,
26+
end?: number | typeof Infinity
27+
): InputType; // same as 'typeof input'

lib/index.d.ts renamed to lib/types/v4.7-v5.4/index.d.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
/// <reference lib="dom" />
2-
import { ReadableStream as NodeWebReadableStream } from 'node:stream/web';
2+
3+
import type { ReadableStream as NodeWebReadableStream } from 'node:stream/web';
4+
type DomReadableStream<R> = ReadableStream<R>;
5+
36
type Data = Uint8Array | string;
47

5-
export type WebStream<T extends Data> = ReadableStream<T>
8+
export type WebStream<T extends Data> = DomReadableStream<T>
69
export type NodeWebStream<T extends Data> = NodeWebReadableStream<T>;
710

811
type Stream<T extends Data> = WebStream<T> | NodeWebStream<T>;

package-lock.json

Lines changed: 13 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@
88
"tsconfig.json"
99
],
1010
"exports": {
11-
"types": "./lib/index.d.ts",
11+
"types@<5.5": "./lib/types/v4.7-v5.4/index.d.ts",
12+
"types": "./lib/types/index.d.ts",
1213
"import": "./lib/index.js"
1314
},
14-
"types": "./lib/index.d.ts",
15+
"typesVersions": {
16+
">=5.5": { ".": ["lib/types/index.d.ts"] },
17+
">=4.7": { ".": ["lib/types/v4.7-v5.4/index.d.ts"] }
18+
},
1519
"main": "./lib/index.js",
1620
"engines": {
1721
"node": ">= 18.0.0"
@@ -31,7 +35,7 @@
3135
"@types/chai": "^4.3.20",
3236
"@types/linkify-it": "^3.0.5",
3337
"@types/mocha": "^10.0.10",
34-
"@types/node": "^20.19.11",
38+
"@types/node": "^24.3.0",
3539
"@typescript-eslint/eslint-plugin": "^5.62.0",
3640
"@typescript-eslint/parser": "^5.62.0",
3741
"chai": "^4.5.0",
@@ -51,18 +55,22 @@
5155
"web-streams-polyfill": "^4.2.0"
5256
},
5357
"peerDependencies": {
54-
"typescript": ">=4.2"
58+
"@types/node": ">=18.0.0",
59+
"typescript": ">=4.7"
5560
},
5661
"peerDependenciesMeta": {
5762
"typescript": {
5863
"optional": true
64+
},
65+
"@types/node": {
66+
"optional": true
5967
}
6068
},
6169
"scripts": {
62-
"test-type-definitions": "tsc --project test/tsconfig.test.json && tsx test/typescript.test.ts && npm run test-type-definitions-es5",
63-
"test-type-definitions-es5": "tsc --project test/tsconfig.es5.test.json && tsx --tsconfig test/tsconfig.es5.test.json test/typescript.test.ts",
70+
"test-type-definitions": "tsc --project test/tsconfig.json && tsx test/typescript.test.ts && npm run test-type-definitions-es5",
71+
"test-type-definitions-es5": "tsc --project test/tsconfig.es5.json && tsx --tsconfig test/tsconfig.es5.json test/typescript.test.ts",
6472
"test-browser": "karma start karma.conf.cjs",
65-
"test-node": "ts-mocha -n loader=ts-node/esm ./test/node.test.ts ./test/common.test.ts",
73+
"test-node": "NODE_OPTIONS='--import=tsx' mocha ./test/node.test.ts ./test/common.test.ts",
6674
"lint": "eslint lib test",
6775
"docs": "jsdoc --configure .jsdocrc.cjs --destination docs --readme README.md lib && printf '%s' 'web-stream-tools.openpgpjs.org' > docs/CNAME",
6876
"preversion": "rm -rf docs",

test/browser.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,17 @@ describe('Browser integration tests', () => {
1313
const streamedData = toStream(stream);
1414
expect(await readToEnd(streamedData)).to.equal(input);
1515
});
16+
17+
it('casting a WebStream to ReadableStream allows using it as AsyncIterable (with lib="DOM.asyncIterable")', async () => {
18+
const input = 'chunk';
19+
const stream: WebStream<string> = new ReadableStream({
20+
start(controller) {
21+
controller.enqueue(input);
22+
controller.close();
23+
}
24+
});
25+
26+
// NB: this works because test/tsconfig.json includes the DOM.asyncIterable lib
27+
for await (const chunk of stream as ReadableStream) { expect(chunk).to.not.be.undefined; }
28+
});
1629
})

test/node.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ describe('Node integration tests', () => {
3737
it('polyfilled web stream cannot be converted into node native stream', async () => {
3838
const { ReadableStream: PolyfilledReadableStream } = await import ('web-streams-polyfill');
3939

40-
// this test is just to keep track that this behaviour is expected: it does not cause a TS error, but a runtime one
4140
const input = 'chunk';
41+
// this test is just to keep track that this behaviour is expected: it may not cause a TS error, but always triggers a runtime one
42+
// @ts-expect-error
4243
const stream: NodeWebReadableStream<string> = new PolyfilledReadableStream<string>({
4344
start(controller) {
4445
controller.enqueue(input);

test/tsconfig.es5.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"target": "es5",
5+
"noEmit": true, // code tested by tsx
6+
"lib": ["DOM.AsyncIterable"]
7+
}
8+
}

test/tsconfig.es5.test.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)