@tempest/core
Version:
The core of the Tempest Stream Library
854 lines (818 loc) • 24.8 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.tempestCore = global.tempestCore || {})));
}(this, function (exports) { 'use strict';
function symbolObservablePonyfill(root) {
var result;
var Symbol = root.Symbol;
if (typeof Symbol === 'function') {
if (Symbol.observable) {
result = Symbol.observable;
} else {
result = Symbol('observable');
Symbol.observable = result;
}
} else {
result = '@@observable';
}
return result;
};
var root = undefined;
if (typeof global !== 'undefined') {
root = global;
} else if (typeof window !== 'undefined') {
root = window;
}
var result = symbolObservablePonyfill(root);
// append :: a -> [a] -> [a]
// a with x appended
function append(x, a) {
var l = a.length;
var b = new Array(l + 1);
for (var i = 0; i < l; ++i) {
b[i] = a[i];
}
b[l] = x;
return b;
}
// remove :: Int -> [a] -> [a]
// remove element at index
function remove(i, a) {
if (i < 0) {
throw new TypeError('i must be >= 0');
}
var l = a.length;
if (l === 0 || i >= l) {
return a;
}
if (l === 1) {
return [];
}
return unsafeRemove(i, a, l - 1);
}
// unsafeRemove :: Int -> [a] -> Int -> [a]
// Internal helper to remove element at index
function unsafeRemove(i, a, l) {
var b = new Array(l);
var j;
for (j = 0; j < i; ++j) {
b[j] = a[j];
}
for (j = i; j < l; ++j) {
b[j] = a[j + 1];
}
return b;
}
// removeAll :: (a -> boolean) -> [a] -> [a]
// remove all elements matching a predicate
function removeAll(f, a) {
var l = a.length;
var b = new Array(l);
var j = 0;
for (var x, i = 0; i < l; ++i) {
x = a[i];
if (!f(x)) {
b[j] = x;
++j;
}
}
b.length = j;
return b;
}
// findIndex :: a -> [a] -> Int
// find index of x in a, from the left
function findIndex(x, a) {
for (var i = 0, l = a.length; i < l; ++i) {
if (x === a[i]) {
return i;
}
}
return -1;
}
// isArrayLike :: * -> boolean
// Return true iff x is array-like
function isArrayLike(x) {
return x != null && typeof x.length === 'number' && typeof x !== 'function';
}
function tryEvent(time, value, sink) {
try {
sink.event(time, value);
}
catch (e) {
sink.error(time, e);
}
}
function tryEnd(time, value, sink) {
try {
sink.end(time, value);
}
catch (e) {
sink.error(time, e);
}
}
var None = function None () {};
None.prototype.event = function event (t, x) { return void 0; };
None.prototype.end = function end (t, x) { return void 0; };
None.prototype.error = function error (t, x) { return void 0; };
var NONE$1 = new None();
function none() {
return NONE$1;
}
function removeManyAt(i, sinks) {
var updated = remove(i, sinks);
// It's impossible to create a Many with 1 sink
// so we can't end up with updated.length === 0 here
return updated.length === 1 ? updated[0]
: new Many(updated);
}
function removeMany(sink, many) {
var sinks = many.sinks;
var i = findIndex(sink, sinks);
return i < 0 ? many : removeManyAt(i, sinks);
}
function addSink(sink, sinks) {
return sinks === NONE$1 ? sink
: sinks instanceof Many ? new Many(append(sink, sinks.sinks))
: new Many([sinks, sink]);
}
function removeSink(sink, sinks) {
return sinks === NONE$1 || sink === sinks ? NONE$1
: sinks instanceof Many ? removeMany(sink, sinks)
: sinks;
}
var Many = function Many(sinks) {
this.sinks = sinks;
};
Many.prototype.event = function event (t, x) {
var s = this.sinks;
for (var i = 0; i < s.length; ++i) {
tryEvent(t, x, s[i]);
}
};
Many.prototype.end = function end (t, x) {
var s = this.sinks;
for (var i = 0; i < s.length; ++i) {
tryEnd(t, x, s[i]);
}
};
Many.prototype.error = function error (t, x) {
var s = this.sinks;
for (var i = 0; i < s.length; ++i) {
s[i].error(t, x);
}
};
var MulticastTask = function MulticastTask(run) {
this._run = run;
this.active = true;
};
MulticastTask.create = function create (run) {
return new MulticastTask(run);
};
MulticastTask.prototype.run = function run (time) {
if (!this.active)
return;
this._run();
};
MulticastTask.prototype.error = function error (time, err) {
return void 0;
};
MulticastTask.prototype.dispose = function dispose () {
this.active = false;
};
var MulticastDisposable = function MulticastDisposable(source, sink, scheduler) {
this.source = source;
this.sink = sink;
this.scheduler = scheduler;
this.disposed = false;
};
MulticastDisposable.prototype.dispose = function dispose () {
if (this.disposed)
return;
this.disposed = true;
var source = this.source;
var remaining = source._remove(this.sink);
if (remaining === 0) {
var task = MulticastTask.create(function () { return source._dispose(); });
source._stopId = this.scheduler.asap(task);
}
};
var EMPTY = { dispose: function dispose$1() { return void 0; } };
var NONE = null;
var Multicast = function Multicast(source) {
this.activeCount = 0;
this.source = source;
this.sink = none();
this.disposable = EMPTY;
this._stopId = NONE;
};
Multicast.prototype.run = function run (sink, scheduler) {
var n = this._add(sink);
if (n === 1) {
if (this._stopId !== NONE) {
scheduler.cancel(this._stopId);
this._stopId = NONE;
}
if (this.disposable === EMPTY) {
this.disposable = this.source.run(this, scheduler);
}
}
return new MulticastDisposable(this, sink, scheduler);
};
Multicast.prototype._dispose = function _dispose () {
var disposable = this.disposable;
this.disposable = EMPTY;
this._stopId = NONE;
Promise.resolve(disposable).then(dispose);
};
Multicast.prototype._add = function _add (sink) {
this.sink = addSink(sink, this.sink);
this.activeCount += 1;
return this.activeCount;
};
Multicast.prototype._remove = function _remove (sink) {
var s = this.sink;
this.sink = removeSink(sink, this.sink);
if (s !== this.sink) {
this.activeCount -= 1;
}
return this.activeCount;
};
Multicast.prototype.event = function event (time, value) {
this.sink.event(time, value);
};
Multicast.prototype.end = function end (time, value) {
this.sink.end(time, value);
};
Multicast.prototype.error = function error (time, err) {
this.sink.error(time, err);
};
function dispose(disposable) {
disposable.dispose();
}
var PredeterminedTask = function PredeterminedTask(delay, period, task, scheduler) {
this.time = delay;
this.period = period;
this.task = task;
this.scheduler = scheduler;
this.active = true;
};
PredeterminedTask.prototype.run = function run () {
this.task.run(this.time);
};
PredeterminedTask.prototype.error = function error (err) {
this.task.error(this.time, err);
};
PredeterminedTask.prototype.dispose = function dispose () {
this.scheduler.cancel(this);
return this.task.dispose();
};
function defer(task) {
return Promise.resolve(task).then(runTask);
}
function runTask(task) {
try {
return task.run(Date.now());
}
catch (e) {
return task.error(Date.now(), e);
}
}
function runScheduledTask(task) {
try {
task.run();
}
catch (e) {
task.error(e);
}
}
var TaskScheduler = function TaskScheduler(timer, timeline) {
this.timer = timer;
this.timeline = timeline;
this._timer = null;
this._nextArrival = Infinity;
var self = this;
this._runReadyTasksBound = function () { return self._runReadyTasks(self.now()); };
};
TaskScheduler.prototype.now = function now () {
return this.timer.now();
};
TaskScheduler.prototype.asap = function asap (task) {
return this.schedule(0, -1, task);
};
TaskScheduler.prototype.delay = function delay (delay, task) {
return this.schedule(delay, -1, task);
};
TaskScheduler.prototype.periodic = function periodic (period, task) {
return this.schedule(0, period, task);
};
TaskScheduler.prototype.schedule = function schedule (delay, period, task) {
var time = this.now();
var st = new PredeterminedTask(+(time) + Math.max(0, delay), period, task, this);
this.timeline.add(st);
this._scheduleNextRun(+(time));
return st;
};
TaskScheduler.prototype.cancel = function cancel (task) {
task.active = false;
if (this.timeline.remove(task)) {
this._reschedule();
}
};
TaskScheduler.prototype.cancelAll = function cancelAll (f) {
this.timeline.removeAll(f);
this._reschedule();
};
TaskScheduler.prototype._reschedule = function _reschedule () {
if (this.timeline.isEmpty()) {
this._unschedule();
}
else {
this._scheduleNextRun(this.now());
}
};
TaskScheduler.prototype._unschedule = function _unschedule () {
this.timer.clearTimer(this._timer);
this._timer = null;
};
TaskScheduler.prototype._scheduleNextRun = function _scheduleNextRun (time) {
if (this.timeline.isEmpty())
return;
var nextArrival = this.timeline.nextArrival();
if (this._timer === null) {
this._scheduleNextArrival(nextArrival, time);
}
else if (nextArrival < this._nextArrival) {
this._unschedule();
this._scheduleNextArrival(nextArrival, time);
}
};
TaskScheduler.prototype._scheduleNextArrival = function _scheduleNextArrival (nextArrival, time) {
this._nextArrival = nextArrival;
var delay = Math.max(0, nextArrival - time);
this._timer = this.timer.setTimer(this._runReadyTasksBound, delay);
};
TaskScheduler.prototype._runReadyTasks = function _runReadyTasks (time) {
this._timer = null;
this.timeline.runTasks(time, runScheduledTask);
this._scheduleNextRun(this.now());
};
var ClockTimer = function ClockTimer () {};
ClockTimer.prototype.now = function now () {
return Date.now();
};
ClockTimer.prototype.setTimer = function setTimer (fn, delayTime) {
return delayTime <= 0
? runAsTask(fn)
: setTimeout(fn, delayTime);
};
ClockTimer.prototype.clearTimer = function clearTimer (task) {
return task instanceof Asap
? task.dispose()
: clearTimeout(task);
};
var Asap = function Asap(f) {
this.f = f;
this.active = true;
};
Asap.prototype.run = function run (time) {
if (this.active)
this.f();
};
Asap.prototype.error = function error (time, e) {
throw e;
};
Asap.prototype.dispose = function dispose () {
this.active = false;
};
function runAsTask(f) {
var task = new Asap(f);
defer(task);
return task;
}
var BinaryTimeline = function BinaryTimeline() {
this.tasks = [];
};
BinaryTimeline.prototype.nextArrival = function nextArrival () {
return this.isEmpty()
? Infinity
: this.tasks[0].time;
};
BinaryTimeline.prototype.isEmpty = function isEmpty () {
return this.tasks.length === 0;
};
BinaryTimeline.prototype.add = function add (task) {
insertByTime(task, this.tasks);
};
BinaryTimeline.prototype.remove = function remove (task) {
var i = binarySearch(task.time, this.tasks);
if (i >= 0 && i < this.tasks.length) {
var at = findIndex(task, this.tasks[i].events);
if (at >= 0) {
this.tasks[i].events.splice(at, 1);
return true;
}
}
return false;
};
BinaryTimeline.prototype.removeAll = function removeAll$1 (f) {
var this$1 = this;
for (var i = 0, l = this.tasks.length; i < l; ++i) {
removeAllFrom(f, this$1.tasks[i]);
}
};
BinaryTimeline.prototype.runTasks = function runTasks$1 (time, runTask) {
var this$1 = this;
var tasks = this.tasks;
var l = tasks.length;
var i = 0;
while (i < l && tasks[i].time <= time) {
++i;
}
this.tasks = tasks.slice(i);
for (var j = 0; j < i; ++j) {
this$1.tasks = runTasks(runTask, tasks[j], this$1.tasks);
}
};
function runTasks(runTask, timeslot, tasks) {
var events = timeslot.events;
for (var i = 0; i < events.length; ++i) {
var task = events[i];
if (task.active) {
runTask(task);
if (task.period >= 0 && task.active) {
task.time = task.time + task.period;
insertByTime(task, tasks);
}
}
}
return tasks;
}
function insertByTime(task, timeslots) {
var l = timeslots.length;
if (l === 0) {
timeslots.push(BinaryTimeslot.create(task.time, [task]));
return;
}
var i = binarySearch(task.time, timeslots);
if (i >= l) {
timeslots.push(BinaryTimeslot.create(task.time, [task]));
}
else if (task.time === timeslots[i].time) {
timeslots[i].events.push(task);
}
else {
timeslots.splice(i, 0, BinaryTimeslot.create(task.time, [task]));
}
}
function removeAllFrom(f, timeslot) {
timeslot.events = removeAll(f, timeslot.events);
}
function binarySearch(time, sortedArray) {
var lo = 0;
var hi = sortedArray.length;
var mid;
var y;
while (lo < hi) {
mid = Math.floor((lo + hi) / 2);
y = sortedArray[mid];
if (time === y.time) {
return mid;
}
else if (time < y.time) {
hi = mid;
}
else {
lo = mid + 1;
}
return hi;
}
}
var BinaryTimeslot = function BinaryTimeslot(time, events) {
this.time = time;
this.events = events;
};
BinaryTimeslot.create = function create (time, events) {
return new BinaryTimeslot(time, events);
};
var defaultScheduler = new TaskScheduler(new ClockTimer(), new BinaryTimeline());
var BasicSubscription = function BasicSubscription(source, sink) {
this.source = source;
this.sink = sink;
this.disposable = source.run(sink, defaultScheduler);
};
BasicSubscription.create = function create (source, sink) {
return new BasicSubscription(source, sink);
};
BasicSubscription.prototype.unsubscribe = function unsubscribe () {
this.disposable.dispose();
};
var SubscriberSink = function SubscriberSink(_next, _error, _complete) {
this._next = _next;
this._error = _error;
this._complete = _complete;
};
SubscriberSink.create = function create (next, error, complete) {
return new SubscriberSink(next, error, complete);
};
SubscriberSink.prototype.event = function event (t, x) {
var ref = this;
var _next = ref._next;
_next(x);
};
SubscriberSink.prototype.error = function error (t, e) {
var ref = this;
var _error = ref._error;
_error(e);
};
SubscriberSink.prototype.end = function end (t, x) {
var ref = this;
var _complete = ref._complete;
_complete(x);
};
function fatalError(err) {
setTimeout(function () { throw err; }, 0);
}
var PropagateTask = function PropagateTask(run, value, sink) {
this._run = run;
this.value = value;
this.sink = sink;
this.active = true;
};
PropagateTask.event = function event$1 (value, sink) {
return new PropagateTask(event, value, sink);
};
PropagateTask.error = function error$1 (err, sink) {
return new PropagateTask(error, err, sink);
};
PropagateTask.end = function end$1 (value, sink) {
return new PropagateTask(end, value, sink);
};
PropagateTask.prototype.run = function run (time) {
if (!this.active)
return;
this._run(time, this.value, this.sink);
};
PropagateTask.prototype.error = function error$2 (time, err) {
if (!this.active)
fatalError(err);
this.active = false;
this.sink.error(time, err);
};
PropagateTask.prototype.dispose = function dispose () {
this.active = false;
};
function event(time, value, sink) {
sink.event(time, value);
}
function error(time, err, sink) {
sink.error(time, err);
}
function end(time, value, sink) {
sink.end(time, value);
}
var FromArraySource = function FromArraySource(array) {
this.array = array;
};
FromArraySource.prototype.run = function run (sink, scheduler) {
var task = scheduler.asap(new PropagateTask(runArrayTask(this.array, scheduler), void 0, sink));
return { dispose: function () { return task.dispose(); } };
};
function runArrayTask(array, scheduler) {
return function arrayTask(time, value, sink) {
array.forEach(function (x) { return sink.event(scheduler.now(), x); });
sink.end(scheduler.now(), void 0);
};
}
var FromObservableSource = function FromObservableSource(observable) {
this.observable = observable;
};
FromObservableSource.prototype.run = function run (sink, scheduler) {
var next = function (x) { return sink.event(scheduler.now(), x); };
var error = function (e) { return sink.error(scheduler.now(), e); };
var complete = function (x) { return sink.end(scheduler.now(), x); };
var subscription = this.observable.subscribe({ next: next, error: error, complete: complete });
return { dispose: function () { return subscription.unsubscribe(); } };
};
var Stream = function Stream(source) {
this.source = new Multicast(source);
};
Stream.from = function from (input) {
if (typeof input[result] === 'function') {
return new Stream(new FromObservableSource(input));
}
else if (isArrayLike(input)) {
return new Stream(new FromArraySource(input));
}
else {
throw new Error('Stream can only be made from an Observable or ArrayLike object');
}
};
Stream.of = function of () {
var items = [], len = arguments.length;
while ( len-- ) items[ len ] = arguments[ len ];
return Stream.from(items);
};
Stream.prototype.subscribe = function subscribe (nextOrSubscriber, error, complete) {
var _next = Function.prototype;
var _error = Function.prototype;
var _complete = Function.prototype;
if (nextOrSubscriber !== null && typeof nextOrSubscriber === 'object') {
var subscriber = nextOrSubscriber;
var next = subscriber.next;
var error$1 = subscriber.error;
var complete$1 = subscriber.complete;
if (next && typeof next === 'function')
_next = next;
if (error$1 && typeof error$1 === 'function')
_error = error$1;
if (complete$1 && typeof complete$1 === 'function')
_complete = complete$1;
}
else if (typeof nextOrSubscriber === 'function') {
_next = nextOrSubscriber;
if (error && typeof error === 'function')
_error = error;
if (complete && typeof complete === 'function')
_complete = complete;
}
return BasicSubscription.create(this.source, SubscriberSink.create(_next, _error, _complete));
};
Stream.prototype[result] = function () {
return this;
};
function withDefaultScheduler(f, source) {
return withScheduler(f, source, defaultScheduler);
}
function withScheduler(f, source, scheduler) {
return new Promise(function (resolve, reject) {
runSource(f, source, scheduler, resolve, reject);
});
}
function runSource(f, source, scheduler, end, error) {
var disposable = new SettableDisposable();
var observer = new Drain(f, end, error, disposable);
disposable.setDisposable(source.run(observer, scheduler));
}
var SettableDisposable = function SettableDisposable() {
this.disposable = void 0;
this.disposed = false;
this._resolve = void 0;
var self = this;
this._result = new Promise(function (resolve) {
self._resolve = resolve;
});
};
SettableDisposable.prototype.dispose = function dispose () {
if (this.disposed)
return this._result;
this.disposed = true;
if (this.disposable) {
this._result = this.disposable.dispose();
}
return this._result;
};
SettableDisposable.prototype.setDisposable = function setDisposable (disposable) {
if (this.disposable !== void 0) {
throw new Error('Disposable can only be set one time');
}
this.disposable = disposable;
if (this.disposed) {
this._resolve(disposable.dispose());
}
};
var Drain = function Drain(_event, _end, _error, disposable) {
this._event = _event;
this._end = _end;
this._error = _error;
this.disposable = disposable;
this.active = true;
};
Drain.prototype.event = function event (time, value) {
if (!this.active)
return;
this._event(value);
};
Drain.prototype.error = function error (time, err) {
if (!this.active)
return;
this.active = false;
disposeThen(this._error, this._error, this.disposable, err);
};
Drain.prototype.end = function end (time, value) {
if (!this.active)
return;
this.active = false;
disposeThen(this._error, this._error, this.disposable, value);
};
function disposeThen(end, error, disposable, value) {
Promise.resolve(disposable.dispose()).then(function () {
end(value);
}, error);
}
function getSource(stream) {
return stream.source instanceof Multicast
? stream.source.source
: stream.source;
}
var IndexSink = function IndexSink(index, sink) {
this.index = index;
this.sink = sink;
this.active = true;
this.value = void 0;
};
IndexSink.prototype.event = function event (time, value) {
if (!this.active)
return;
this.value = value;
this.sink.event(time, { index: this.index, value: this.value });
};
IndexSink.prototype.error = function error (time, err) {
this.sink.error(time, err);
};
IndexSink.prototype.end = function end (time, value) {
if (!this.active)
return;
this.active = false;
this.sink.end(time, { index: this.index, value: value });
};
/**
* Takes a function with 1 argument and returns a curried version of it
*
* @export
* @template A
* @template B
* @param {(a: A) => B} f
* @returns {OneMore<A, B>}
*/
function curry1(f) {
function curried(a) {
switch (arguments.length) {
case 0: return curried;
case 1: return f(a);
default: return curried;
}
}
return curried;
}
/**
* Takes a function with 2 arguments and returns a curried version of it
*
* @export
* @template A
* @template B
* @template C
* @param {(a: A, b: B) => C} f
* @returns {TwoMore<A, B, C>}
*/
function curry2(f) {
function curried(a, b) {
switch (arguments.length) {
case 0: return curried;
case 1: return curry1(function (b) { return f(a, b); });
case 2: return f(a, b);
default: return curried;
}
}
return curried;
}
/**
* Takes a function with 3 arguments and returns a curried version
*
* @export
* @template A
* @template B
* @template C
* @template D
* @param {(a: A, b: B, c: C) => D} f
* @returns {ThreeMore<A, B, C, D>}
*/
function curry3(f) {
function curried(a, b, c) {
switch (arguments.length) {
case 0: return curried;
case 1: return curry2(function (b, c) { return f(a, b, c); });
case 2: return curry1(function (c) { return f(a, b, c); });
case 3: return f(a, b, c);
default: return curried;
}
}
return curried;
}
exports.Stream = Stream;
exports.defaultScheduler = defaultScheduler;
exports.PropagateTask = PropagateTask;
exports.runSource = runSource;
exports.withScheduler = withScheduler;
exports.withDefaultScheduler = withDefaultScheduler;
exports.getSource = getSource;
exports.BasicSubscription = BasicSubscription;
exports.SubscriberSink = SubscriberSink;
exports.IndexSink = IndexSink;
exports.Multicast = Multicast;
exports.curry1 = curry1;
exports.curry2 = curry2;
exports.curry3 = curry3;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=tempest-core.js.map