orbit-state-tree
Version:
State tree manager based on Orbit.js
1,211 lines (1,052 loc) • 33.7 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var invariant = _interopDefault(require('invariant'));
var normalize = _interopDefault(require('json-api-normalizer'));
var Jsona = _interopDefault(require('jsona'));
var core = require('@orbit/core');
var build = _interopDefault(require('redux-object'));
'use strict';
var domain;
// This constructor is used to store event handlers. Instantiating this is
// faster than explicitly calling `Object.create(null)` to get a "clean" empty
// object (tested with v8 v4.9).
function EventHandlers() {}
EventHandlers.prototype = Object.create(null);
function EventEmitter() {
EventEmitter.init.call(this);
}
// nodejs oddity
// require('events') === require('events').EventEmitter
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.usingDomains = false;
EventEmitter.prototype.domain = undefined;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;
EventEmitter.init = function() {
this.domain = null;
if (EventEmitter.usingDomains) {
// if there is an active domain, then attach to it.
if (domain.active && !(this instanceof domain.Domain)) {
this.domain = domain.active;
}
}
if (!this._events || this._events === Object.getPrototypeOf(this)._events) {
this._events = new EventHandlers();
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
};
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || isNaN(n))
throw new TypeError('"n" argument must be a positive number');
this._maxListeners = n;
return this;
};
function $getMaxListeners(that) {
if (that._maxListeners === undefined)
return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return $getMaxListeners(this);
};
// These standalone emit* functions are used to optimize calling of event
// handlers for fast cases because emit() itself often has a variable number of
// arguments and can be deoptimized because of that. These functions always have
// the same number of arguments and thus do not get deoptimized, so the code
// inside them can execute faster.
function emitNone(handler, isFn, self) {
if (isFn)
handler.call(self);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self);
}
}
function emitOne(handler, isFn, self, arg1) {
if (isFn)
handler.call(self, arg1);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1);
}
}
function emitTwo(handler, isFn, self, arg1, arg2) {
if (isFn)
handler.call(self, arg1, arg2);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1, arg2);
}
}
function emitThree(handler, isFn, self, arg1, arg2, arg3) {
if (isFn)
handler.call(self, arg1, arg2, arg3);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1, arg2, arg3);
}
}
function emitMany(handler, isFn, self, args) {
if (isFn)
handler.apply(self, args);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].apply(self, args);
}
}
EventEmitter.prototype.emit = function emit(type) {
var er, handler, len, args, i, events, domain;
var needDomainExit = false;
var doError = (type === 'error');
events = this._events;
if (events)
doError = (doError && events.error == null);
else if (!doError)
return false;
domain = this.domain;
// If there is no 'error' event listener then throw.
if (doError) {
er = arguments[1];
if (domain) {
if (!er)
er = new Error('Uncaught, unspecified "error" event');
er.domainEmitter = this;
er.domain = domain;
er.domainThrown = false;
domain.emit('error', er);
} else if (er instanceof Error) {
throw er; // Unhandled 'error' event
} else {
// At least give some kind of context to the user
var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
err.context = er;
throw err;
}
return false;
}
handler = events[type];
if (!handler)
return false;
var isFn = typeof handler === 'function';
len = arguments.length;
switch (len) {
// fast cases
case 1:
emitNone(handler, isFn, this);
break;
case 2:
emitOne(handler, isFn, this, arguments[1]);
break;
case 3:
emitTwo(handler, isFn, this, arguments[1], arguments[2]);
break;
case 4:
emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
break;
// slower
default:
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
emitMany(handler, isFn, this, args);
}
if (needDomainExit)
domain.exit();
return true;
};
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
events = target._events;
if (!events) {
events = target._events = new EventHandlers();
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (events.newListener) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
// Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = target._events;
}
existing = events[type];
}
if (!existing) {
// Optimize the case of one listener. Don't need the extra array object.
existing = events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events[type] = prepend ? [listener, existing] :
[existing, listener];
} else {
// If we've already got an array, just append.
if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
}
// Check for listener leak
if (!existing.warned) {
m = $getMaxListeners(target);
if (m && m > 0 && existing.length > m) {
existing.warned = true;
var w = new Error('Possible EventEmitter memory leak detected. ' +
existing.length + ' ' + type + ' listeners added. ' +
'Use emitter.setMaxListeners() to increase limit');
w.name = 'MaxListenersExceededWarning';
w.emitter = target;
w.type = type;
w.count = existing.length;
emitWarning(w);
}
}
}
return target;
}
function emitWarning(e) {
typeof console.warn === 'function' ? console.warn(e) : console.log(e);
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener =
function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
function _onceWrap(target, type, listener) {
var fired = false;
function g() {
target.removeListener(type, g);
if (!fired) {
fired = true;
listener.apply(target, arguments);
}
}
g.listener = listener;
return g;
}
EventEmitter.prototype.once = function once(type, listener) {
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
this.on(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.prependOnceListener =
function prependOnceListener(type, listener) {
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
this.prependListener(type, _onceWrap(this, type, listener));
return this;
};
// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
var list, events, position, i, originalListener;
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
events = this._events;
if (!events)
return this;
list = events[type];
if (!list)
return this;
if (list === listener || (list.listener && list.listener === listener)) {
if (--this._eventsCount === 0)
this._events = new EventHandlers();
else {
delete events[type];
if (events.removeListener)
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
position = -1;
for (i = list.length; i-- > 0;) {
if (list[i] === listener ||
(list[i].listener && list[i].listener === listener)) {
originalListener = list[i].listener;
position = i;
break;
}
}
if (position < 0)
return this;
if (list.length === 1) {
list[0] = undefined;
if (--this._eventsCount === 0) {
this._events = new EventHandlers();
return this;
} else {
delete events[type];
}
} else {
spliceOne(list, position);
}
if (events.removeListener)
this.emit('removeListener', type, originalListener || listener);
}
return this;
};
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
var listeners, events;
events = this._events;
if (!events)
return this;
// not listening for removeListener, no need to emit
if (!events.removeListener) {
if (arguments.length === 0) {
this._events = new EventHandlers();
this._eventsCount = 0;
} else if (events[type]) {
if (--this._eventsCount === 0)
this._events = new EventHandlers();
else
delete events[type];
}
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
var keys = Object.keys(events);
for (var i = 0, key; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = new EventHandlers();
this._eventsCount = 0;
return this;
}
listeners = events[type];
if (typeof listeners === 'function') {
this.removeListener(type, listeners);
} else if (listeners) {
// LIFO order
do {
this.removeListener(type, listeners[listeners.length - 1]);
} while (listeners[0]);
}
return this;
};
EventEmitter.prototype.listeners = function listeners(type) {
var evlistener;
var ret;
var events = this._events;
if (!events)
ret = [];
else {
evlistener = events[type];
if (!evlistener)
ret = [];
else if (typeof evlistener === 'function')
ret = [evlistener.listener || evlistener];
else
ret = unwrapListeners(evlistener);
}
return ret;
};
EventEmitter.listenerCount = function(emitter, type) {
if (typeof emitter.listenerCount === 'function') {
return emitter.listenerCount(type);
} else {
return listenerCount.call(emitter, type);
}
};
EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
var events = this._events;
if (events) {
var evlistener = events[type];
if (typeof evlistener === 'function') {
return 1;
} else if (evlistener) {
return evlistener.length;
}
}
return 0;
}
EventEmitter.prototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
};
// About 1.5x faster than the two-arg version of Array#splice().
function spliceOne(list, index) {
for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
list[i] = list[k];
list.pop();
}
function arrayClone(arr, i) {
var copy = new Array(i);
while (i--)
copy[i] = arr[i];
return copy;
}
function unwrapListeners(arr) {
var ret = new Array(arr.length);
for (var i = 0; i < ret.length; ++i) {
ret[i] = arr[i].listener || arr[i];
}
return ret;
}
var asyncGenerator = function () {
function AwaitValue(value) {
this.value = value;
}
function AsyncGenerator(gen) {
var front, back;
function send(key, arg) {
return new Promise(function (resolve, reject) {
var request = {
key: key,
arg: arg,
resolve: resolve,
reject: reject,
next: null
};
if (back) {
back = back.next = request;
} else {
front = back = request;
resume(key, arg);
}
});
}
function resume(key, arg) {
try {
var result = gen[key](arg);
var value = result.value;
if (value instanceof AwaitValue) {
Promise.resolve(value.value).then(function (arg) {
resume("next", arg);
}, function (arg) {
resume("throw", arg);
});
} else {
settle(result.done ? "return" : "normal", result.value);
}
} catch (err) {
settle("throw", err);
}
}
function settle(type, value) {
switch (type) {
case "return":
front.resolve({
value: value,
done: true
});
break;
case "throw":
front.reject(value);
break;
default:
front.resolve({
value: value,
done: false
});
break;
}
front = front.next;
if (front) {
resume(front.key, front.arg);
} else {
back = null;
}
}
this._invoke = send;
if (typeof gen.return !== "function") {
this.return = undefined;
}
}
if (typeof Symbol === "function" && Symbol.asyncIterator) {
AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
return this;
};
}
AsyncGenerator.prototype.next = function (arg) {
return this._invoke("next", arg);
};
AsyncGenerator.prototype.throw = function (arg) {
return this._invoke("throw", arg);
};
AsyncGenerator.prototype.return = function (arg) {
return this._invoke("return", arg);
};
return {
wrap: function (fn) {
return function () {
return new AsyncGenerator(fn.apply(this, arguments));
};
},
await: function (value) {
return new AwaitValue(value);
}
};
}();
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var defineProperty = function (obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
};
/**
* Serialize plain objects into json api format
*/
var Serializer = function () {
function Serializer(schema) {
classCallCheck(this, Serializer);
this._formatter = new Jsona();
this._schema = schema;
}
/**
* Augment object with attributes needed by formatter
*
* @param {String} type Object type
* @param {Object} object
*/
createClass(Serializer, [{
key: '_augmentObject',
value: function _augmentObject(type, object) {
var _this = this;
var relationships = this._schema.models[type].relationships;
var relationshipNames = relationships ? Object.keys(relationships) : [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
var _loop = function _loop() {
var relationshipName = _step.value;
if (!object[relationshipName]) {
return 'continue';
}
if (Array.isArray(object[relationshipName])) {
object[relationshipName] = object[relationshipName].map(function (relationshipObject) {
return _this._augmentObject(relationships[relationshipName].model, relationshipObject);
});
return 'continue';
}
object[relationshipName] = _this._augmentObject(relationships[relationshipName].model, object[relationshipName]);
};
for (var _iterator = relationshipNames[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _ret = _loop();
if (_ret === 'continue') continue;
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return Object.assign({
type: type,
relationshipNames: relationshipNames
}, object);
}
/**
* Serialize an object into json api format
*
* @param {String} type Object type
* @param {Object} object Object
* @returns {Object}
*/
}, {
key: 'serialize',
value: function serialize(type, object) {
invariant(type in this._schema.models, 'serialized type must exist in schema');
return this._formatter.serialize({ stuff: this._augmentObject(type, object) });
}
}]);
return Serializer;
}();
var StateTree = function () {
/**
* Initialize the state tree with your store and schema instances
*
* @param {Object} {store, schema}
* @memberof StateTree
*/
function StateTree(_ref) {
var store = _ref.store,
schema = _ref.schema;
classCallCheck(this, StateTree);
invariant(store, 'Store instance required');
invariant(schema, 'Schema instance required');
this._store = store;
this._schema = schema;
this._serializer = new Serializer(this._schema);
this._events = new EventEmitter();
this._lastRequestId = 0;
this._state = {
entities: {},
requests: {}
};
}
/**
* Listen for tree changes, receving a new tree every time a change is comitted
*
* @param {Function} callback
*/
createClass(StateTree, [{
key: 'onChange',
value: function onChange(callback) {
return this._events.addListener('change', callback);
}
/**
* Listen for unhandled errors, errors coming from orbitjs are described on request object
*
* @param {Function} callback
*/
}, {
key: 'onUnhandledError',
value: function onUnhandledError(callback) {
return this._events.addListener('unhandledError', callback);
}
/**
* Adds an record
*
* @param {String} type
* @param {Object} object
*/
}, {
key: 'addRecord',
value: function addRecord(type, object) {
return this._storeUpdate('addRecord', [this._serialize(type, object)]);
}
/**
* Replaces an entire record
*
* @param {String} type
* @param {Object} object
*/
}, {
key: 'replaceRecord',
value: function replaceRecord(type, object) {
return this._storeUpdate('replaceRecord', [this._serialize(type, object)]);
}
/**
* Removes an record
*
* @param {String} type
* @param {String} id
*/
}, {
key: 'removeRecord',
value: function removeRecord(type, id) {
return this._storeUpdate('removeRecord', [{ type: type, id: id }]);
}
/**
* Replaces a key
*
* @param {String} type
* @param {String} id
* @param {String} keyName
* @param {String} keyValue
*/
}, {
key: 'replaceKey',
value: function replaceKey(type, id, keyName, keyValue) {
return this._storeUpdate('replaceKey', [{ type: type, id: id }, keyName, keyValue]);
}
/**
* Replaces an attribute
*
* @param {String} type
* @param {String} id
* @param {String} attributeName
* @param {String} attributeValue
*/
}, {
key: 'replaceAttribute',
value: function replaceAttribute(type, id, attributeName, attributeValue) {
return this._storeUpdate('replaceAttribute', [{ type: type, id: id }, attributeName, attributeValue]);
}
/**
* Adds a member to a to-many relationship
*
* @param {String} type
* @param {String} id
* @param {String} relationshipName
* @param {String} relationshipId
*/
}, {
key: 'addToRelatedRecords',
value: function addToRelatedRecords(type, id, relationshipName, relationshipId) {
this._validateRelationship(type, relationshipName, 'hasMany');
var relationshipType = this._schema.models[type].relationships[relationshipName].model;
return this._storeUpdate('addToRelatedRecords', [{ type: type, id: id }, relationshipName, { type: relationshipType, id: relationshipId }]);
}
/**
* Removes a member from a to-many relationship
*
* @param {String} type
* @param {String} id
* @param {String} relationshipName
* @param {String} relationshipId
*/
}, {
key: 'removeFromRelatedRecords',
value: function removeFromRelatedRecords(type, id, relationshipName, relationshipId) {
this._validateRelationship(type, relationshipName, 'hasMany');
var relationshipType = this._schema.models[type].relationships[relationshipName].model;
return this._storeUpdate('removeFromRelatedRecords', [{ type: type, id: id }, relationshipName, { type: relationshipType, id: relationshipId }]);
}
/**
* Replaces every member of a to-many relationship
*
* @param {String} type
* @param {String} id
* @param {String} relationshipName
* @param {String[]} relationshipIds
*/
}, {
key: 'replaceRelatedRecords',
value: function replaceRelatedRecords(type, id, relationshipName, relationshipIds) {
this._validateRelationship(type, relationshipName, 'hasMany');
var relationshipType = this._schema.models[type].relationships[relationshipName].model;
var data = relationshipIds.map(function (id) {
return { type: relationshipType, id: id };
});
return this._storeUpdate('replaceRelatedRecords', [{ type: type, id: id }, relationshipName, data]);
}
/**
* Replaces a to-one relationship
*
* @param {String} type
* @param {String} id
* @param {String} relationshipName
* @param {String} relationshipId
*/
}, {
key: 'replaceRelatedRecord',
value: function replaceRelatedRecord(type, id, relationshipName, relationshipId) {
this._validateRelationship(type, relationshipName, 'hasOne');
var relationshipType = this._schema.models[type].relationships[relationshipName].model;
return this._storeUpdate('replaceRelatedRecord', [{ type: type, id: id }, relationshipName, { type: relationshipType, id: relationshipId }]);
}
/**
* Find a record by its identity.
*
* @param {String} type
* @param {String} id
*/
}, {
key: 'findRecord',
value: function findRecord(type, id) {
return this._storeQuery('findRecord', [{ type: type, id: id }]);
}
/**
* Find all records of a specific type.
*
* @param {String} type
* @param {Object} query
*/
}, {
key: 'findRecords',
value: function findRecords(type, query) {
var hook = function hook(builder) {
if (query.filter) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = query.filter[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var filter = _step.value;
builder.filter(filter);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
if (query.sort) {
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = query.sort[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var sort = _step2.value;
builder.sort(sort);
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
if (query.page) {
builder.page(query.page);
}
return builder;
};
return this._storeQuery('findRecords', [type], hook);
}
/**
* Find a related record in a to-one relationship
*
* @param {String} type
* @param {String} id
* @param {String} relationship
*/
}, {
key: 'findRelatedRecord',
value: function findRelatedRecord(type, id, relationship) {
this._validateRelationship(type, relationship, 'hasOne');
return this._storeQuery('findRelatedRecord', [{ type: type, id: id }, relationship]);
}
/**
* Find a related record in a to-one relationship
*
* @param {String} type
* @param {String} id
* @param {String} relationship
*/
}, {
key: 'findRelatedRecords',
value: function findRelatedRecords(type, id, relationship) {
this._validateRelationship(type, relationship, 'hasMany');
return this._storeQuery('findRelatedRecords', [{ type: type, id: id }, relationship]);
}
}, {
key: '_validateRelationship',
value: function _validateRelationship(type, name, relationshipType) {
var isValid = this._schema.models[type] && this._schema.models[type].relationships[name] && (!relationshipType || this._schema.models[type].relationships[name].type === relationshipType);
if (!isValid) {
throw new Error('Invalid relationship ' + name + ' on model ' + type + ' with type ' + relationshipType);
}
}
}, {
key: '_serialize',
value: function _serialize(type, object) {
return this._serializer.serialize(type, object).data;
}
}, {
key: '_storeUpdate',
value: function _storeUpdate(method, args) {
var _this = this;
var requestId = this._createRequest();
var promise = this._store.update(function (t) {
return t[method].apply(t, args);
}).then(function (jsonApiObject) {
return _this._handeRequestSuccess(requestId, jsonApiObject);
}).catch(function (error) {
return _this._handleRequestError(requestId, error);
});
return { requestId: requestId, promise: promise };
}
}, {
key: '_storeQuery',
value: function _storeQuery(method, args, hook) {
var _this2 = this;
var requestId = this._createRequest();
var promise = this._store.query(function (queryBuilder) {
var query = queryBuilder[method].apply(queryBuilder, args);
if (hook) {
query = hook(query);
}
return query;
}).then(function (jsonApiObject) {
return _this2._handeRequestSuccess(requestId, jsonApiObject);
}).catch(function (error) {
return _this2._handleRequestError(requestId, error);
});
return { requestId: requestId, promise: promise };
}
}, {
key: '_createRequest',
value: function _createRequest() {
var requestId = ++this._lastRequestId;
this._mergeState({
requests: defineProperty({}, requestId, {
id: requestId,
completed: false,
timestamp: new Date().getTime()
})
});
return requestId;
}
}, {
key: '_handeRequestSuccess',
value: function _handeRequestSuccess(requestId, response) {
var idsByType = {};
if (Array.isArray(response)) {
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = response[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var item = _step3.value;
if (!idsByType[item.type]) {
idsByType[item.type] = [];
}
idsByType[item.type].push(item.id);
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
} else if (response && response.type) {
idsByType[response.type] = [response.id];
}
this._insertEntities(requestId, normalize({ data: response }), idsByType);
}
}, {
key: '_handleRequestError',
value: function _handleRequestError(requestId, error) {
if (!(error instanceof core.Exception)) {
this._events.emit('unhandledError', error, requestId);
return error;
}
var request = {
id: requestId,
completed: true,
result: {},
error: {
message: error.message,
description: error.description,
type: error.type,
id: error.id,
relationship: error.relationship
}
};
this._mergeState({
entities: {},
requests: defineProperty({}, requestId, request)
});
}
}, {
key: '_insertEntities',
value: function _insertEntities(requestId, entities, idsByType) {
var request = {
id: requestId,
completed: true,
result: idsByType,
error: null
};
this._mergeState({
entities: entities,
requests: defineProperty({}, requestId, request)
});
}
}, {
key: '_mergeState',
value: function _mergeState(mergedState) {
var newState = Object.assign({}, this._state);
for (var key in mergedState.entities) {
newState.entities[key] = Object.assign({}, this._state.entities[key], mergedState.entities[key]);
}
for (var _key in mergedState.requests) {
newState.requests[_key] = Object.assign({}, this._state.requests[_key], mergedState.requests[_key]);
}
this._state = newState;
this._events.emit('change', this._state);
}
}]);
return StateTree;
}();
/**
* Returns an request object from current state tree
*/
function requestSelector(state, requestId) {
return state.requests[requestId];
}
/**
* Return a record from current state tree
*/
function recordSelector(state, type, id) {
return build(state.entities, type, id);
}
exports.StateTree = StateTree;
exports.requestSelector = requestSelector;
exports.recordSelector = recordSelector;
//# sourceMappingURL=orbit-state-tree.js.map