-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add full
NodeJS.ReadableStream
support
see also: microsoft/TypeScript#420 (comment) thanks: @nthypes @ericmasiello
- Loading branch information
Showing
13 changed files
with
233 additions
and
165 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,8 +16,8 @@ jobs: | |
strategy: | ||
matrix: | ||
nodejs: [ | ||
# 14, | ||
# 16, | ||
14, | ||
16, | ||
18, | ||
# "lts/*" | ||
] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,106 +1,18 @@ | ||
# `yield-stream` | ||
|
||
- **Docs: https://yield-stream.vercel.app** | ||
[**Github**](https://github.com/gptlabs/yield-stream) | | ||
[**NPM**](https://npmjs.com/package/yield-stream) | | ||
[**Docs**](https://yield-stream.vercel.app) | ||
|
||
A small library for switching between streams, generators, and arrays. | ||
A small library for switching between streams, generators, and arrays. See docs | ||
for details. | ||
|
||
### Note: Using `NodeJS.ReadableStream` | ||
|
||
```ts | ||
/** | ||
* `compose(f, g, h, ...)` returns a generator function `G(data)` that yields | ||
* all `(f · g · h · ...)(data)`. | ||
* | ||
* @note Used to compose multiple transforms into a `pipeline`. | ||
*/ | ||
export const compose = <T>( | ||
...generators: GeneratorFn<T>[] | ||
): GeneratorFn<T> => { | ||
return generators.reduce( | ||
(prev, next) => async function* (data) { | ||
for await (const chunk of prev(data)) { | ||
yield* next(chunk); | ||
} | ||
}, | ||
); | ||
}; | ||
|
||
/** | ||
* Accepts a stream and transforms and returns a stream of the transformed | ||
* chunks. Transforms can yield multiple chunks per input chunk. | ||
*/ | ||
export const pipeline = <T>( | ||
stream: ReadableStream<T>, | ||
...transforms: GeneratorFn<T>[] | ||
): ReadableStream<T> => { | ||
const composed = compose(...transforms); | ||
return generateStream( | ||
async function* () { | ||
for await (const chunk of yieldStream(stream)) { | ||
yield* composed(chunk); | ||
} | ||
} | ||
); | ||
}; | ||
|
||
/** | ||
* Accepts a stream and yields all of its chunks. | ||
*/ | ||
export const yieldStream = async function* <T>( | ||
stream: ReadableStream<T>, | ||
controller?: AbortController | ||
) { | ||
const reader = stream.getReader(); | ||
while (true) { | ||
if (controller?.signal.aborted) { | ||
break; | ||
} | ||
|
||
const { done, value } = await reader.read(); | ||
if (done) { | ||
break; | ||
} | ||
|
||
yield value; | ||
} | ||
}; | ||
By default, this library uses WHATWG `ReadableStream`, which is only available | ||
on Node 18+. If you are on an older version of Node or otherwise need to use | ||
`NodeJS.ReadableStream`, import from: | ||
|
||
/** | ||
* Accepts a generator function and streams its outputs. | ||
*/ | ||
export const generateStream = <T, TReturn, D>( | ||
G: StreamGenerator<D, T, TReturn>, | ||
data?: D | ||
): ReadableStream<T> => { | ||
return new ReadableStream<T>({ | ||
async start(controller) { | ||
for await (const chunk of G(data)) { | ||
controller.enqueue(chunk); | ||
} | ||
controller.close(); | ||
}, | ||
}); | ||
}; | ||
|
||
/** | ||
* Accepts an array and returns a stream of its items. | ||
*/ | ||
export const streamArray = <T>(array: T[]): ReadableStream<T> => { | ||
return generateStream(function* () { | ||
for (const item of array) { | ||
yield item; | ||
} | ||
}); | ||
}; | ||
|
||
/** | ||
* Accepts a stream and yields a growing buffer of all chunks received. | ||
*/ | ||
export const buffer = async function* <T>(stream: ReadableStream<T>) { | ||
const buffer: T[] = []; | ||
|
||
for await (const chunk of yieldStream(stream)) { | ||
buffer.push(chunk); | ||
yield buffer; | ||
} | ||
}; | ||
```ts | ||
import { yieldStream } from "yield-stream/node"; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./dist/platforms/node"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./dist/platforms/node.js"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,5 @@ | ||
export * from "./lib"; | ||
export * from "./types"; | ||
/** | ||
* `.` entrypoint is Edge by default. NodeJS.ReadableStream version available at | ||
* `./node`. | ||
*/ | ||
export * from "./platforms/edge"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { Readable } from "stream"; | ||
import { GeneratorFn, StreamGenerator } from "../types"; | ||
import { compose, generateArray } from "./shared"; | ||
|
||
/** | ||
* Accepts a generator function and returns a NodeJS.ReadableStream of its | ||
* outputs. | ||
*/ | ||
export const generateStream = <Chunk, Return, Data>( | ||
G: StreamGenerator<Data, Chunk, Return>, | ||
data?: Data | ||
): NodeJS.ReadableStream => { | ||
const readable = Readable.from(G(data)); | ||
return readable; | ||
}; | ||
|
||
|
||
/** | ||
* Accepts a stream and yields all of its chunks. | ||
*/ | ||
export const yieldStream = async function* <Chunk>( | ||
stream: NodeJS.ReadableStream, | ||
controller?: AbortController | ||
): AsyncGenerator<Chunk> { | ||
for await (const chunk of stream) { | ||
if (controller?.signal.aborted) { | ||
break; | ||
} | ||
|
||
yield chunk as Chunk; | ||
} | ||
}; | ||
|
||
/** | ||
* Accepts a stream and transforms and returns a stream of the transformed | ||
* chunks. Transforms can yield multiple chunks per input chunk. | ||
*/ | ||
export const pipeline = <Chunk>( | ||
stream: NodeJS.ReadableStream, | ||
...transforms: GeneratorFn<Chunk>[] | ||
): NodeJS.ReadableStream => { | ||
const composed = compose(...transforms); | ||
return generateStream( | ||
async function* () { | ||
for await (const chunk of yieldStream<Chunk>(stream)) { | ||
yield* composed(chunk); | ||
} | ||
} | ||
); | ||
}; | ||
|
||
/** | ||
* Accepts an array and returns a stream of its items. | ||
*/ | ||
export const streamArray = <Chunk>( | ||
array: Chunk[] | ||
): NodeJS.ReadableStream => { | ||
return generateStream(generateArray(array)); | ||
}; | ||
|
||
/** | ||
* Accepts a stream and yields a growing buffer of all chunks received. | ||
*/ | ||
export const buffer = async function* <Chunk>( | ||
stream: NodeJS.ReadableStream | ||
) { | ||
const buffer: Chunk[] = []; | ||
|
||
for await (const chunk of yieldStream<Chunk>(stream)) { | ||
buffer.push(chunk); | ||
yield buffer; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { GeneratorFn } from "../types"; | ||
|
||
/** | ||
* `compose(f, g, h, ...)` returns a generator function `G(data)` that yields | ||
* all `(f · g · h · ...)(data)`. | ||
* | ||
* @note Used to compose multiple transforms into a `pipeline`. | ||
*/ | ||
export const compose = <Chunk>( | ||
...generators: GeneratorFn<Chunk>[] | ||
): GeneratorFn<Chunk> => { | ||
return generators.reduce( | ||
(prev, next) => async function* (data) { | ||
for await (const chunk of prev(data)) { | ||
yield* next(chunk); | ||
} | ||
}, | ||
); | ||
}; | ||
|
||
/** | ||
* Accepts an array and returns a generator function that yields its items. | ||
*/ | ||
export const generateArray = <Chunk>( | ||
array: Chunk[] | ||
) => { | ||
return function* () { | ||
for (const item of array) { | ||
yield item; | ||
} | ||
}; | ||
}; |
Oops, something went wrong.
fabf6e6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
yield-stream – ./
yield-stream-gptlabs.vercel.app
yield-stream.vercel.app
yield-stream-git-master-gptlabs.vercel.app