UNPKG

fluorine-orchestra

Version:
307 lines (231 loc) 9.57 kB
import _getIterator from 'babel-runtime/core-js/get-iterator'; import _extends from 'babel-runtime/helpers/extends'; import _Object$keys from 'babel-runtime/core-js/object/keys'; import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; import _Symbol from 'babel-runtime/core-js/symbol'; import invariant from 'invariant'; import { Observable } from 'rxjs'; import { Collection } from './Collection'; import { Store } from './Store'; import combineStores from './util/combineStores'; import isDispatcher from 'fluorine-lib/lib/util/isDispatcher'; import { Iterable, Set } from 'immutable'; var resultCache = _Symbol('resultCache'); function _ref(acc, store) { var identifier = store.identifier; if (acc[identifier]) { throw new Error('Orchestra: The identifier `' + identifier + '` is not unique.'); } acc[identifier] = store; return acc; } function _ref2(debounceTime) { invariant(typeof debounceTime === 'number' && debounceTime > 0, 'Orchestra: Expected `debounceTime` to be a number and > 0.'); this.opts.debounceTime = debounceTime; } function _ref3(identifier, reducer) { invariant(typeof identifier === 'string', 'Orchestra: `identifier` is expected to be a string.'); invariant(typeof reducer === 'function', 'Orchestra: `reducer` is expected to be a reducer function.'); var externals = this.externals; var stores = this.stores; invariant(!stores.hasOwnProperty(identifier), 'Orchesta: The identifier `' + identifier + '` is already taken by a Store.'); invariant(!externals.hasOwnProperty(identifier), 'Orchesta: The identifier `' + identifier + '` is not unique.'); externals[identifier] = reducer; return this; } function _ref4(dispatcher) { invariant(isDispatcher(dispatcher), 'Orchestra: `dispatcher` is expected to be a Fluorine dispatcher.'); if (this[resultCache][dispatcher.identifier]) { return this[resultCache][dispatcher.identifier]; } var stores = this.stores; var externals = this.externals; var connections = this.connections; var debounceTime = this.opts.debounceTime; var _externals = _Object$keys(externals).reduce(function (acc, key) { var external = dispatcher.reduce(externals[key]); if (debounceTime) { external = external.debounceTime(debounceTime); } acc[key] = external; return acc; }, {}); var _stores = {}; var resolveStore = function resolveStore(store) { var visited = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var identifier = store.identifier; if (visited[identifier]) { throw new Error('Orchestra: Failed to resolve circular dependency for identifier `' + identifier + '`.'); } visited[identifier] = true; if (_stores[identifier]) { return _stores[identifier]; } var reducer = store.getReducer(); var dependencies = store.getDependencies(); var dependencyIdentifiers = _Object$keys(dependencies); var post = store.getPost(); var _dependencies = dependencyIdentifiers.reduce(function (acc, dependency) { var _dependency = void 0; if (_externals[dependency]) { _dependency = _externals[dependency]; } else if (_stores[dependency]) { _dependency = _stores[dependency]; } else if (stores[dependency]) { _dependency = resolveStore(stores[dependency], visited); } else { throw new Error('Orchestra: Failed to resolve dependency for identifier `' + dependency + '`.'); } acc[dependency] = _dependency; return acc; }, {}); var _store = dispatcher.reduce(reducer); if (debounceTime) { _store = _store.debounceTime(debounceTime); } // The resulting observable store var res = void 0; function _ref5(state, dependencyState, dependencyIdentifier) { var _dependencies$depende = dependencies[dependencyIdentifier]; var getter = _dependencies$depende.getter; var setter = _dependencies$depende.setter; // If getter exists we need to resolve dependencies per ids if (getter && typeof getter === 'function') { var _ret2 = function () { var missingIds = []; var nextState = state.map(function (x) { var ids = getter(x); var result = undefined; if (typeof ids === 'string') { result = dependencyState.get(ids); if (result === undefined) { missingIds = missingIds.concat(ids); return x; } } else if (Iterable.isIterable(ids) || Array.isArray(ids)) { invariant(typeof ids.forEach === 'function', 'Orchestra: `ids` is expected to have a method `forEach`.'); var _store2 = stores[dependencyIdentifier]; var collection = _store2 ? _store2.createCollection() : new Collection(); result = collection.withMutations(function (map) { ids.forEach(function (id) { var item = dependencyState.get(id); if (item === undefined) { missingIds.push(id); } map.set(id, item); }); }); } return setter(x, result); }); // Report missing items for ids var dependencyStore = stores[dependencyIdentifier]; if (dependencyStore) { dependencyStore._missing(new Set(missingIds), identifier); } return { v: nextState }; }(); if (typeof _ret2 === "object") return _ret2.v; } // If there is no getter we just map over the items with the setter only return state.map(function (x) { return setter(x, dependencyState); }); } if (dependencies.length === 0) { // Bail early if there are no dependencies for the store res = _store; } else { (function () { var _extends2; var applyDependency = _ref5; var lastDeps = null; res = combineStores(_extends({}, _dependencies, (_extends2 = {}, _extends2[identifier] = _store, _extends2))).scan(function (pastResult, deps) { // Update lastDeps and copy it to _lastDeps var _lastDeps = lastDeps; lastDeps = deps; var currentResult = deps[identifier]; if (_lastDeps && pastResult && pastResult === currentResult) { // There can only be one changed dependency, since we're just combining their outputs. // Therefore we can find the changed one and process it exclusively on the last resolved // state that we had here. var result = currentResult; for (var _iterator = dependencyIdentifiers, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _getIterator(_iterator);;) { var _ref6; if (_isArray) { if (_i >= _iterator.length) break; _ref6 = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) break; _ref6 = _i.value; } var dependencyIdentifier = _ref6; var current = deps[dependencyIdentifier]; var last = _lastDeps[dependencyIdentifier]; if (current !== last) { result = applyDependency(result, current, dependencyIdentifier); } } return result; } // Fallback to processing everything, since either this is the first iteration, or the original // store changed. return dependencyIdentifiers.reduce(function (acc, dependencyIdentifier) { var dependencyState = deps[dependencyIdentifier]; return applyDependency(acc, dependencyState, dependencyIdentifier); }, currentResult); }, null); })(); } // Apply post-hook, if it's defined function _ref7(x) { return x.map(post); } if (post) { res = res.map(_ref7); } res = res.publishReplay(1); _stores[identifier] = res; connections[identifier] = res.connect(); return res; }; for (var identifier in stores) { if (stores.hasOwnProperty(identifier)) { var store = stores[identifier]; _stores[identifier] = resolveStore(store); } } this[resultCache][dispatcher.identifier] = _stores; return _stores; } export var Orchestra = function () { Orchestra.isOrchestra = function isOrchestra(obj) { return typeof obj === 'object' && obj instanceof Orchestra; }; function Orchestra() { _classCallCheck(this, Orchestra); for (var _len = arguments.length, stores = Array(_len), _key = 0; _key < _len; _key++) { stores[_key] = arguments[_key]; } invariant(stores.length > 0 && stores.every(Store.isStore), 'Orchestra: constructor expects to receive Stores.'); var _stores = stores.reduce(_ref, {}); this[resultCache] = {}; this.stores = _stores; this.connections = {}; this.externals = {}; this.opts = {}; } Orchestra.prototype.addDebounce = _ref2; Orchestra.prototype.addReducer = _ref3; Orchestra.prototype.reduce = _ref4; return Orchestra; }(); export default function createOrchestra() { for (var _len2 = arguments.length, stores = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { stores[_key2] = arguments[_key2]; } return new (Function.prototype.bind.apply(Orchestra, [null].concat(stores)))(); }