UNPKG

xstream

Version:

An extremely intuitive, small, and fast functional reactive stream library for JavaScript

1,654 lines 173 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.NO_IL = exports.NO = exports.MemoryStream = exports.Stream = void 0; var ponyfill_1 = require("symbol-observable/ponyfill"); var globalthis_1 = require("globalthis"); var $$observable = ponyfill_1.default(globalthis_1.getPolyfill()); var NO = {}; exports.NO = NO; function noop() { } function cp(a) { var l = a.length; var b = Array(l); for (var i = 0; i < l; ++i) b[i] = a[i]; return b; } function and(f1, f2) { return function andFn(t) { return f1(t) && f2(t); }; } function _try(c, t, u) { try { return c.f(t); } catch (e) { u._e(e); return NO; } } var NO_IL = { _n: noop, _e: noop, _c: noop, }; exports.NO_IL = NO_IL; // mutates the input function internalizeProducer(producer) { producer._start = function _start(il) { il.next = il._n; il.error = il._e; il.complete = il._c; this.start(il); }; producer._stop = producer.stop; } var StreamSub = /** @class */ (function () { function StreamSub(_stream, _listener) { this._stream = _stream; this._listener = _listener; } StreamSub.prototype.unsubscribe = function () { this._stream._remove(this._listener); }; return StreamSub; }()); var Observer = /** @class */ (function () { function Observer(_listener) { this._listener = _listener; } Observer.prototype.next = function (value) { this._listener._n(value); }; Observer.prototype.error = function (err) { this._listener._e(err); }; Observer.prototype.complete = function () { this._listener._c(); }; return Observer; }()); var FromObservable = /** @class */ (function () { function FromObservable(observable) { this.type = 'fromObservable'; this.ins = observable; this.active = false; } FromObservable.prototype._start = function (out) { this.out = out; this.active = true; this._sub = this.ins.subscribe(new Observer(out)); if (!this.active) this._sub.unsubscribe(); }; FromObservable.prototype._stop = function () { if (this._sub) this._sub.unsubscribe(); this.active = false; }; return FromObservable; }()); var Merge = /** @class */ (function () { function Merge(insArr) { this.type = 'merge'; this.insArr = insArr; this.out = NO; this.ac = 0; } Merge.prototype._start = function (out) { this.out = out; var s = this.insArr; var L = s.length; this.ac = L; for (var i = 0; i < L; i++) s[i]._add(this); }; Merge.prototype._stop = function () { var s = this.insArr; var L = s.length; for (var i = 0; i < L; i++) s[i]._remove(this); this.out = NO; }; Merge.prototype._n = function (t) { var u = this.out; if (u === NO) return; u._n(t); }; Merge.prototype._e = function (err) { var u = this.out; if (u === NO) return; u._e(err); }; Merge.prototype._c = function () { if (--this.ac <= 0) { var u = this.out; if (u === NO) return; u._c(); } }; return Merge; }()); var CombineListener = /** @class */ (function () { function CombineListener(i, out, p) { this.i = i; this.out = out; this.p = p; p.ils.push(this); } CombineListener.prototype._n = function (t) { var p = this.p, out = this.out; if (out === NO) return; if (p.up(t, this.i)) { var b = cp(p.vals); out._n(b); } }; CombineListener.prototype._e = function (err) { var out = this.out; if (out === NO) return; out._e(err); }; CombineListener.prototype._c = function () { var p = this.p; if (p.out === NO) return; if (--p.Nc === 0) p.out._c(); }; return CombineListener; }()); var Combine = /** @class */ (function () { function Combine(insArr) { this.type = 'combine'; this.insArr = insArr; this.out = NO; this.ils = []; this.Nc = this.Nn = 0; this.vals = []; } Combine.prototype.up = function (t, i) { var v = this.vals[i]; var Nn = !this.Nn ? 0 : v === NO ? --this.Nn : this.Nn; this.vals[i] = t; return Nn === 0; }; Combine.prototype._start = function (out) { this.out = out; var s = this.insArr; var n = this.Nc = this.Nn = s.length; var vals = this.vals = new Array(n); if (n === 0) { out._n([]); out._c(); } else { for (var i = 0; i < n; i++) { vals[i] = NO; s[i]._add(new CombineListener(i, out, this)); } } }; Combine.prototype._stop = function () { var s = this.insArr; var n = s.length; var ils = this.ils; for (var i = 0; i < n; i++) s[i]._remove(ils[i]); this.out = NO; this.ils = []; this.vals = []; }; return Combine; }()); var FromArray = /** @class */ (function () { function FromArray(a) { this.type = 'fromArray'; this.a = a; } FromArray.prototype._start = function (out) { var a = this.a; for (var i = 0, n = a.length; i < n; i++) out._n(a[i]); out._c(); }; FromArray.prototype._stop = function () { }; return FromArray; }()); var FromPromise = /** @class */ (function () { function FromPromise(p) { this.type = 'fromPromise'; this.on = false; this.p = p; } FromPromise.prototype._start = function (out) { var prod = this; this.on = true; this.p.then(function (v) { if (prod.on) { out._n(v); out._c(); } }, function (e) { out._e(e); }).then(noop, function (err) { setTimeout(function () { throw err; }); }); }; FromPromise.prototype._stop = function () { this.on = false; }; return FromPromise; }()); var Periodic = /** @class */ (function () { function Periodic(period) { this.type = 'periodic'; this.period = period; this.intervalID = -1; this.i = 0; } Periodic.prototype._start = function (out) { var self = this; function intervalHandler() { out._n(self.i++); } this.intervalID = setInterval(intervalHandler, this.period); }; Periodic.prototype._stop = function () { if (this.intervalID !== -1) clearInterval(this.intervalID); this.intervalID = -1; this.i = 0; }; return Periodic; }()); var Debug = /** @class */ (function () { function Debug(ins, arg) { this.type = 'debug'; this.ins = ins; this.out = NO; this.s = noop; this.l = ''; if (typeof arg === 'string') this.l = arg; else if (typeof arg === 'function') this.s = arg; } Debug.prototype._start = function (out) { this.out = out; this.ins._add(this); }; Debug.prototype._stop = function () { this.ins._remove(this); this.out = NO; }; Debug.prototype._n = function (t) { var u = this.out; if (u === NO) return; var s = this.s, l = this.l; if (s !== noop) { try { s(t); } catch (e) { u._e(e); } } else if (l) console.log(l + ':', t); else console.log(t); u._n(t); }; Debug.prototype._e = function (err) { var u = this.out; if (u === NO) return; u._e(err); }; Debug.prototype._c = function () { var u = this.out; if (u === NO) return; u._c(); }; return Debug; }()); var Drop = /** @class */ (function () { function Drop(max, ins) { this.type = 'drop'; this.ins = ins; this.out = NO; this.max = max; this.dropped = 0; } Drop.prototype._start = function (out) { this.out = out; this.dropped = 0; this.ins._add(this); }; Drop.prototype._stop = function () { this.ins._remove(this); this.out = NO; }; Drop.prototype._n = function (t) { var u = this.out; if (u === NO) return; if (this.dropped++ >= this.max) u._n(t); }; Drop.prototype._e = function (err) { var u = this.out; if (u === NO) return; u._e(err); }; Drop.prototype._c = function () { var u = this.out; if (u === NO) return; u._c(); }; return Drop; }()); var EndWhenListener = /** @class */ (function () { function EndWhenListener(out, op) { this.out = out; this.op = op; } EndWhenListener.prototype._n = function () { this.op.end(); }; EndWhenListener.prototype._e = function (err) { this.out._e(err); }; EndWhenListener.prototype._c = function () { this.op.end(); }; return EndWhenListener; }()); var EndWhen = /** @class */ (function () { function EndWhen(o, ins) { this.type = 'endWhen'; this.ins = ins; this.out = NO; this.o = o; this.oil = NO_IL; } EndWhen.prototype._start = function (out) { this.out = out; this.o._add(this.oil = new EndWhenListener(out, this)); this.ins._add(this); }; EndWhen.prototype._stop = function () { this.ins._remove(this); this.o._remove(this.oil); this.out = NO; this.oil = NO_IL; }; EndWhen.prototype.end = function () { var u = this.out; if (u === NO) return; u._c(); }; EndWhen.prototype._n = function (t) { var u = this.out; if (u === NO) return; u._n(t); }; EndWhen.prototype._e = function (err) { var u = this.out; if (u === NO) return; u._e(err); }; EndWhen.prototype._c = function () { this.end(); }; return EndWhen; }()); var Filter = /** @class */ (function () { function Filter(passes, ins) { this.type = 'filter'; this.ins = ins; this.out = NO; this.f = passes; } Filter.prototype._start = function (out) { this.out = out; this.ins._add(this); }; Filter.prototype._stop = function () { this.ins._remove(this); this.out = NO; }; Filter.prototype._n = function (t) { var u = this.out; if (u === NO) return; var r = _try(this, t, u); if (r === NO || !r) return; u._n(t); }; Filter.prototype._e = function (err) { var u = this.out; if (u === NO) return; u._e(err); }; Filter.prototype._c = function () { var u = this.out; if (u === NO) return; u._c(); }; return Filter; }()); var FlattenListener = /** @class */ (function () { function FlattenListener(out, op) { this.out = out; this.op = op; } FlattenListener.prototype._n = function (t) { this.out._n(t); }; FlattenListener.prototype._e = function (err) { this.out._e(err); }; FlattenListener.prototype._c = function () { this.op.inner = NO; this.op.less(); }; return FlattenListener; }()); var Flatten = /** @class */ (function () { function Flatten(ins) { this.type = 'flatten'; this.ins = ins; this.out = NO; this.open = true; this.inner = NO; this.il = NO_IL; } Flatten.prototype._start = function (out) { this.out = out; this.open = true; this.inner = NO; this.il = NO_IL; this.ins._add(this); }; Flatten.prototype._stop = function () { this.ins._remove(this); if (this.inner !== NO) this.inner._remove(this.il); this.out = NO; this.open = true; this.inner = NO; this.il = NO_IL; }; Flatten.prototype.less = function () { var u = this.out; if (u === NO) return; if (!this.open && this.inner === NO) u._c(); }; Flatten.prototype._n = function (s) { var u = this.out; if (u === NO) return; var _a = this, inner = _a.inner, il = _a.il; if (inner !== NO && il !== NO_IL) inner._remove(il); (this.inner = s)._add(this.il = new FlattenListener(u, this)); }; Flatten.prototype._e = function (err) { var u = this.out; if (u === NO) return; u._e(err); }; Flatten.prototype._c = function () { this.open = false; this.less(); }; return Flatten; }()); var Fold = /** @class */ (function () { function Fold(f, seed, ins) { var _this = this; this.type = 'fold'; this.ins = ins; this.out = NO; this.f = function (t) { return f(_this.acc, t); }; this.acc = this.seed = seed; } Fold.prototype._start = function (out) { this.out = out; this.acc = this.seed; out._n(this.acc); this.ins._add(this); }; Fold.prototype._stop = function () { this.ins._remove(this); this.out = NO; this.acc = this.seed; }; Fold.prototype._n = function (t) { var u = this.out; if (u === NO) return; var r = _try(this, t, u); if (r === NO) return; u._n(this.acc = r); }; Fold.prototype._e = function (err) { var u = this.out; if (u === NO) return; u._e(err); }; Fold.prototype._c = function () { var u = this.out; if (u === NO) return; u._c(); }; return Fold; }()); var Last = /** @class */ (function () { function Last(ins) { this.type = 'last'; this.ins = ins; this.out = NO; this.has = false; this.val = NO; } Last.prototype._start = function (out) { this.out = out; this.has = false; this.ins._add(this); }; Last.prototype._stop = function () { this.ins._remove(this); this.out = NO; this.val = NO; }; Last.prototype._n = function (t) { this.has = true; this.val = t; }; Last.prototype._e = function (err) { var u = this.out; if (u === NO) return; u._e(err); }; Last.prototype._c = function () { var u = this.out; if (u === NO) return; if (this.has) { u._n(this.val); u._c(); } else u._e(new Error('last() failed because input stream completed')); }; return Last; }()); var MapOp = /** @class */ (function () { function MapOp(project, ins) { this.type = 'map'; this.ins = ins; this.out = NO; this.f = project; } MapOp.prototype._start = function (out) { this.out = out; this.ins._add(this); }; MapOp.prototype._stop = function () { this.ins._remove(this); this.out = NO; }; MapOp.prototype._n = function (t) { var u = this.out; if (u === NO) return; var r = _try(this, t, u); if (r === NO) return; u._n(r); }; MapOp.prototype._e = function (err) { var u = this.out; if (u === NO) return; u._e(err); }; MapOp.prototype._c = function () { var u = this.out; if (u === NO) return; u._c(); }; return MapOp; }()); var Remember = /** @class */ (function () { function Remember(ins) { this.type = 'remember'; this.ins = ins; this.out = NO; } Remember.prototype._start = function (out) { this.out = out; this.ins._add(out); }; Remember.prototype._stop = function () { this.ins._remove(this.out); this.out = NO; }; return Remember; }()); var ReplaceError = /** @class */ (function () { function ReplaceError(replacer, ins) { this.type = 'replaceError'; this.ins = ins; this.out = NO; this.f = replacer; } ReplaceError.prototype._start = function (out) { this.out = out; this.ins._add(this); }; ReplaceError.prototype._stop = function () { this.ins._remove(this); this.out = NO; }; ReplaceError.prototype._n = function (t) { var u = this.out; if (u === NO) return; u._n(t); }; ReplaceError.prototype._e = function (err) { var u = this.out; if (u === NO) return; try { this.ins._remove(this); (this.ins = this.f(err))._add(this); } catch (e) { u._e(e); } }; ReplaceError.prototype._c = function () { var u = this.out; if (u === NO) return; u._c(); }; return ReplaceError; }()); var StartWith = /** @class */ (function () { function StartWith(ins, val) { this.type = 'startWith'; this.ins = ins; this.out = NO; this.val = val; } StartWith.prototype._start = function (out) { this.out = out; this.out._n(this.val); this.ins._add(out); }; StartWith.prototype._stop = function () { this.ins._remove(this.out); this.out = NO; }; return StartWith; }()); var Take = /** @class */ (function () { function Take(max, ins) { this.type = 'take'; this.ins = ins; this.out = NO; this.max = max; this.taken = 0; } Take.prototype._start = function (out) { this.out = out; this.taken = 0; if (this.max <= 0) out._c(); else this.ins._add(this); }; Take.prototype._stop = function () { this.ins._remove(this); this.out = NO; }; Take.prototype._n = function (t) { var u = this.out; if (u === NO) return; var m = ++this.taken; if (m < this.max) u._n(t); else if (m === this.max) { u._n(t); u._c(); } }; Take.prototype._e = function (err) { var u = this.out; if (u === NO) return; u._e(err); }; Take.prototype._c = function () { var u = this.out; if (u === NO) return; u._c(); }; return Take; }()); var Stream = /** @class */ (function () { function Stream(producer) { this._prod = producer || NO; this._ils = []; this._stopID = NO; this._dl = NO; this._d = false; this._target = null; this._err = NO; } Stream.prototype._n = function (t) { var a = this._ils; var L = a.length; if (this._d) this._dl._n(t); if (L == 1) a[0]._n(t); else if (L == 0) return; else { var b = cp(a); for (var i = 0; i < L; i++) b[i]._n(t); } }; Stream.prototype._e = function (err) { if (this._err !== NO) return; this._err = err; var a = this._ils; var L = a.length; this._x(); if (this._d) this._dl._e(err); if (L == 1) a[0]._e(err); else if (L == 0) return; else { var b = cp(a); for (var i = 0; i < L; i++) b[i]._e(err); } if (!this._d && L == 0) throw this._err; }; Stream.prototype._c = function () { var a = this._ils; var L = a.length; this._x(); if (this._d) this._dl._c(); if (L == 1) a[0]._c(); else if (L == 0) return; else { var b = cp(a); for (var i = 0; i < L; i++) b[i]._c(); } }; Stream.prototype._x = function () { if (this._ils.length === 0) return; if (this._prod !== NO) this._prod._stop(); this._err = NO; this._ils = []; }; Stream.prototype._stopNow = function () { // WARNING: code that calls this method should // first check if this._prod is valid (not `NO`) this._prod._stop(); this._err = NO; this._stopID = NO; }; Stream.prototype._add = function (il) { var ta = this._target; if (ta) return ta._add(il); var a = this._ils; a.push(il); if (a.length > 1) return; if (this._stopID !== NO) { clearTimeout(this._stopID); this._stopID = NO; } else { var p = this._prod; if (p !== NO) p._start(this); } }; Stream.prototype._remove = function (il) { var _this = this; var ta = this._target; if (ta) return ta._remove(il); var a = this._ils; var i = a.indexOf(il); if (i > -1) { a.splice(i, 1); if (this._prod !== NO && a.length <= 0) { this._err = NO; this._stopID = setTimeout(function () { return _this._stopNow(); }); } else if (a.length === 1) { this._pruneCycles(); } } }; // If all paths stemming from `this` stream eventually end at `this` // stream, then we remove the single listener of `this` stream, to // force it to end its execution and dispose resources. This method // assumes as a precondition that this._ils has just one listener. Stream.prototype._pruneCycles = function () { if (this._hasNoSinks(this, [])) this._remove(this._ils[0]); }; // Checks whether *there is no* path starting from `x` that leads to an end // listener (sink) in the stream graph, following edges A->B where B is a // listener of A. This means these paths constitute a cycle somehow. Is given // a trace of all visited nodes so far. Stream.prototype._hasNoSinks = function (x, trace) { if (trace.indexOf(x) !== -1) return true; else if (x.out === this) return true; else if (x.out && x.out !== NO) return this._hasNoSinks(x.out, trace.concat(x)); else if (x._ils) { for (var i = 0, N = x._ils.length; i < N; i++) if (!this._hasNoSinks(x._ils[i], trace.concat(x))) return false; return true; } else return false; }; Stream.prototype.ctor = function () { return this instanceof MemoryStream ? MemoryStream : Stream; }; /** * Adds a Listener to the Stream. * * @param {Listener} listener */ Stream.prototype.addListener = function (listener) { listener._n = listener.next || noop; listener._e = listener.error || noop; listener._c = listener.complete || noop; this._add(listener); }; /** * Removes a Listener from the Stream, assuming the Listener was added to it. * * @param {Listener<T>} listener */ Stream.prototype.removeListener = function (listener) { this._remove(listener); }; /** * Adds a Listener to the Stream returning a Subscription to remove that * listener. * * @param {Listener} listener * @returns {Subscription} */ Stream.prototype.subscribe = function (listener) { this.addListener(listener); return new StreamSub(this, listener); }; /** * Add interop between most.js and RxJS 5 * * @returns {Stream} */ Stream.prototype[$$observable] = function () { return this; }; /** * 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} */ Stream.create = function (producer) { if (producer) { if (typeof producer.start !== 'function' || typeof producer.stop !== 'function') throw new Error('producer requires both start and stop functions'); internalizeProducer(producer); // mutates the input } return new Stream(producer); }; /** * 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} */ Stream.createWithMemory = function (producer) { if (producer) internalizeProducer(producer); // mutates the input return new MemoryStream(producer); }; /** * Creates a Stream that does nothing when started. It never emits any event. * * Marble diagram: * * ```text * never * ----------------------- * ``` * * @factory true * @return {Stream} */ Stream.never = function () { return new Stream({ _start: noop, _stop: noop }); }; /** * Creates a Stream that immediately emits the "complete" notification when * started, and that's it. * * Marble diagram: * * ```text * empty * -| * ``` * * @factory true * @return {Stream} */ Stream.empty = function () { return new Stream({ _start: function (il) { il._c(); }, _stop: noop, }); }; /** * 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} */ Stream.throw = function (error) { return new Stream({ _start: function (il) { il._e(error); }, _stop: noop, }); }; /** * 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} */ Stream.from = function (input) { if (typeof input[$$observable] === 'function') return Stream.fromObservable(input); else if (typeof input.then === 'function') return Stream.fromPromise(input); else if (Array.isArray(input)) return Stream.fromArray(input); throw new TypeError("Type of input to from() must be an Array, Promise, or Observable"); }; /** * 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} */ Stream.of = function () { var items = []; for (var _i = 0; _i < arguments.length; _i++) { items[_i] = arguments[_i]; } return Stream.fromArray(items); }; /** * 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} */ Stream.fromArray = function (array) { return new Stream(new FromArray(array)); }; /** * 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} */ Stream.fromPromise = function (promise) { return new Stream(new FromPromise(promise)); }; /** * Converts an Observable into a Stream. * * @factory true * @param {any} observable The observable to be converted as a stream. * @return {Stream} */ Stream.fromObservable = function (obs) { if (obs.endWhen !== undefined) return obs; var o = typeof obs[$$observable] === 'function' ? obs[$$observable]() : obs; return new Stream(new FromObservable(o)); }; /** * 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} */ Stream.periodic = function (period) { return new Stream(new Periodic(period)); }; Stream.prototype._map = function (project) { return new (this.ctor())(new MapOp(project, this)); }; /** * 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} */ Stream.prototype.map = function (project) { return this._map(project); }; /** * 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} */ Stream.prototype.mapTo = function (projectedValue) { var s = this.map(function () { return projectedValue; }); var op = s._prod; op.type = 'mapTo'; return s; }; /** * Only allows events that pass the test given by the `passes` argument. * * Each event from the input stream is given to the `passes` function. If the * function returns `true`, the event is forwarded to the output stream, * otherwise it is ignored and not forwarded. * * Marble diagram: * * ```text * --1---2--3-----4-----5---6--7-8-- * filter(i => i % 2 === 0) * ------2--------4---------6----8-- * ``` * * @param {Function} passes A function of type `(t: T) => boolean` that takes * an event from the input stream and checks if it passes, by returning a * boolean. * @return {Stream} */ Stream.prototype.filter = function (passes) { var p = this._prod; if (p instanceof Filter) return new Stream(new Filter(and(p.f, passes), p.ins)); return new Stream(new Filter(passes, this)); }; /** * 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} */ Stream.prototype.take = function (amount) { return new (this.ctor())(new Take(amount, this)); }; /** * 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} */ Stream.prototype.drop = function (amount) { return new Stream(new Drop(amount, this)); }; /** * 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} */ Stream.prototype.last = function () { return new Stream(new Last(this)); }; /** * 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} */ Stream.prototype.startWith = function (initial) { return new MemoryStream(new StartWith(this, initial)); }; /** * 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} */ Stream.prototype.endWhen = function (other) { return new (this.ctor())(new EndWhen(other, this)); }; /** * "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} */ Stream.prototype.fold = function (accumulate, seed) { return new MemoryStream(new Fold(accumulate, seed, this)); }; /** * 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} */ Stream.prototype.replaceError = function (replace) { return new (this.ctor())(new ReplaceError(replace, this)); }; /** * 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} */ Stream.prototype.flatten = function () { return new Stream(new Flatten(this)); }; /** * 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} */ Stream.prototype.compose = function (operator) { return operator(this); }; /** * 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} */ Stream.prototype.remember = function () { return new MemoryStream(new Remember(this)); }; /** * Returns an output stream that identically behaves like the input stream, * but also runs a `spy` function for each event, to help you debug your app. * * *debug* takes a `spy` function as argument, and runs that for each event * happening on the input stream. If you don't provide the `spy` argument, * then *debug* will just `console.log` each event. This helps you to * understand the flow of events through some operator chain. * * Please note that if the output stream has no listeners, then it will not * start, which means `spy` will never run because no actual event happens in * that case. * * Marble diagram: * * ```text * --1----2-----3-----4-- * debug * --1----2-----3-----4-- * ``` * * @param {function} labelOrSpy A string to use as the label when printing * debug information on the console, or a 'spy' function that takes an event * as argument, and does not need to return anything. * @return {Stream} */ Stream.prototype.debug = function (labelOrSpy) { return new (this.ctor())(new Debug(this, labelOrSpy)); }; /** * *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. */ Stream.prototype.imitate = function (target) { if (target instanceof MemoryStream) throw new Error('A MemoryStream was given to imitate(), but it only ' + 'supports a Stream. Read more about this restriction here: ' + 'https://github.com/staltz/xstream#faq'); this._target = target; for (var ils = this._ils, N = ils.length, i = 0; i < N; i++) target._add(ils[i]); this._ils = []; }; /** * 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. */ Stream.prototype.shamefullySendNext = function (value) { this._n(value); }; /** * 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. */ Stream.prototype.shamefullySendError = function (error) { this._e(error); }; /** * 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. */ Stream.prototype.shamefullySendComplete = function () { this._c(); }; /** * 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 */ Stream.prototype.setDebugListener = function (listener) { if (!listener) { this._d = false; this._dl = NO; } else { this._d = true; listener._n = listener.next || noop; listener._e = listener.error || noop; listener._c = listener.complete || noop; this._dl = listener; } }; /** * 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} */ Stream.merge = function merge() { var streams = []; for (var _i = 0; _i < arguments.length; _i++) { streams[_i] = arguments[_i]; } return new Stream(new Merge(streams)); }; /** * 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 * -