Skip to content

Commit dd03f79

Browse files
authored
Add checkOpen option (#20)
1 parent 45e90c2 commit dd03f79

5 files changed

Lines changed: 88 additions & 23 deletions

File tree

index.d.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ import {
66
Transform as TransformStream,
77
} from 'node:stream';
88

9+
export type Options = {
10+
/**
11+
When this option is `true`, the method returns `false` if the stream has already been closed.
12+
13+
@default: `false` with `isStream()`, `true` with the other methods
14+
*/
15+
checkOpen?: boolean;
16+
};
17+
918
/**
1019
@returns Whether `stream` is a [`Stream`](https://nodejs.org/api/stream.html#stream_stream).
1120
@@ -21,7 +30,7 @@ isStream({});
2130
//=> false
2231
```
2332
*/
24-
export function isStream(stream: unknown): stream is Stream;
33+
export function isStream(stream: unknown, options?: Options): stream is Stream;
2534

2635
/**
2736
@returns Whether `stream` is a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable), an [`http.OutgoingMessage`](https://nodejs.org/api/http.html#class-httpoutgoingmessage), an [`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) or an [`http.ClientRequest`](https://nodejs.org/api/http.html#class-httpserverresponse).
@@ -35,7 +44,7 @@ isWritableStream(fs.createWriteStrem('unicorn.txt'));
3544
//=> true
3645
```
3746
*/
38-
export function isWritableStream(stream: unknown): stream is WritableStream;
47+
export function isWritableStream(stream: unknown, options?: Options): stream is WritableStream;
3948

4049
/**
4150
@returns Whether `stream` is a [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable) or an [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage).
@@ -49,7 +58,7 @@ isReadableStream(fs.createReadStream('unicorn.png'));
4958
//=> true
5059
```
5160
*/
52-
export function isReadableStream(stream: unknown): stream is ReadableStream;
61+
export function isReadableStream(stream: unknown, options?: Options): stream is ReadableStream;
5362

5463
/**
5564
@returns Whether `stream` is a [`stream.Duplex`](https://nodejs.org/api/stream.html#stream_class_stream_duplex).
@@ -63,7 +72,7 @@ isDuplexStream(new DuplexStream());
6372
//=> true
6473
```
6574
*/
66-
export function isDuplexStream(stream: unknown): stream is DuplexStream;
75+
export function isDuplexStream(stream: unknown, options?: Options): stream is DuplexStream;
6776

6877
/**
6978
@returns Whether `stream` is a [`stream.Transform`](https://nodejs.org/api/stream.html#stream_class_stream_transform).
@@ -78,4 +87,4 @@ isTransformStream(StringifyStream());
7887
//=> true
7988
```
8089
*/
81-
export function isTransformStream(stream: unknown): stream is TransformStream;
90+
export function isTransformStream(stream: unknown, options?: Options): stream is TransformStream;

index.js

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
export function isStream(stream) {
1+
export function isStream(stream, {checkOpen = true} = {}) {
22
return stream !== null
33
&& typeof stream === 'object'
4+
&& (stream.writable || stream.readable || !checkOpen || (stream.writable === undefined && stream.readable === undefined))
45
&& typeof stream.pipe === 'function';
56
}
67

7-
export function isWritableStream(stream) {
8-
return isStream(stream)
9-
&& stream.writable !== false
8+
export function isWritableStream(stream, {checkOpen = true} = {}) {
9+
return isStream(stream, {checkOpen})
10+
&& (stream.writable || !checkOpen)
1011
&& typeof stream.write === 'function'
1112
&& typeof stream.end === 'function'
1213
&& typeof stream.writable === 'boolean'
@@ -15,22 +16,22 @@ export function isWritableStream(stream) {
1516
&& typeof stream.destroyed === 'boolean';
1617
}
1718

18-
export function isReadableStream(stream) {
19-
return isStream(stream)
20-
&& stream.readable !== false
19+
export function isReadableStream(stream, {checkOpen = true} = {}) {
20+
return isStream(stream, {checkOpen})
21+
&& (stream.readable || !checkOpen)
2122
&& typeof stream.read === 'function'
2223
&& typeof stream.readable === 'boolean'
2324
&& typeof stream.readableObjectMode === 'boolean'
2425
&& typeof stream.destroy === 'function'
2526
&& typeof stream.destroyed === 'boolean';
2627
}
2728

28-
export function isDuplexStream(stream) {
29-
return isWritableStream(stream)
30-
&& isReadableStream(stream);
29+
export function isDuplexStream(stream, options) {
30+
return isWritableStream(stream, options)
31+
&& isReadableStream(stream, options);
3132
}
3233

33-
export function isTransformStream(stream) {
34-
return isDuplexStream(stream)
34+
export function isTransformStream(stream, options) {
35+
return isDuplexStream(stream, options)
3536
&& typeof stream._transform === 'function';
3637
}

index.test-d.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ import {
55
Duplex as DuplexStream,
66
Transform as TransformStream,
77
} from 'node:stream';
8-
import {expectAssignable} from 'tsd';
8+
import {expectType, expectAssignable} from 'tsd';
99
import {
1010
isStream,
1111
isWritableStream,
1212
isReadableStream,
1313
isDuplexStream,
1414
isTransformStream,
15+
Options,
1516
} from './index.js';
1617

1718
const foo = '';
@@ -35,3 +36,16 @@ if (isDuplexStream(foo)) {
3536
if (isTransformStream(foo)) {
3637
expectAssignable<TransformStream>(foo);
3738
}
39+
40+
isStream(foo, {});
41+
isStream(foo, {checkOpen: false});
42+
isWritableStream(foo, {});
43+
isWritableStream(foo, {checkOpen: false});
44+
isReadableStream(foo, {});
45+
isReadableStream(foo, {checkOpen: false});
46+
isDuplexStream(foo, {});
47+
isDuplexStream(foo, {checkOpen: false});
48+
isTransformStream(foo, {});
49+
isTransformStream(foo, {checkOpen: false});
50+
51+
expectType<boolean | undefined>({} as Options['checkOpen']);

readme.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,35 @@ isStream({});
2323

2424
## API
2525

26-
### isStream(stream)
26+
### isStream(stream, options?)
2727

2828
Returns a `boolean` for whether it's a [`Stream`](https://nodejs.org/api/stream.html#stream_stream).
2929

30-
#### isWritableStream(stream)
30+
### isWritableStream(stream, options?)
3131

3232
Returns a `boolean` for whether it's a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable), an [`http.OutgoingMessage`](https://nodejs.org/api/http.html#class-httpoutgoingmessage), an [`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) or an [`http.ClientRequest`](https://nodejs.org/api/http.html#class-httpserverresponse).
3333

34-
#### isReadableStream(stream)
34+
### isReadableStream(stream, options?)
3535

3636
Returns a `boolean` for whether it's a [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable) or an [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage).
3737

38-
#### isDuplexStream(stream)
38+
### isDuplexStream(stream, options?)
3939

4040
Returns a `boolean` for whether it's a [`stream.Duplex`](https://nodejs.org/api/stream.html#stream_class_stream_duplex).
4141

42-
#### isTransformStream(stream)
42+
### isTransformStream(stream, options?)
4343

4444
Returns a `boolean` for whether it's a [`stream.Transform`](https://nodejs.org/api/stream.html#stream_class_stream_transform).
4545

46+
### Options
47+
48+
#### checkOpen
49+
50+
Type: `boolean`\
51+
Default: `false` with [`isStream()`](#isstreamstream-options), `true` with the other methods
52+
53+
When this option is `true`, the method returns `false` if the stream has already been closed.
54+
4655
## Related
4756

4857
- [is-file-stream](https://github.com/jamestalmage/is-file-stream) - Detect if a stream is a file stream

test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,35 @@ test('isTransformStream()', t => {
111111
t.false(isTransformStream(undefined));
112112
t.false(isTransformStream(''));
113113
});
114+
115+
// eslint-disable-next-line max-params
116+
const testStreamOpen = (t, stream, checkMethod, expectedResult, options) => {
117+
t.true(checkMethod(stream, options));
118+
stream.destroy();
119+
t.is(checkMethod(stream, options), expectedResult);
120+
};
121+
122+
test('isStream(readable), no options', testStreamOpen, new Stream.Readable(), isStream, false);
123+
test('isStream(readable, {})', testStreamOpen, new Stream.Readable(), isStream, false, {});
124+
test('isStream(readable, {checkOpen: true})', testStreamOpen, new Stream.Readable(), isStream, false, {checkOpen: true});
125+
test('isStream(readable, {checkOpen: false})', testStreamOpen, new Stream.Readable(), isStream, true, {checkOpen: false});
126+
test('isStream(writable), no options', testStreamOpen, new Stream.Writable(), isStream, false);
127+
test('isStream(writable, {})', testStreamOpen, new Stream.Writable(), isStream, false, {});
128+
test('isStream(writable, {checkOpen: true})', testStreamOpen, new Stream.Writable(), isStream, false, {checkOpen: true});
129+
test('isStream(writable, {checkOpen: false})', testStreamOpen, new Stream.Writable(), isStream, true, {checkOpen: false});
130+
test('isWritableStream(writable), no options', testStreamOpen, new Stream.Writable(), isWritableStream, false);
131+
test('isWritableStream(writable, {})', testStreamOpen, new Stream.Writable(), isWritableStream, false, {});
132+
test('isWritableStream(writable, {checkOpen: true})', testStreamOpen, new Stream.Writable(), isWritableStream, false, {checkOpen: true});
133+
test('isWritableStream(writable, {checkOpen: false})', testStreamOpen, new Stream.Writable(), isWritableStream, true, {checkOpen: false});
134+
test('isReadableStream(readable), no options', testStreamOpen, new Stream.Readable(), isReadableStream, false);
135+
test('isReadableStream(readable, {})', testStreamOpen, new Stream.Readable(), isReadableStream, false, {});
136+
test('isReadableStream(readable, {checkOpen: true})', testStreamOpen, new Stream.Readable(), isReadableStream, false, {checkOpen: true});
137+
test('isReadableStream(readable, {checkOpen: false})', testStreamOpen, new Stream.Readable(), isReadableStream, true, {checkOpen: false});
138+
test('isDuplexStream(duplex), no options', testStreamOpen, new Stream.Duplex(), isDuplexStream, false);
139+
test('isDuplexStream(duplex, {})', testStreamOpen, new Stream.Duplex(), isDuplexStream, false, {});
140+
test('isDuplexStream(duplex, {checkOpen: true})', testStreamOpen, new Stream.Duplex(), isDuplexStream, false, {checkOpen: true});
141+
test('isDuplexStream(duplex, {checkOpen: false})', testStreamOpen, new Stream.Duplex(), isDuplexStream, true, {checkOpen: false});
142+
test('isTransformStream(transform), no options', testStreamOpen, new Stream.Transform(), isTransformStream, false);
143+
test('isTransformStream(transform, {})', testStreamOpen, new Stream.Transform(), isTransformStream, false, {});
144+
test('isTransformStream(transform, {checkOpen: true})', testStreamOpen, new Stream.Transform(), isTransformStream, false, {checkOpen: true});
145+
test('isTransformStream(transform, {checkOpen: false})', testStreamOpen, new Stream.Transform(), isTransformStream, true, {checkOpen: false});

0 commit comments

Comments
 (0)