Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/meirionhughes/web-streams-extensions
helpers to create, change and pipe web-streams, like rxjs
https://github.com/meirionhughes/web-streams-extensions
reactive-streams readablestream webstreams
Last synced: 4 days ago
JSON representation
helpers to create, change and pipe web-streams, like rxjs
- Host: GitHub
- URL: https://github.com/meirionhughes/web-streams-extensions
- Owner: MeirionHughes
- Created: 2020-08-06T16:46:48.000Z (about 4 years ago)
- Default Branch: master
- Last Pushed: 2024-05-09T18:08:17.000Z (5 months ago)
- Last Synced: 2024-09-17T23:46:29.482Z (17 days ago)
- Topics: reactive-streams, readablestream, webstreams
- Language: TypeScript
- Homepage:
- Size: 328 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# WebStream Extensions
A collection of helper methods for WebStreams, inspired by ReactiveExtensions.
Being built on-top of ReadableStream we can have a reactive-pipeline with non-blocking back-pressure built-in.requires support for ReadableStream [use a polyfill if they're not available](https://www.npmjs.com/package/web-streams-polyfill)
Subjects require support for WritableStream. Requires support for async / await.
## Creation
### from(src: Iterable | AsyncIterable | (()=>Iterable | AsyncIterable) | ReadableLike): ReadableStream
turns an iterable source into a readable stream.
It will not try create an iterator until the result stream is read from.
```ts
from([1,2,3,4])
from(function*(){yield 1, yield 2, yield 3, yield 4};
from(async function*(){yield 1, yield 2, yield 3, await Promise.resolve(4)};
```### of(...args:T[]): ReadableStream
creates a ReadableStream where the chunks will be the in-order arguments passed to it```ts
of(1, "foo", ()=>"bar", {})
```
### concat(...streams: ReadableStream[]): ReadableStream
concatenates several streams together in the order given.It will not read from the streams until the result stream is read from.
```ts
let inputA = [1,2];
let inputB = [3,4];
let expected = [1,2,3,4];
let stream = concat(from(inputA), from(inputB));
let result = await toArray(stream);
```### defer(cb: ()=>Promise> | ReadableStream): ReadableStream
await a callback method that returns a readable-stream```ts
let input = [1,2,3,4];
let expected = [1,2,3,4];let result = await toArray(defer(x=>Promise.resolve(from(input))));
```## Consuming
### toArray(src: ReadableStream): T[]
```ts
let input = [1,2,3,4];
let expected = [1,2,3,4];
let result = await toArray(from([1,2,3,4]))
```### toPromise(src: ReadableStream): T
await exhaustion of the stream and return the last entry```ts
let input = [1,2,3,4];
let expected = 4;
let result = await toPromise(from([1,2,3,4]));
```### subscribe(src, next, complete, error): ()=>void
immediately begins to read from src, passing each chunk to the `next` callback and awaiting if it returns a promise.
once the source signals the end of the stream, `complete` is called.
if the source stream throws an error, this is passed to the `error` callback
returns a disposer method to stop reading```ts
let src = from(function*(){yield 1, yield 2, yield 3})subscribe(src,
(next)=>{ console.log("Next:", next);})
()=>{console.log("Complete")}
(err)=>{console.log("Error:", err)}
);
```## Piping
Given inconsistencies in browser support for anything other than ReadableStream, we opted to make an Operator a function of the form:
`type Op = (src:ReadableStream)=>ReadableStream`
this only requires ReadableStream to be implemented/available with getReader support. To aid pipeline these operators, a `pipe` method is available:
### pipe(src: ReadableStream, ...ops:Op): ReadableStream
```ts
let input = [1, 2, 3, 4];
let expected = { "1": 1, "2": 2, "4": 4 };let result = await toPromise(
pipe(
from(input),
filter(x => x != 3),
buffer(Infinity),
map(x => {
return x.reduce((p, c) => { p[c.toString()] = c; return p }, {});
}),
first()
));
```## Operators
### buffer(count: number, highWaterMark: number): Op
buffer chunks until the buffer size is `count` length, then enqueues the buffer and starts a new buffer
```ts
let input = [1,2,3,4];
let expected = [[1,2],[3,4]];
let stream = buffer(2)(from(input));
let result = await toArray(stream);
```### concatAll(): Op, T>
given a ReadableStream of ReadableStreams, concatenates the output of each stream.```ts
let input = [from([1,2]), from([3,4]), from([5])];
let expected = [1,2,3,4,5];
let stream = concatAll()(from(input));
let result = await toArray(stream);
```### filter(predicate: (chunk: T) => boolean): Op
filter out chunks that fail a predicate
```ts
let input = [1,2,3,4];
let expected = [1,2,4];
let stream = filter(x=>x!=3)(from(input));
let result = await toArray(stream);
```### first(predicate?:(chunk:T)=>boolean): Op
returns a stream of one chunk, the first to return true when passed to the selector, or simply the first if no predicate is supplied
```ts
let input = [1,2,3,4];
let expected = 3;
let stream = first(x=>x>=3)(from(input));
let result = await toPromise(stream);
```### last(predicate?:(chunk:T)=>boolean): Op
returns a stream of one chunk, the last to return true when passed to the predicate, or simply the last if no predicate is supplied.
```ts
let input = [1,2,3,4];
let expected = 3;
let stream = last(x=>x<4)(from(input));
let result = await toPromise(stream);
```### map(select:MapSelector, highWaterMark): Op
given a stream of T and selector f(T)->R, return a stream of R, for all f(T) != undefined
```ts
let input = [1,2,3,4];
let expected = [2,4,6,8];
let stream = map(x=>x*2)(from(input));
let result = await toArray(stream);
```### skip(count: number): Op
skip `count` elements and then stream the rest to the output
```ts
let input = [1,2,3,4,5];
let expected = [3,4,5];
let stream = pipe(from(input), skip(2));
let result = await toArray(stream);
```### take(count: number): Op
take `count` elements and close
```ts
let input = [1,2,3,4,5];
let expected = [1,2];
let stream = pipe(from(input), take(2));
let result = await toArray(stream);
```### tap(cb: (chunk: T) => void): Op
allows observing each chunk, but the output is exactly the same as in the input.
```ts
let input = [1,2,3,4];
let expected = [1,2,3,4];
let result = []
let stream = tap(x=>result.push(x))(from(input));
let result = await toPromise(stream); //execute
```### timeout(duration: number): Op
throws an error if the duration between chunks exceeds the duration (milliseconds)
## Subjects
Subjects are duplex streams with automatic tee'ing of the readable. i.e. each access call to `subject.readable` returns a _new_ ReadableStream.
### Subject()
> proof of concept - its likely there are cases not covered by the tests.
a Subject instance has the following members:
```ts
readable: ReadableStream;
writable: WritableStream;next(value:T): number;
complete(): void;
error(err): void;
```you can `pipeTo` the subject's `writable`:
```ts
let input = [1, 2, 3, 4];
let subject = new Subject();let resultPromise = toArray(subject.readable);
from(input).pipeTo(subject.writable);
let result = await resultPromise;//[1,2,3,4]
```or `pipeThrough` the subject:
```ts
let input = [1, 2, 3, 4];
let subject = new Subject();let result = await toArray(from(input).pipeThrough(subject));
expect(result).to.be.deep.eq(expected); // [1,2,3,4]
```or manually call `next`, `complete`, `error`
```ts
let subject = new Subject();
let resultPromise = toArray(subject.readable);subject.next(1);
subject.next(2);
subject.next(3);
subject.next(4);
subject.complete();let result = await resultPromise; // [1,2,3,4]
```although mixing these approaches is not advised - unpredictable behavior.