UNPKG

cycle-restart

Version:

Restart a Cycle.js application and preserve state.

380 lines (295 loc) 10.8 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; exports.default = restartable; var _xstream = require('xstream'); var _xstream2 = _interopRequireDefault(_xstream); require('get-own-property-symbols'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function pausable(pause$) { return function (stream) { return pause$.map(function (paused) { return stream.filter(function () { return paused; }); }).flatten(); }; } function disposeAllStreams(streams) { keys(streams).forEach(function (key) { var value = streams[key]; delete streams[key]; value && value.dispose && value.dispose(); }); } function makeDispose(_ref, originalDispose, context) { var streams = _ref.streams; return function dispose() { originalDispose.bind(context)(); disposeAllStreams(streams); }; } function record(_ref2, streamToRecord, identifier) { var streams = _ref2.streams, addLogEntry = _ref2.addLogEntry, pause$ = _ref2.pause$, Time = _ref2.Time; var stream = streamToRecord.compose(pausable(pause$.startWith(true))).map(function (event) { if (typeof event.subscribe === 'function') { var eventStream = event.debug(function (innerEvent) { var response$ = _xstream2.default.of(innerEvent); response$.request = event.request; addLogEntry({ event: response$, time: Time._time(), identifier: identifier, stream: stream }); }); eventStream.request = event.request; return eventStream; } addLogEntry({ event: event, time: Time._time(), identifier: identifier, stream: stream }); // TODO - stream is undefined and unused return event; }); streams[identifier] = stream; return stream.debug(function () {}); } function wrapSourceFunction(_ref3, name, f, context) { var streams = _ref3.streams, addLogEntry = _ref3.addLogEntry, pause$ = _ref3.pause$, Time = _ref3.Time; var scope = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : []; return function newSource() { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var newScope = scope.concat(args); var returnValue = f.bind.apply(f, [context].concat(args))(); /* Not all names will be strings, some may be symbols. */ if (typeof name === 'string' && name.indexOf('isolate') !== -1 || (typeof returnValue === 'undefined' ? 'undefined' : _typeof(returnValue)) !== 'object') { return returnValue; } if (typeof returnValue.addListener !== 'function') { return wrapSource({ streams: streams, addLogEntry: addLogEntry, pause$: pause$, Time: Time }, returnValue, newScope); } var identifier = newScope.join('/'); return record({ streams: streams, addLogEntry: addLogEntry, pause$: pause$, Time: Time }, returnValue, identifier); }; } function keys(obj) { var _keys = []; for (var prop in obj) { _keys.push(prop); } /* Expose all symbol properties as keys. */ var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = Object.getOwnPropertySymbols(obj)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _prop = _step.value; _keys.push(_prop); } /* Expose all prototype sybmol properties as keys. */ } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = Object.getOwnPropertySymbols(Object.getPrototypeOf(obj) || {})[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var _prop2 = _step2.value; _keys.push(_prop2); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } return _keys; } function wrapSource(_ref4, source) { var streams = _ref4.streams, addLogEntry = _ref4.addLogEntry, pause$ = _ref4.pause$, Time = _ref4.Time; var scope = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; var returnValue = {}; keys(source).forEach(function (key) { var value = source[key]; if (key === 'dispose') { returnValue[key] = makeDispose({ streams: streams }, value, source); } else if (typeof value === 'function') { returnValue[key] = wrapSourceFunction({ streams: streams, addLogEntry: addLogEntry, pause$: pause$, Time: Time }, key, value, returnValue, scope); } else { returnValue[key] = value; } }); return returnValue; } var subscribe = function subscribe(f) { return { next: f, error: function error(err) { return console.error(err); }, complete: function complete() {} }; }; function createLog$() { var logEntry$ = _xstream2.default.create(); var logEntryReducer$ = logEntry$.map(function (entry) { return function (log) { return log.concat(entry); }; }); var logReplace$ = _xstream2.default.create(); var logReplaceReducer$ = logReplace$.map(function (newLog) { return function () { return newLog; }; }); var logReducer$ = _xstream2.default.merge(logEntryReducer$, logReplaceReducer$); var log$ = logReducer$.fold(function (log, reducer) { return reducer(log); }, []); function addLogEntry(entry) { logEntry$.shamefullySendNext(entry); } function replaceLog(newLog) { logReplace$.shamefullySendNext(newLog); } return { log$: log$, addLogEntry: addLogEntry, replaceLog: replaceLog }; } function restartable(driver) { var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var _createLog$ = createLog$(), log$ = _createLog$.log$, addLogEntry = _createLog$.addLogEntry, replaceLog = _createLog$.replaceLog; // TODO - dispose log subscription log$.addListener(subscribe(function () {})); var streams = {}; var pauseSinksWhileReplaying = opts.pauseSinksWhileReplaying === undefined ? true : opts.pauseSinksWhileReplaying; var pause$ = (opts.pause$ || _xstream2.default.empty()).startWith(true); var replayOnlyLastSink = opts.replayOnlyLastSink || false; var replaying = void 0; var isReplaying = function isReplaying() { return replaying; }; var setReplaying = function setReplaying(newReplaying) { return replaying = newReplaying; }; var finishedReplay$ = _xstream2.default.create(); function restartableDriver(sink$, Time) { var filteredSink$ = _xstream2.default.create(); var lastSinkEvent$ = _xstream2.default.createWithMemory(); if (sink$) { if (isReplaying() && replayOnlyLastSink) { lastSinkEvent$.map(function (lastEvent) { return finishedReplay$.mapTo(lastEvent); }).flatten().take(1).addListener(subscribe(function (event) { filteredSink$.shamefullySendNext(event); })); } if (pauseSinksWhileReplaying) { sink$.compose(pausable(pause$)).filter(function () { return !isReplaying(); }).addListener({ next: function next(ev) { return filteredSink$.shamefullySendNext(ev); }, error: function error(err) { return console.error(err); }, complete: function complete() {} }); } else { sink$.compose(pausable(pause$)).addListener(subscribe(function (ev) { return filteredSink$.shamefullySendNext(ev); })); } } if (sink$) { // force sink$ to a normal stream with .filter() to prevent attempting // imitation of a MemoryStream which can't be imitated lastSinkEvent$.imitate(sink$.filter(function () { return true; })); } var source = driver(filteredSink$); var returnValue = void 0; if (source === undefined || source === null) { return source; } else if (typeof source.addListener === 'function') { returnValue = record({ streams: streams, addLogEntry: addLogEntry, pause$: pause$, Time: Time }, source, ':root'); } else { returnValue = wrapSource({ streams: streams, addLogEntry: addLogEntry, pause$: pause$, Time: Time }, source); } var oldReturnValueDispose = source.dispose; returnValue.dispose = function () { oldReturnValueDispose && oldReturnValueDispose.bind(returnValue)(); sink$ && sink$.dispose && sink$.dispose(); disposeAllStreams(streams); }; returnValue.log$ = log$; return returnValue; } function replayable(driver) { driver.onPreReplay = function () { setReplaying(true); }; driver.replayLog = function (schedule, newLog$) { var timeToResetTo = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; var logToReplaceWith = void 0; newLog$.take(1).addListener({ next: function next(newLog) { logToReplaceWith = newLog; function scheduleEvent(historicEvent) { var stream = streams[historicEvent.identifier]; if (stream) { schedule.next(stream, historicEvent.time, historicEvent.event); } else { console.error('Missing replay stream ', historicEvent.identifier); } } newLog.filter(function (event) { return timeToResetTo === null || event.time <= timeToResetTo; }).forEach(scheduleEvent); }, error: function error(err) { console.error(err); }, complete: function complete() { replaceLog(logToReplaceWith); } }); }; driver.onPostReplay = function () { setReplaying(false); finishedReplay$.shamefullySendNext(); }; return driver; } return replayable(restartableDriver); }