xstream
Version:
An extremely intuitive, small, and fast functional reactive stream library for JavaScript
674 lines (673 loc) • 26 kB
TypeScript
declare const NO: {};
export interface InternalListener<T> {
_n: (v: T) => void;
_e: (err: any) => void;
_c: () => void;
}
declare const NO_IL: InternalListener<any>;
export interface InternalProducer<T> {
_start(listener: InternalListener<T>): void;
_stop: () => void;
}
export interface OutSender<T> {
out: Stream<T>;
}
export interface Operator<T, R> extends InternalProducer<R>, InternalListener<T>, OutSender<R> {
type: string;
ins: Stream<T>;
_start(out: Stream<R>): void;
}
export interface Aggregator<T, U> extends InternalProducer<U>, OutSender<U> {
type: string;
insArr: Array<Stream<T>>;
_start(out: Stream<U>): void;
}
export interface Producer<T> {
start: (listener: Listener<T>) => void;
stop: () => void;
}
export interface Listener<T> {
next: (x: T) => void;
error: (err: any) => void;
complete: () => void;
}
export interface Subscription {
unsubscribe(): void;
}
export interface Observable<T> {
subscribe(listener: Listener<T>): Subscription;
}
export interface MergeSignature {
(): Stream<any>;
<T1>(s1: Stream<T1>): Stream<T1>;
<T1, T2>(s1: Stream<T1>, s2: Stream<T2>): Stream<T1 | T2>;
<T1, T2, T3>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>): Stream<T1 | T2 | T3>;
<T1, T2, T3, T4>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>): Stream<T1 | T2 | T3 | T4>;
<T1, T2, T3, T4, T5>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>, s5: Stream<T5>): Stream<T1 | T2 | T3 | T4 | T5>;
<T1, T2, T3, T4, T5, T6>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>, s5: Stream<T5>, s6: Stream<T6>): Stream<T1 | T2 | T3 | T4 | T5 | T6>;
<T1, T2, T3, T4, T5, T6, T7>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>, s5: Stream<T5>, s6: Stream<T6>, s7: Stream<T7>): Stream<T1 | T2 | T3 | T4 | T5 | T6 | T7>;
<T1, T2, T3, T4, T5, T6, T7, T8>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>, s5: Stream<T5>, s6: Stream<T6>, s7: Stream<T7>, s8: Stream<T8>): Stream<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8>;
<T1, T2, T3, T4, T5, T6, T7, T8, T9>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>, s5: Stream<T5>, s6: Stream<T6>, s7: Stream<T7>, s8: Stream<T8>, s9: Stream<T9>): Stream<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9>;
<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>, s5: Stream<T5>, s6: Stream<T6>, s7: Stream<T7>, s8: Stream<T8>, s9: Stream<T9>, s10: Stream<T10>): Stream<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10>;
<T>(...stream: Array<Stream<T>>): Stream<T>;
}
export interface CombineSignature {
(): Stream<Array<any>>;
<T1>(s1: Stream<T1>): Stream<[T1]>;
<T1, T2>(s1: Stream<T1>, s2: Stream<T2>): Stream<[T1, T2]>;
<T1, T2, T3>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>): Stream<[T1, T2, T3]>;
<T1, T2, T3, T4>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>): Stream<[T1, T2, T3, T4]>;
<T1, T2, T3, T4, T5>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>, s5: Stream<T5>): Stream<[T1, T2, T3, T4, T5]>;
<T1, T2, T3, T4, T5, T6>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>, s5: Stream<T5>, s6: Stream<T6>): Stream<[T1, T2, T3, T4, T5, T6]>;
<T1, T2, T3, T4, T5, T6, T7>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>, s5: Stream<T5>, s6: Stream<T6>, s7: Stream<T7>): Stream<[T1, T2, T3, T4, T5, T6, T7]>;
<T1, T2, T3, T4, T5, T6, T7, T8>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>, s5: Stream<T5>, s6: Stream<T6>, s7: Stream<T7>, s8: Stream<T8>): Stream<[T1, T2, T3, T4, T5, T6, T7, T8]>;
<T1, T2, T3, T4, T5, T6, T7, T8, T9>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>, s5: Stream<T5>, s6: Stream<T6>, s7: Stream<T7>, s8: Stream<T8>, s9: Stream<T9>): Stream<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(s1: Stream<T1>, s2: Stream<T2>, s3: Stream<T3>, s4: Stream<T4>, s5: Stream<T5>, s6: Stream<T6>, s7: Stream<T7>, s8: Stream<T8>, s9: Stream<T9>, s10: Stream<T10>): Stream<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
<T>(...stream: Array<Stream<T>>): Stream<Array<T>>;
(...stream: Array<Stream<any>>): Stream<Array<any>>;
}
export declare class Stream<T> implements InternalListener<T> {
_prod: InternalProducer<T>;
protected _ils: Array<InternalListener<T>>;
protected _stopID: any;
protected _dl: InternalListener<T>;
protected _d: boolean;
protected _target: Stream<T> | null;
protected _err: any;
constructor(producer?: InternalProducer<T>);
_n(t: T): void;
_e(err: any): void;
_c(): void;
_x(): void;
_stopNow(): void;
_add(il: InternalListener<T>): void;
_remove(il: InternalListener<T>): void;
_pruneCycles(): void;
_hasNoSinks(x: InternalListener<any>, trace: Array<any>): boolean;
private ctor;
/**
* Adds a Listener to the Stream.
*
* @param {Listener} listener
*/
addListener(listener: Partial<Listener<T>>): void;
/**
* Removes a Listener from the Stream, assuming the Listener was added to it.
*
* @param {Listener<T>} listener
*/
removeListener(listener: Partial<Listener<T>>): void;
/**
* Adds a Listener to the Stream returning a Subscription to remove that
* listener.
*
* @param {Listener} listener
* @returns {Subscription}
*/
subscribe(listener: Partial<Listener<T>>): Subscription;
/**
* Creates a new Stream given a Producer.
*
* @factory true
* @param {Producer} producer An optional Producer that dictates how to
* start, generate events, and stop the Stream.
* @return {Stream}
*/
static create<T>(producer?: Producer<T>): Stream<T>;
/**
* Creates a new MemoryStream given a Producer.
*
* @factory true
* @param {Producer} producer An optional Producer that dictates how to
* start, generate events, and stop the Stream.
* @return {MemoryStream}
*/
static createWithMemory<T>(producer?: Producer<T>): MemoryStream<T>;
/**
* Creates a Stream that does nothing when started. It never emits any event.
*
* Marble diagram:
*
* ```text
* never
* -----------------------
* ```
*
* @factory true
* @return {Stream}
*/
static never<T = any>(): Stream<T>;
/**
* Creates a Stream that immediately emits the "complete" notification when
* started, and that's it.
*
* Marble diagram:
*
* ```text
* empty
* -|
* ```
*
* @factory true
* @return {Stream}
*/
static empty<T = any>(): Stream<T>;
/**
* Creates a Stream that immediately emits an "error" notification with the
* value you passed as the `error` argument when the stream starts, and that's
* it.
*
* Marble diagram:
*
* ```text
* throw(X)
* -X
* ```
*
* @factory true
* @param error The error event to emit on the created stream.
* @return {Stream}
*/
static throw(error: any): Stream<any>;
/**
* Creates a stream from an Array, Promise, or an Observable.
*
* @factory true
* @param {Array|PromiseLike|Observable} input The input to make a stream from.
* @return {Stream}
*/
static from<T>(input: PromiseLike<T> | Stream<T> | Array<T> | Observable<T>): Stream<T>;
/**
* Creates a Stream that immediately emits the arguments that you give to
* *of*, then completes.
*
* Marble diagram:
*
* ```text
* of(1,2,3)
* 123|
* ```
*
* @factory true
* @param a The first value you want to emit as an event on the stream.
* @param b The second value you want to emit as an event on the stream. One
* or more of these values may be given as arguments.
* @return {Stream}
*/
static of<T>(...items: Array<T>): Stream<T>;
/**
* Converts an array to a stream. The returned stream will emit synchronously
* all the items in the array, and then complete.
*
* Marble diagram:
*
* ```text
* fromArray([1,2,3])
* 123|
* ```
*
* @factory true
* @param {Array} array The array to be converted as a stream.
* @return {Stream}
*/
static fromArray<T>(array: Array<T>): Stream<T>;
/**
* Converts a promise to a stream. The returned stream will emit the resolved
* value of the promise, and then complete. However, if the promise is
* rejected, the stream will emit the corresponding error.
*
* Marble diagram:
*
* ```text
* fromPromise( ----42 )
* -----------------42|
* ```
*
* @factory true
* @param {PromiseLike} promise The promise to be converted as a stream.
* @return {Stream}
*/
static fromPromise<T>(promise: PromiseLike<T>): Stream<T>;
/**
* Converts an Observable into a Stream.
*
* @factory true
* @param {any} observable The observable to be converted as a stream.
* @return {Stream}
*/
static fromObservable<T>(obs: {
subscribe: any;
}): Stream<T>;
/**
* Creates a stream that periodically emits incremental numbers, every
* `period` milliseconds.
*
* Marble diagram:
*
* ```text
* periodic(1000)
* ---0---1---2---3---4---...
* ```
*
* @factory true
* @param {number} period The interval in milliseconds to use as a rate of
* emission.
* @return {Stream}
*/
static periodic(period: number): Stream<number>;
/**
* Blends multiple streams together, emitting events from all of them
* concurrently.
*
* *merge* takes multiple streams as arguments, and creates a stream that
* behaves like each of the argument streams, in parallel.
*
* Marble diagram:
*
* ```text
* --1----2-----3--------4---
* ----a-----b----c---d------
* merge
* --1-a--2--b--3-c---d--4---
* ```
*
* @factory true
* @param {Stream} stream1 A stream to merge together with other streams.
* @param {Stream} stream2 A stream to merge together with other streams. Two
* or more streams may be given as arguments.
* @return {Stream}
*/
static merge: MergeSignature;
/**
* Combines multiple input streams together to return a stream whose events
* are arrays that collect the latest events from each input stream.
*
* *combine* internally remembers the most recent event from each of the input
* streams. When any of the input streams emits an event, that event together
* with all the other saved events are combined into an array. That array will
* be emitted on the output stream. It's essentially a way of joining together
* the events from multiple streams.
*
* Marble diagram:
*
* ```text
* --1----2-----3--------4---
* ----a-----b-----c--d------
* combine
* ----1a-2a-2b-3b-3c-3d-4d--
* ```
*
* @factory true
* @param {Stream} stream1 A stream to combine together with other streams.
* @param {Stream} stream2 A stream to combine together with other streams.
* Multiple streams, not just two, may be given as arguments.
* @return {Stream}
*/
static combine: CombineSignature;
protected _map<U>(project: (t: T) => U): Stream<U> | MemoryStream<U>;
/**
* Transforms each event from the input Stream through a `project` function,
* to get a Stream that emits those transformed events.
*
* Marble diagram:
*
* ```text
* --1---3--5-----7------
* map(i => i * 10)
* --10--30-50----70-----
* ```
*
* @param {Function} project A function of type `(t: T) => U` that takes event
* `t` of type `T` from the input Stream and produces an event of type `U`, to
* be emitted on the output Stream.
* @return {Stream}
*/
map<U>(project: (t: T) => U): Stream<U>;
/**
* It's like `map`, but transforms each input event to always the same
* constant value on the output Stream.
*
* Marble diagram:
*
* ```text
* --1---3--5-----7-----
* mapTo(10)
* --10--10-10----10----
* ```
*
* @param projectedValue A value to emit on the output Stream whenever the
* input Stream emits any value.
* @return {Stream}
*/
mapTo<U>(projectedValue: U): Stream<U>;
filter<S extends T>(passes: (t: T) => t is S): Stream<S>;
filter(passes: (t: T) => boolean): Stream<T>;
/**
* Lets the first `amount` many events from the input stream pass to the
* output stream, then makes the output stream complete.
*
* Marble diagram:
*
* ```text
* --a---b--c----d---e--
* take(3)
* --a---b--c|
* ```
*
* @param {number} amount How many events to allow from the input stream
* before completing the output stream.
* @return {Stream}
*/
take(amount: number): Stream<T>;
/**
* Ignores the first `amount` many events from the input stream, and then
* after that starts forwarding events from the input stream to the output
* stream.
*
* Marble diagram:
*
* ```text
* --a---b--c----d---e--
* drop(3)
* --------------d---e--
* ```
*
* @param {number} amount How many events to ignore from the input stream
* before forwarding all events from the input stream to the output stream.
* @return {Stream}
*/
drop(amount: number): Stream<T>;
/**
* When the input stream completes, the output stream will emit the last event
* emitted by the input stream, and then will also complete.
*
* Marble diagram:
*
* ```text
* --a---b--c--d----|
* last()
* -----------------d|
* ```
*
* @return {Stream}
*/
last(): Stream<T>;
/**
* Prepends the given `initial` value to the sequence of events emitted by the
* input stream. The returned stream is a MemoryStream, which means it is
* already `remember()`'d.
*
* Marble diagram:
*
* ```text
* ---1---2-----3---
* startWith(0)
* 0--1---2-----3---
* ```
*
* @param initial The value or event to prepend.
* @return {MemoryStream}
*/
startWith(initial: T): MemoryStream<T>;
/**
* Uses another stream to determine when to complete the current stream.
*
* When the given `other` stream emits an event or completes, the output
* stream will complete. Before that happens, the output stream will behaves
* like the input stream.
*
* Marble diagram:
*
* ```text
* ---1---2-----3--4----5----6---
* endWhen( --------a--b--| )
* ---1---2-----3--4--|
* ```
*
* @param other Some other stream that is used to know when should the output
* stream of this operator complete.
* @return {Stream}
*/
endWhen(other: Stream<any>): Stream<T>;
/**
* "Folds" the stream onto itself.
*
* Combines events from the past throughout
* the entire execution of the input stream, allowing you to accumulate them
* together. It's essentially like `Array.prototype.reduce`. The returned
* stream is a MemoryStream, which means it is already `remember()`'d.
*
* The output stream starts by emitting the `seed` which you give as argument.
* Then, when an event happens on the input stream, it is combined with that
* seed value through the `accumulate` function, and the output value is
* emitted on the output stream. `fold` remembers that output value as `acc`
* ("accumulator"), and then when a new input event `t` happens, `acc` will be
* combined with that to produce the new `acc` and so forth.
*
* Marble diagram:
*
* ```text
* ------1-----1--2----1----1------
* fold((acc, x) => acc + x, 3)
* 3-----4-----5--7----8----9------
* ```
*
* @param {Function} accumulate A function of type `(acc: R, t: T) => R` that
* takes the previous accumulated value `acc` and the incoming event from the
* input stream and produces the new accumulated value.
* @param seed The initial accumulated value, of type `R`.
* @return {MemoryStream}
*/
fold<R>(accumulate: (acc: R, t: T) => R, seed: R): MemoryStream<R>;
/**
* Replaces an error with another stream.
*
* When (and if) an error happens on the input stream, instead of forwarding
* that error to the output stream, *replaceError* will call the `replace`
* function which returns the stream that the output stream will replicate.
* And, in case that new stream also emits an error, `replace` will be called
* again to get another stream to start replicating.
*
* Marble diagram:
*
* ```text
* --1---2-----3--4-----X
* replaceError( () => --10--| )
* --1---2-----3--4--------10--|
* ```
*
* @param {Function} replace A function of type `(err) => Stream` that takes
* the error that occurred on the input stream or on the previous replacement
* stream and returns a new stream. The output stream will behave like the
* stream that this function returns.
* @return {Stream}
*/
replaceError(replace: (err: any) => Stream<T>): Stream<T>;
/**
* Flattens a "stream of streams", handling only one nested stream at a time
* (no concurrency).
*
* If the input stream is a stream that emits streams, then this operator will
* return an output stream which is a flat stream: emits regular events. The
* flattening happens without concurrency. It works like this: when the input
* stream emits a nested stream, *flatten* will start imitating that nested
* one. However, as soon as the next nested stream is emitted on the input
* stream, *flatten* will forget the previous nested one it was imitating, and
* will start imitating the new nested one.
*
* Marble diagram:
*
* ```text
* --+--------+---------------
* \ \
* \ ----1----2---3--
* --a--b----c----d--------
* flatten
* -----a--b------1----2---3--
* ```
*
* @return {Stream}
*/
flatten<R>(this: Stream<Stream<R> | MemoryStream<R>>): Stream<R>;
/**
* Passes the input stream to a custom operator, to produce an output stream.
*
* *compose* is a handy way of using an existing function in a chained style.
* Instead of writing `outStream = f(inStream)` you can write
* `outStream = inStream.compose(f)`.
*
* @param {function} operator A function that takes a stream as input and
* returns a stream as well.
* @return {Stream}
*/
compose<U>(operator: (stream: Stream<T>) => U): U;
/**
* Returns an output stream that behaves like the input stream, but also
* remembers the most recent event that happens on the input stream, so that a
* newly added listener will immediately receive that memorised event.
*
* @return {MemoryStream}
*/
remember(): MemoryStream<T>;
debug(): Stream<T>;
debug(labelOrSpy: string): Stream<T>;
debug(labelOrSpy: (t: T) => any): Stream<T>;
/**
* *imitate* changes this current Stream to emit the same events that the
* `other` given Stream does. This method returns nothing.
*
* This method exists to allow one thing: **circular dependency of streams**.
* For instance, let's imagine that for some reason you need to create a
* circular dependency where stream `first$` depends on stream `second$`
* which in turn depends on `first$`:
*
* <!-- skip-example -->
* ```js
* import delay from 'xstream/extra/delay'
*
* var first$ = second$.map(x => x * 10).take(3);
* var second$ = first$.map(x => x + 1).startWith(1).compose(delay(100));
* ```
*
* However, that is invalid JavaScript, because `second$` is undefined
* on the first line. This is how *imitate* can help solve it:
*
* ```js
* import delay from 'xstream/extra/delay'
*
* var secondProxy$ = xs.create();
* var first$ = secondProxy$.map(x => x * 10).take(3);
* var second$ = first$.map(x => x + 1).startWith(1).compose(delay(100));
* secondProxy$.imitate(second$);
* ```
*
* We create `secondProxy$` before the others, so it can be used in the
* declaration of `first$`. Then, after both `first$` and `second$` are
* defined, we hook `secondProxy$` with `second$` with `imitate()` to tell
* that they are "the same". `imitate` will not trigger the start of any
* stream, it just binds `secondProxy$` and `second$` together.
*
* The following is an example where `imitate()` is important in Cycle.js
* applications. A parent component contains some child components. A child
* has an action stream which is given to the parent to define its state:
*
* <!-- skip-example -->
* ```js
* const childActionProxy$ = xs.create();
* const parent = Parent({...sources, childAction$: childActionProxy$});
* const childAction$ = parent.state$.map(s => s.child.action$).flatten();
* childActionProxy$.imitate(childAction$);
* ```
*
* Note, though, that **`imitate()` does not support MemoryStreams**. If we
* would attempt to imitate a MemoryStream in a circular dependency, we would
* either get a race condition (where the symptom would be "nothing happens")
* or an infinite cyclic emission of values. It's useful to think about
* MemoryStreams as cells in a spreadsheet. It doesn't make any sense to
* define a spreadsheet cell `A1` with a formula that depends on `B1` and
* cell `B1` defined with a formula that depends on `A1`.
*
* If you find yourself wanting to use `imitate()` with a
* MemoryStream, you should rework your code around `imitate()` to use a
* Stream instead. Look for the stream in the circular dependency that
* represents an event stream, and that would be a candidate for creating a
* proxy Stream which then imitates the target Stream.
*
* @param {Stream} target The other stream to imitate on the current one. Must
* not be a MemoryStream.
*/
imitate(target: Stream<T>): void;
/**
* Forces the Stream to emit the given value to its listeners.
*
* As the name indicates, if you use this, you are most likely doing something
* The Wrong Way. Please try to understand the reactive way before using this
* method. Use it only when you know what you are doing.
*
* @param value The "next" value you want to broadcast to all listeners of
* this Stream.
*/
shamefullySendNext(value: T): void;
/**
* Forces the Stream to emit the given error to its listeners.
*
* As the name indicates, if you use this, you are most likely doing something
* The Wrong Way. Please try to understand the reactive way before using this
* method. Use it only when you know what you are doing.
*
* @param {any} error The error you want to broadcast to all the listeners of
* this Stream.
*/
shamefullySendError(error: any): void;
/**
* Forces the Stream to emit the "completed" event to its listeners.
*
* As the name indicates, if you use this, you are most likely doing something
* The Wrong Way. Please try to understand the reactive way before using this
* method. Use it only when you know what you are doing.
*/
shamefullySendComplete(): void;
/**
* Adds a "debug" listener to the stream. There can only be one debug
* listener, that's why this is 'setDebugListener'. To remove the debug
* listener, just call setDebugListener(null).
*
* A debug listener is like any other listener. The only difference is that a
* debug listener is "stealthy": its presence/absence does not trigger the
* start/stop of the stream (or the producer inside the stream). This is
* useful so you can inspect what is going on without changing the behavior
* of the program. If you have an idle stream and you add a normal listener to
* it, the stream will start executing. But if you set a debug listener on an
* idle stream, it won't start executing (not until the first normal listener
* is added).
*
* As the name indicates, we don't recommend using this method to build app
* logic. In fact, in most cases the debug operator works just fine. Only use
* this one if you know what you're doing.
*
* @param {Listener<T>} listener
*/
setDebugListener(listener: Partial<Listener<T>> | null | undefined): void;
}
export declare class MemoryStream<T> extends Stream<T> {
private _v?;
private _has?;
constructor(producer: InternalProducer<T>);
_n(x: T): void;
_add(il: InternalListener<T>): void;
_stopNow(): void;
_x(): void;
map<U>(project: (t: T) => U): MemoryStream<U>;
mapTo<U>(projectedValue: U): MemoryStream<U>;
take(amount: number): MemoryStream<T>;
endWhen(other: Stream<any>): MemoryStream<T>;
replaceError(replace: (err: any) => Stream<T>): MemoryStream<T>;
remember(): MemoryStream<T>;
debug(): MemoryStream<T>;
debug(labelOrSpy: string): MemoryStream<T>;
debug(labelOrSpy: (t: T) => any): MemoryStream<T>;
}
export { NO, NO_IL };
declare const xs: typeof Stream;
declare type xs<T> = Stream<T>;
export default xs;