fluorine-orchestra
Version:
A data orchestration layer for Fluorine
342 lines (246 loc) • 10.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Orchestra = undefined;
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _extends3 = require('babel-runtime/helpers/extends');
var _extends4 = _interopRequireDefault(_extends3);
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _symbol = require('babel-runtime/core-js/symbol');
var _symbol2 = _interopRequireDefault(_symbol);
exports.default = createOrchestra;
var _invariant = require('invariant');
var _invariant2 = _interopRequireDefault(_invariant);
var _rxjs = require('rxjs');
var _Collection = require('./Collection');
var _Store = require('./Store');
var _combineStores = require('./util/combineStores');
var _combineStores2 = _interopRequireDefault(_combineStores);
var _isDispatcher = require('fluorine-lib/lib/util/isDispatcher');
var _isDispatcher2 = _interopRequireDefault(_isDispatcher);
var _immutable = require('immutable');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var resultCache = (0, _symbol2.default)('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) {
(0, _invariant2.default)(typeof debounceTime === 'number' && debounceTime > 0, 'Orchestra: Expected `debounceTime` to be a number and > 0.');
this.opts.debounceTime = debounceTime;
}
function _ref3(identifier, reducer) {
(0, _invariant2.default)(typeof identifier === 'string', 'Orchestra: `identifier` is expected to be a string.');
(0, _invariant2.default)(typeof reducer === 'function', 'Orchestra: `reducer` is expected to be a reducer function.');
var externals = this.externals;
var stores = this.stores;
(0, _invariant2.default)(!stores.hasOwnProperty(identifier), 'Orchesta: The identifier `' + identifier + '` is already taken by a Store.');
(0, _invariant2.default)(!externals.hasOwnProperty(identifier), 'Orchesta: The identifier `' + identifier + '` is not unique.');
externals[identifier] = reducer;
return this;
}
function _ref4(dispatcher) {
(0, _invariant2.default)((0, _isDispatcher2.default)(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 = (0, _keys2.default)(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 = (0, _keys2.default)(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 (_immutable.Iterable.isIterable(ids) || Array.isArray(ids)) {
(0, _invariant2.default)(typeof ids.forEach === 'function', 'Orchestra: `ids` is expected to have a method `forEach`.');
var _store2 = stores[dependencyIdentifier];
var collection = _store2 ? _store2.createCollection() : new _Collection.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 _immutable.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 = (0, _combineStores2.default)((0, _extends4.default)({}, _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 : (0, _getIterator3.default)(_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;
}
var Orchestra = exports.Orchestra = function () {
Orchestra.isOrchestra = function isOrchestra(obj) {
return typeof obj === 'object' && obj instanceof Orchestra;
};
function Orchestra() {
(0, _classCallCheck3.default)(this, Orchestra);
for (var _len = arguments.length, stores = Array(_len), _key = 0; _key < _len; _key++) {
stores[_key] = arguments[_key];
}
(0, _invariant2.default)(stores.length > 0 && stores.every(_Store.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;
}();
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)))();
}