Skip to content

Commit

Permalink
Add checkOpen option (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky authored Feb 19, 2024
1 parent 45e90c2 commit dd03f79
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 23 deletions.
19 changes: 14 additions & 5 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ import {
Transform as TransformStream,
} from 'node:stream';

export type Options = {
/**
When this option is `true`, the method returns `false` if the stream has already been closed.
@default: `false` with `isStream()`, `true` with the other methods
*/
checkOpen?: boolean;
};

/**
@returns Whether `stream` is a [`Stream`](https://nodejs.org/api/stream.html#stream_stream).
Expand All @@ -21,7 +30,7 @@ isStream({});
//=> false
```
*/
export function isStream(stream: unknown): stream is Stream;
export function isStream(stream: unknown, options?: Options): stream is Stream;

/**
@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).
Expand All @@ -35,7 +44,7 @@ isWritableStream(fs.createWriteStrem('unicorn.txt'));
//=> true
```
*/
export function isWritableStream(stream: unknown): stream is WritableStream;
export function isWritableStream(stream: unknown, options?: Options): stream is WritableStream;

/**
@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).
Expand All @@ -49,7 +58,7 @@ isReadableStream(fs.createReadStream('unicorn.png'));
//=> true
```
*/
export function isReadableStream(stream: unknown): stream is ReadableStream;
export function isReadableStream(stream: unknown, options?: Options): stream is ReadableStream;

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

/**
@returns Whether `stream` is a [`stream.Transform`](https://nodejs.org/api/stream.html#stream_class_stream_transform).
Expand All @@ -78,4 +87,4 @@ isTransformStream(StringifyStream());
//=> true
```
*/
export function isTransformStream(stream: unknown): stream is TransformStream;
export function isTransformStream(stream: unknown, options?: Options): stream is TransformStream;
25 changes: 13 additions & 12 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
export function isStream(stream) {
export function isStream(stream, {checkOpen = true} = {}) {
return stream !== null
&& typeof stream === 'object'
&& (stream.writable || stream.readable || !checkOpen || (stream.writable === undefined && stream.readable === undefined))
&& typeof stream.pipe === 'function';
}

export function isWritableStream(stream) {
return isStream(stream)
&& stream.writable !== false
export function isWritableStream(stream, {checkOpen = true} = {}) {
return isStream(stream, {checkOpen})
&& (stream.writable || !checkOpen)
&& typeof stream.write === 'function'
&& typeof stream.end === 'function'
&& typeof stream.writable === 'boolean'
Expand All @@ -15,22 +16,22 @@ export function isWritableStream(stream) {
&& typeof stream.destroyed === 'boolean';
}

export function isReadableStream(stream) {
return isStream(stream)
&& stream.readable !== false
export function isReadableStream(stream, {checkOpen = true} = {}) {
return isStream(stream, {checkOpen})
&& (stream.readable || !checkOpen)
&& typeof stream.read === 'function'
&& typeof stream.readable === 'boolean'
&& typeof stream.readableObjectMode === 'boolean'
&& typeof stream.destroy === 'function'
&& typeof stream.destroyed === 'boolean';
}

export function isDuplexStream(stream) {
return isWritableStream(stream)
&& isReadableStream(stream);
export function isDuplexStream(stream, options) {
return isWritableStream(stream, options)
&& isReadableStream(stream, options);
}

export function isTransformStream(stream) {
return isDuplexStream(stream)
export function isTransformStream(stream, options) {
return isDuplexStream(stream, options)
&& typeof stream._transform === 'function';
}
16 changes: 15 additions & 1 deletion index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import {
Duplex as DuplexStream,
Transform as TransformStream,
} from 'node:stream';
import {expectAssignable} from 'tsd';
import {expectType, expectAssignable} from 'tsd';
import {
isStream,
isWritableStream,
isReadableStream,
isDuplexStream,
isTransformStream,
Options,
} from './index.js';

const foo = '';
Expand All @@ -35,3 +36,16 @@ if (isDuplexStream(foo)) {
if (isTransformStream(foo)) {
expectAssignable<TransformStream>(foo);
}

isStream(foo, {});
isStream(foo, {checkOpen: false});
isWritableStream(foo, {});
isWritableStream(foo, {checkOpen: false});
isReadableStream(foo, {});
isReadableStream(foo, {checkOpen: false});
isDuplexStream(foo, {});
isDuplexStream(foo, {checkOpen: false});
isTransformStream(foo, {});
isTransformStream(foo, {checkOpen: false});

expectType<boolean | undefined>({} as Options['checkOpen']);
19 changes: 14 additions & 5 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,35 @@ isStream({});

## API

### isStream(stream)
### isStream(stream, options?)

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

#### isWritableStream(stream)
### isWritableStream(stream, options?)

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).

#### isReadableStream(stream)
### isReadableStream(stream, options?)

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).

#### isDuplexStream(stream)
### isDuplexStream(stream, options?)

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

#### isTransformStream(stream)
### isTransformStream(stream, options?)

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

### Options

#### checkOpen

Type: `boolean`\
Default: `false` with [`isStream()`](#isstreamstream-options), `true` with the other methods

When this option is `true`, the method returns `false` if the stream has already been closed.

## Related

- [is-file-stream](https://github.com/jamestalmage/is-file-stream) - Detect if a stream is a file stream
Expand Down
32 changes: 32 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,35 @@ test('isTransformStream()', t => {
t.false(isTransformStream(undefined));
t.false(isTransformStream(''));
});

// eslint-disable-next-line max-params
const testStreamOpen = (t, stream, checkMethod, expectedResult, options) => {
t.true(checkMethod(stream, options));
stream.destroy();
t.is(checkMethod(stream, options), expectedResult);
};

test('isStream(readable), no options', testStreamOpen, new Stream.Readable(), isStream, false);
test('isStream(readable, {})', testStreamOpen, new Stream.Readable(), isStream, false, {});
test('isStream(readable, {checkOpen: true})', testStreamOpen, new Stream.Readable(), isStream, false, {checkOpen: true});
test('isStream(readable, {checkOpen: false})', testStreamOpen, new Stream.Readable(), isStream, true, {checkOpen: false});
test('isStream(writable), no options', testStreamOpen, new Stream.Writable(), isStream, false);
test('isStream(writable, {})', testStreamOpen, new Stream.Writable(), isStream, false, {});
test('isStream(writable, {checkOpen: true})', testStreamOpen, new Stream.Writable(), isStream, false, {checkOpen: true});
test('isStream(writable, {checkOpen: false})', testStreamOpen, new Stream.Writable(), isStream, true, {checkOpen: false});
test('isWritableStream(writable), no options', testStreamOpen, new Stream.Writable(), isWritableStream, false);
test('isWritableStream(writable, {})', testStreamOpen, new Stream.Writable(), isWritableStream, false, {});
test('isWritableStream(writable, {checkOpen: true})', testStreamOpen, new Stream.Writable(), isWritableStream, false, {checkOpen: true});
test('isWritableStream(writable, {checkOpen: false})', testStreamOpen, new Stream.Writable(), isWritableStream, true, {checkOpen: false});
test('isReadableStream(readable), no options', testStreamOpen, new Stream.Readable(), isReadableStream, false);
test('isReadableStream(readable, {})', testStreamOpen, new Stream.Readable(), isReadableStream, false, {});
test('isReadableStream(readable, {checkOpen: true})', testStreamOpen, new Stream.Readable(), isReadableStream, false, {checkOpen: true});
test('isReadableStream(readable, {checkOpen: false})', testStreamOpen, new Stream.Readable(), isReadableStream, true, {checkOpen: false});
test('isDuplexStream(duplex), no options', testStreamOpen, new Stream.Duplex(), isDuplexStream, false);
test('isDuplexStream(duplex, {})', testStreamOpen, new Stream.Duplex(), isDuplexStream, false, {});
test('isDuplexStream(duplex, {checkOpen: true})', testStreamOpen, new Stream.Duplex(), isDuplexStream, false, {checkOpen: true});
test('isDuplexStream(duplex, {checkOpen: false})', testStreamOpen, new Stream.Duplex(), isDuplexStream, true, {checkOpen: false});
test('isTransformStream(transform), no options', testStreamOpen, new Stream.Transform(), isTransformStream, false);
test('isTransformStream(transform, {})', testStreamOpen, new Stream.Transform(), isTransformStream, false, {});
test('isTransformStream(transform, {checkOpen: true})', testStreamOpen, new Stream.Transform(), isTransformStream, false, {checkOpen: true});
test('isTransformStream(transform, {checkOpen: false})', testStreamOpen, new Stream.Transform(), isTransformStream, true, {checkOpen: false});

0 comments on commit dd03f79

Please sign in to comment.