UNPKG

js-data

Version:

Robust, framework-agnostic in-memory data store.

1,654 lines (1,501 loc) 496 kB
/*! * js-data * @version 3.0.10 - Homepage <http://www.js-data.io/> * @author js-data project authors * @copyright (c) 2014-2016 js-data project authors * @license MIT <https://github.com/js-data/js-data/blob/master/LICENSE> * * @overview js-data is a framework-agnostic, datastore-agnostic ORM/ODM for Node.js and the Browser. */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('js-data', ['exports'], factory) : (global = global || self, factory(global.JSData = {})); }(this, (function (exports) { 'use strict'; function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function (obj) { return typeof obj; }; } else { _typeof = function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _defineProperty(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; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } /** * Utility methods used by JSData. * * @example * import { utils } from 'js-data'; * console.log(utils.isString('foo')); // true * * @namespace utils * @type {Object} */ var DOMAIN = 'utils'; var INFINITY = 1 / 0; var MAX_INTEGER = 1.7976931348623157e308; var BOOL_TAG = '[object Boolean]'; var DATE_TAG = '[object Date]'; var FUNC_TAG = '[object Function]'; var NUMBER_TAG = '[object Number]'; var OBJECT_TAG = '[object Object]'; var REGEXP_TAG = '[object RegExp]'; var STRING_TAG = '[object String]'; var objToString = Object.prototype.toString; var PATH = /^(.+)\.(.+)$/; var ERRORS = { '400': function _() { return "expected: ".concat(arguments[0], ", found: ").concat(arguments[2] ? arguments[1] : _typeof(arguments[1])); }, '404': function _() { return "".concat(arguments[0], " not found"); } }; var toInteger = function toInteger(value) { if (!value) { return 0; } // Coerce to number value = +value; if (value === INFINITY || value === -INFINITY) { var sign = value < 0 ? -1 : 1; return sign * MAX_INTEGER; } var remainder = value % 1; return value === value ? remainder ? value - remainder : value : 0; // eslint-disable-line }; var toStr = function toStr(value) { return objToString.call(value); }; var isPlainObject = function isPlainObject(value) { return !!value && _typeof(value) === 'object' && value.constructor === Object; }; var mkdirP = function mkdirP(object, path) { if (!path) { return object; } var parts = path.split('.'); parts.forEach(function (key) { if (isPrototypePolluted(key)) return; if (!object[key]) { object[key] = {}; } object = object[key]; }); return object; }; var isPrototypePolluted = function isPrototypePolluted(key) { return ['__proto__', 'prototype', 'constructor'].includes(key); }; var utils = { /** * Reference to the Promise constructor used by JSData. Defaults to * `window.Promise` or `global.Promise`. * * @example <caption>Make JSData use a different `Promise` constructor</caption> * import Promise from 'bluebird'; * import { utils } from 'js-data'; * utils.Promise = Promise; * * @name utils.Promise * @since 3.0.0 * @type {Function} */ Promise: Promise, /** * Shallow copy properties that meet the following criteria from `src` to * `dest`: * * - own enumerable * - not a function * - does not start with "_" * * @method utils._ * @param {object} dest Destination object. * @param {object} src Source object. * @private * @since 3.0.0 */ _: function _(dest, src) { utils.forOwn(src, function (value, key) { if (key && dest[key] === undefined && !utils.isFunction(value) && key.indexOf('_') !== 0) { dest[key] = value; } }); }, /** * Recursively iterates over relations found in `opts.with`. * * @method utils._forRelation * @param {object} opts Configuration options. * @param {Relation} def Relation definition. * @param {Function} fn Callback function. * @param {*} [thisArg] Execution context for the callback function. * @private * @since 3.0.0 */ _forRelation: function _forRelation(opts, def, fn, thisArg) { var relationName = def.relation; var containedName = null; var index; opts || (opts = {}); opts.with || (opts.with = []); if ((index = utils._getIndex(opts.with, relationName)) >= 0) { containedName = relationName; } else if ((index = utils._getIndex(opts.with, def.localField)) >= 0) { containedName = def.localField; } if (opts.withAll) { fn.call(thisArg, def, {}); return; } else if (!containedName) { return; } var optsCopy = {}; utils.fillIn(optsCopy, def.getRelation()); utils.fillIn(optsCopy, opts); optsCopy.with = opts.with.slice(); optsCopy._activeWith = optsCopy.with.splice(index, 1)[0]; optsCopy.with.forEach(function (relation, i) { if (relation && relation.indexOf(containedName) === 0 && relation.length >= containedName.length && relation[containedName.length] === '.') { optsCopy.with[i] = relation.substr(containedName.length + 1); } else { optsCopy.with[i] = ''; } }); fn.call(thisArg, def, optsCopy); }, /** * Find the index of a relation in the given list * * @method utils._getIndex * @param {string[]} list List to search. * @param {string} relation Relation to find. * @private * @returns {number} */ _getIndex: function _getIndex(list, relation) { var index = -1; list.forEach(function (_relation, i) { if (_relation === relation) { index = i; return false; } else if (utils.isObject(_relation)) { if (_relation.relation === relation) { index = i; return false; } } }); return index; }, /** * Define hidden (non-enumerable), writable properties on `target` from the * provided `props`. * * @example * import { utils } from 'js-data'; * function Cat () {} * utils.addHiddenPropsToTarget(Cat.prototype, { * say () { * console.log('meow'); * } * }); * const cat = new Cat(); * cat.say(); // "meow" * * @method utils.addHiddenPropsToTarget * @param {object} target That to which `props` should be added. * @param {object} props Properties to be added to `target`. * @since 3.0.0 */ addHiddenPropsToTarget: function addHiddenPropsToTarget(target, props) { var map = {}; Object.keys(props).forEach(function (propName) { var descriptor = Object.getOwnPropertyDescriptor(props, propName); descriptor.enumerable = false; map[propName] = descriptor; }); Object.defineProperties(target, map); }, /** * Return whether the two objects are deeply different. * * @example * import { utils } from 'js-data'; * utils.areDifferent({}, {}); // false * utils.areDifferent({ a: 1 }, { a: 1 }); // false * utils.areDifferent({ foo: 'bar' }, {}); // true * * @method utils.areDifferent * @param {object} a Base object. * @param {object} b Comparison object. * @param {object} [opts] Configuration options. * @param {Function} [opts.equalsFn={@link utils.deepEqual}] Equality function. * @param {array} [opts.ignore=[]] Array of strings or RegExp of fields to ignore. * @returns {boolean} Whether the two objects are deeply different. * @see utils.diffObjects * @since 3.0.0 */ areDifferent: function areDifferent(newObject, oldObject, opts) { opts || (opts = {}); var diff = utils.diffObjects(newObject, oldObject, opts); var diffCount = Object.keys(diff.added).length + Object.keys(diff.removed).length + Object.keys(diff.changed).length; return diffCount > 0; }, /** * Verified that the given constructor is being invoked via `new`, as opposed * to just being called like a normal function. * * @example * import { utils } from 'js-data'; * function Cat () { * utils.classCallCheck(this, Cat); * } * const cat = new Cat(); // this is ok * Cat(); // this throws an error * * @method utils.classCallCheck * @param {*} instance Instance that is being constructed. * @param {Constructor} ctor Constructor function used to construct the * instance. * @since 3.0.0 * @throws {Error} Throws an error if the constructor is being improperly * invoked. */ classCallCheck: function classCallCheck(instance, ctor) { if (!(instance instanceof ctor)) { throw utils.err("".concat(ctor.name))(500, 'Cannot call a class as a function'); } }, /** * Deep copy a value. * * @example * import { utils } from 'js-data'; * const a = { foo: { bar: 'baz' } }; * const b = utils.copy(a); * a === b; // false * utils.areDifferent(a, b); // false * * @param {*} from Value to deep copy. * @param {*} [to] Destination object for the copy operation. * @param {*} [stackFrom] For internal use. * @param {*} [stackTo] For internal use. * @param {string[]|RegExp[]} [blacklist] List of strings or RegExp of * properties to skip. * @param {boolean} [plain] Whether to make a plain copy (don't try to use * original prototype). * @returns {*} Deep copy of `from`. * @since 3.0.0 */ copy: function copy(from, to, stackFrom, stackTo, blacklist, plain) { if (!to) { to = from; if (from) { if (utils.isArray(from)) { to = utils.copy(from, [], stackFrom, stackTo, blacklist, plain); } else if (utils.isDate(from)) { to = new Date(from.getTime()); } else if (utils.isRegExp(from)) { to = new RegExp(from.source, from.toString().match(/[^/]*$/)[0]); to.lastIndex = from.lastIndex; } else if (utils.isObject(from)) { if (plain) { to = utils.copy(from, {}, stackFrom, stackTo, blacklist, plain); } else { to = utils.copy(from, Object.create(Object.getPrototypeOf(from)), stackFrom, stackTo, blacklist, plain); } } } } else { if (from === to) { throw utils.err("".concat(DOMAIN, ".copy"))(500, 'Cannot copy! Source and destination are identical.'); } stackFrom = stackFrom || []; stackTo = stackTo || []; if (utils.isObject(from)) { var index = stackFrom.indexOf(from); if (index !== -1) { return stackTo[index]; } stackFrom.push(from); stackTo.push(to); } var result; if (utils.isArray(from)) { var i; to.length = 0; for (i = 0; i < from.length; i++) { result = utils.copy(from[i], null, stackFrom, stackTo, blacklist, plain); if (utils.isObject(from[i])) { stackFrom.push(from[i]); stackTo.push(result); } to.push(result); } } else { if (utils.isArray(to)) { to.length = 0; } else { utils.forOwn(to, function (value, key) { delete to[key]; }); } for (var key in from) { if (Object.hasOwnProperty.call(from, key)) { if (utils.isBlacklisted(key, blacklist)) { continue; } result = utils.copy(from[key], null, stackFrom, stackTo, blacklist, plain); if (utils.isObject(from[key])) { stackFrom.push(from[key]); stackTo.push(result); } to[key] = result; } } } } return to; }, /** * Recursively shallow fill in own enumerable properties from `source` to * `dest`. * * @example * import { utils } from 'js-data'; * const a = { foo: { bar: 'baz' }, beep: 'boop' }; * const b = { beep: 'bip' }; * utils.deepFillIn(b, a); * console.log(b); // {"foo":{"bar":"baz"},"beep":"bip"} * * @method utils.deepFillIn * @param {object} dest The destination object. * @param {object} source The source object. * @see utils.fillIn * @see utils.deepMixIn * @since 3.0.0 */ deepFillIn: function deepFillIn(dest, source) { if (source) { utils.forOwn(source, function (value, key) { if (isPrototypePolluted(key)) return; var existing = dest[key]; if (isPlainObject(value) && isPlainObject(existing)) { utils.deepFillIn(existing, value); } else if (!Object.hasOwnProperty.call(dest, key) || dest[key] === undefined) { dest[key] = value; } }); } return dest; }, /** * Recursively shallow copy enumerable properties from `source` to `dest`. * * @example * import { utils } from 'js-data'; * const a = { foo: { bar: 'baz' }, beep: 'boop' }; * const b = { beep: 'bip' }; * utils.deepFillIn(b, a); * console.log(b); // {"foo":{"bar":"baz"},"beep":"boop"} * * @method utils.deepMixIn * @param {object} dest The destination object. * @param {object} source The source object. * @see utils.fillIn * @see utils.deepFillIn * @since 3.0.0 */ deepMixIn: function deepMixIn(dest, source) { if (source) { for (var key in source) { if (isPrototypePolluted(key)) continue; var value = source[key]; var existing = dest[key]; if (isPlainObject(value) && isPlainObject(existing)) { utils.deepMixIn(existing, value); } else { dest[key] = value; } } } return dest; }, /** * Return a diff of the base object to the comparison object. * * @example * import { utils } from 'js-data'; * const oldObject = { foo: 'bar', a: 1234 }; * const newObject = { beep: 'boop', a: 5678 }; * const diff = utils.diffObjects(oldObject, newObject); * console.log(diff.added); // {"beep":"boop"} * console.log(diff.changed); // {"a":5678} * console.log(diff.removed); // {"foo":undefined} * * @method utils.diffObjects * @param {object} newObject Comparison object. * @param {object} oldObject Base object. * @param {object} [opts] Configuration options. * @param {Function} [opts.equalsFn={@link utils.deepEqual}] Equality function. * @param {array} [opts.ignore=[]] Array of strings or RegExp of fields to ignore. * @returns {Object} The diff from the base object to the comparison object. * @see utils.areDifferent * @since 3.0.0 */ diffObjects: function diffObjects(newObject, oldObject, opts) { opts || (opts = {}); var equalsFn = opts.equalsFn; var blacklist = opts.ignore; var diff = { added: {}, changed: {}, removed: {} }; if (!utils.isFunction(equalsFn)) { equalsFn = utils.deepEqual; } var newKeys = Object.keys(newObject).filter(function (key) { return !utils.isBlacklisted(key, blacklist); }); var oldKeys = Object.keys(oldObject).filter(function (key) { return !utils.isBlacklisted(key, blacklist); }); // Check for properties that were added or changed newKeys.forEach(function (key) { var oldValue = oldObject[key]; var newValue = newObject[key]; if (equalsFn(oldValue, newValue)) { return; } if (oldValue === undefined) { diff.added[key] = newValue; } else { diff.changed[key] = newValue; } }); // Check for properties that were removed oldKeys.forEach(function (key) { var oldValue = oldObject[key]; var newValue = newObject[key]; if (newValue === undefined && oldValue !== undefined) { diff.removed[key] = undefined; } }); return diff; }, /** * Return whether the two values are equal according to the `==` operator. * * @example * import { utils } from 'js-data'; * console.log(utils.equal(1,1)); // true * console.log(utils.equal(1,'1')); // true * console.log(utils.equal(93, 66)); // false * * @method utils.equal * @param {*} a First value in the comparison. * @param {*} b Second value in the comparison. * @returns {boolean} Whether the two values are equal according to `==`. * @since 3.0.0 */ equal: function equal(a, b) { return a == b; // eslint-disable-line }, /** * Produce a factory function for making Error objects with the provided * metadata. Used throughout the various js-data components. * * @example * import { utils } from 'js-data'; * const errorFactory = utils.err('domain', 'target'); * const error400 = errorFactory(400, 'expected type', 'actual type'); * console.log(error400); // [Error: [domain:target] expected: expected type, found: string http://www.js-data.io/v3.0/docs/errors#400] * @method utils.err * @param {string} domain Namespace. * @param {string} target Target. * @returns {Function} Factory function. * @since 3.0.0 */ err: function err(domain, target) { return function (code) { var prefix = "[".concat(domain, ":").concat(target, "] "); var message = ERRORS[code].apply(null, Array.prototype.slice.call(arguments, 1)); message = "".concat(prefix).concat(message, "\nhttp://www.js-data.io/v3.0/docs/errors#").concat(code); return new Error(message); }; }, /** * Add eventing capabilities into the target object. * * @example * import { utils } from 'js-data'; * const user = { name: 'John' }; * utils.eventify(user); * user.on('foo', () => console.log(arguments)); * user.emit('foo', 1, 'bar'); // should log to console values (1, "bar") * * @method utils.eventify * @param {object} target Target object. * @param {Function} [getter] Custom getter for retrieving the object's event * listeners. * @param {Function} [setter] Custom setter for setting the object's event * listeners. * @since 3.0.0 */ eventify: function eventify(target, getter, setter) { target = target || this; var _events = {}; if (!getter && !setter) { getter = function getter() { return _events; }; setter = function setter(value) { _events = value; }; } Object.defineProperties(target, { emit: { value: function value() { var events = getter.call(this) || {}; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var type = args.shift(); var listeners = events[type] || []; var i; for (i = 0; i < listeners.length; i++) { listeners[i].f.apply(listeners[i].c, args); } listeners = events.all || []; args.unshift(type); for (i = 0; i < listeners.length; i++) { listeners[i].f.apply(listeners[i].c, args); } } }, off: { value: function value(type, func) { var events = getter.call(this); var listeners = events[type]; if (!listeners) { setter.call(this, {}); } else if (func) { for (var i = 0; i < listeners.length; i++) { if (listeners[i].f === func) { listeners.splice(i, 1); break; } } } else { listeners.splice(0, listeners.length); } } }, on: { value: function value(type, func, thisArg) { if (!getter.call(this)) { setter.call(this, {}); } var events = getter.call(this); events[type] = events[type] || []; events[type].push({ c: thisArg, f: func }); } } }); }, /** * Used for sublcassing. Invoke this method in the context of a superclass to * to produce a subclass based on `props` and `classProps`. * * @example * import { utils } from 'js-data'; * function Animal () {} * Animal.extend = utils.extend; * const Cat = Animal.extend({ * say () { * console.log('meow'); * } * }); * const cat = new Cat(); * cat instanceof Animal; // true * cat instanceof Cat; // true * cat.say(); // "meow" * * @method utils.extend * @param {object} props Instance properties for the subclass. * @param {object} [props.constructor] Provide a custom constructor function * to use as the subclass. * @param {object} props Static properties for the subclass. * @returns {Constructor} A new subclass. * @since 3.0.0 */ extend: function extend(props, classProps) { var superClass = this; var _subClass; props || (props = {}); classProps || (classProps = {}); if (Object.hasOwnProperty.call(props, 'constructor')) { _subClass = props.constructor; delete props.constructor; } else { _subClass = function subClass() { utils.classCallCheck(this, _subClass); for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } superClass.apply(this, args); }; } // Setup inheritance of instance members _subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { configurable: true, enumerable: false, value: _subClass, writable: true } }); var obj = Object; // Setup inheritance of static members if (obj.setPrototypeOf) { obj.setPrototypeOf(_subClass, superClass); } else if (classProps.strictEs6Class) { _subClass.__proto__ = superClass; // eslint-disable-line } else { utils.forOwn(superClass, function (value, key) { _subClass[key] = value; }); } if (!Object.hasOwnProperty.call(_subClass, '__super__')) { Object.defineProperty(_subClass, '__super__', { configurable: true, value: superClass }); } utils.addHiddenPropsToTarget(_subClass.prototype, props); utils.fillIn(_subClass, classProps); return _subClass; }, /** * Shallow copy own enumerable properties from `src` to `dest` that are on * `src` but are missing from `dest. * * @example * import { utils } from 'js-data'; * const a = { foo: 'bar', beep: 'boop' }; * const b = { beep: 'bip' }; * utils.fillIn(b, a); * console.log(b); // {"foo":"bar","beep":"bip"} * * @method utils.fillIn * @param {object} dest The destination object. * @param {object} source The source object. * @see utils.deepFillIn * @see utils.deepMixIn * @since 3.0.0 */ fillIn: function fillIn(dest, src) { utils.forOwn(src, function (value, key) { if (!Object.hasOwnProperty.call(dest, key) || dest[key] === undefined) { dest[key] = value; } }); }, /** * Find the last index of an item in an array according to the given checker function. * * @example * import { utils } from 'js-data'; * * const john = { name: 'John', age: 20 }; * const sara = { name: 'Sara', age: 25 }; * const dan = { name: 'Dan', age: 20 }; * const users = [john, sara, dan]; * * console.log(utils.findIndex(users, (user) => user.age === 25)); // 1 * console.log(utils.findIndex(users, (user) => user.age > 19)); // 2 * console.log(utils.findIndex(users, (user) => user.name === 'John')); // 0 * console.log(utils.findIndex(users, (user) => user.name === 'Jimmy')); // -1 * * @method utils.findIndex * @param {array} array The array to search. * @param {Function} fn Checker function. * @returns {number} Index if found or -1 if not found. * @since 3.0.0 */ findIndex: function findIndex(array, fn) { var index = -1; if (!array) { return index; } array.forEach(function (record, i) { if (fn(record)) { index = i; return false; } }); return index; }, /** * Recursively iterate over a {@link Mapper}'s relations according to * `opts.with`. * * @method utils.forEachRelation * @param {Mapper} mapper Mapper. * @param {object} opts Configuration options. * @param {Function} fn Callback function. * @param {*} thisArg Execution context for the callback function. * @since 3.0.0 */ forEachRelation: function forEachRelation(mapper, opts, fn, thisArg) { var relationList = mapper.relationList || []; if (!relationList.length) { return; } relationList.forEach(function (def) { utils._forRelation(opts, def, fn, thisArg); }); }, /** * Iterate over an object's own enumerable properties. * * @example * import { utils } from 'js-data'; * const a = { b: 1, c: 4 }; * let sum = 0; * utils.forOwn(a, function (value, key) { * sum += value; * }); * console.log(sum); // 5 * * @method utils.forOwn * @param {object} object The object whose properties are to be enumerated. * @param {Function} fn Iteration function. * @param {object} [thisArg] Content to which to bind `fn`. * @since 3.0.0 */ forOwn: function forOwn(obj, fn, thisArg) { var keys = Object.keys(obj); var len = keys.length; var i; for (i = 0; i < len; i++) { if (fn.call(thisArg, obj[keys[i]], keys[i], obj) === false) { break; } } }, /** * Proxy for `JSON.parse`. * * @example * import { utils } from 'js-data'; * * const a = utils.fromJson('{"name" : "John"}'); * console.log(a); // { name: 'John' } * * @method utils.fromJson * @param {string} json JSON to parse. * @returns {Object} Parsed object. * @see utils.toJson * @since 3.0.0 */ fromJson: function fromJson(json) { return utils.isString(json) ? JSON.parse(json) : json; }, /** * Retrieve the specified property from the given object. Supports retrieving * nested properties. * * @example * import { utils } from 'js-data'; * const a = { foo: { bar: 'baz' }, beep: 'boop' }; * console.log(utils.get(a, 'beep')); // "boop" * console.log(utils.get(a, 'foo.bar')); // "baz" * * @method utils.get * @param {object} object Object from which to retrieve a property's value. * @param {string} prop Property to retrieve. * @returns {*} Value of the specified property. * @see utils.set * @since 3.0.0 */ get: function get(object, prop) { if (!prop) { return; } var parts = prop.split('.'); var last = parts.pop(); while (prop = parts.shift()) { // eslint-disable-line object = object[prop]; if (object == null) { // eslint-disable-line return; } } return object[last]; }, /** * Return the superclass for the given instance or subclass. If an instance is * provided, then finds the parent class of the instance's constructor. * * @example * import { utils } from 'js-data'; * // using ES2015 classes * class Foo {} * class Bar extends Foo {} * const barInstance = new Bar(); * let baseType = utils.getSuper(barInstance); * console.log(Foo === baseType); // true * * // using Function constructor with utils.extend * function Foo () {} * Foo.extend = utils.extend; * const Bar = Foo.extend(); * const barInstance = new Bar(); * let baseType = utils.getSuper(barInstance); * console.log(Foo === baseType); // true * * @method utils.getSuper * @param {Object|Function} instance Instance or constructor. * @param {boolean} [isCtor=false] Whether `instance` is a constructor. * @returns {Constructor} The superclass (grandparent constructor). * @since 3.0.0 */ getSuper: function getSuper(instance, isCtor) { var ctor = isCtor ? instance : instance.constructor; if (Object.hasOwnProperty.call(ctor, '__super__')) { return ctor.__super__; } return Object.getPrototypeOf(ctor) || ctor.__proto__; // eslint-disable-line }, /** * Return the intersection of two arrays. * * @example * import { utils } from 'js-data'; * const arrA = ['green', 'red', 'blue', 'red']; * const arrB = ['green', 'yellow', 'red']; * const intersected = utils.intersection(arrA, arrB); * * console.log(intersected); // ['green', 'red']) * * @method utils.intersection * @param {array} array1 First array. * @param {array} array2 Second array. * @returns {Array} Array of elements common to both arrays. * @since 3.0.0 */ intersection: function intersection(array1, array2) { if (!array1 || !array2) { return []; } array1 = Array.isArray(array1) ? array1 : [array1]; array2 = Array.isArray(array2) ? array2 : [array2]; var result = []; var item; var i; var len = array1.length; for (i = 0; i < len; i++) { item = array1[i]; if (result.indexOf(item) !== -1) { continue; } if (array2.indexOf(item) !== -1) { result.push(item); } } return result; }, /** * Proxy for `Array.isArray`. * * @example * import { utils } from 'js-data'; * const a = [1,2,3,4,5]; * const b = { foo: "bar" }; * console.log(utils.isArray(a)); // true * console.log(utils.isArray(b)); // false * * @method utils.isArray * @param {*} value The value to test. * @returns {boolean} Whether the provided value is an array. * @since 3.0.0 */ isArray: Array.isArray, /** * Return whether `prop` is matched by any string or regular expression in * `blacklist`. * * @example * import { utils } from 'js-data'; * const blacklist = [/^\$hashKey/g, /^_/g, 'id']; * console.log(utils.isBlacklisted("$hashKey", blacklist)); // true * console.log(utils.isBlacklisted("id", blacklist)); // true * console.log(utils.isBlacklisted("_myProp", blacklist)); // true * console.log(utils.isBlacklisted("my_id", blacklist)); // false * * @method utils.isBlacklisted * @param {string} prop The name of a property to check. * @param {array} blacklist Array of strings and regular expressions. * @returns {boolean} Whether `prop` was matched. * @since 3.0.0 */ isBlacklisted: function isBlacklisted(prop, blacklist) { if (!blacklist || !blacklist.length) { return false; } var matches; for (var i = 0; i < blacklist.length; i++) { if (toStr(blacklist[i]) === REGEXP_TAG && blacklist[i].test(prop) || blacklist[i] === prop) { matches = prop; return !!matches; } } return !!matches; }, /** * Return whether the provided value is a boolean. * * @example * import { utils } from 'js-data'; * const a = true; * const b = { foo: "bar" }; * console.log(utils.isBoolean(a)); // true * console.log(utils.isBoolean(b)); // false * * @method utils.isBoolean * @param {*} value The value to test. * @returns {boolean} Whether the provided value is a boolean. * @since 3.0.0 */ isBoolean: function isBoolean(value) { return toStr(value) === BOOL_TAG; }, /** * Return whether the provided value is a date. * * @example * import { utils } from 'js-data'; * const a = new Date(); * const b = { foo: "bar" }; * console.log(utils.isDate(a)); // true * console.log(utils.isDate(b)); // false * * @method utils.isDate * @param {*} value The value to test. * @returns {Date} Whether the provided value is a date. * @since 3.0.0 */ isDate: function isDate(value) { return value && _typeof(value) === 'object' && toStr(value) === DATE_TAG; }, /** * Return whether the provided value is a function. * * @example * import { utils } from 'js-data'; * const a = function () { console.log('foo bar'); }; * const b = { foo: "bar" }; * console.log(utils.isFunction(a)); // true * console.log(utils.isFunction(b)); // false * * @method utils.isFunction * @param {*} value The value to test. * @returns {boolean} Whether the provided value is a function. * @since 3.0.0 */ isFunction: function isFunction(value) { return typeof value === 'function' || value && toStr(value) === FUNC_TAG; }, /** * Return whether the provided value is an integer. * * @example * import { utils } from 'js-data'; * const a = 1; * const b = 1.25; * const c = '1'; * console.log(utils.isInteger(a)); // true * console.log(utils.isInteger(b)); // false * console.log(utils.isInteger(c)); // false * * @method utils.isInteger * @param {*} value The value to test. * @returns {boolean} Whether the provided value is an integer. * @since 3.0.0 */ isInteger: function isInteger(value) { return toStr(value) === NUMBER_TAG && value == toInteger(value); // eslint-disable-line }, /** * Return whether the provided value is `null`. * * @example * import { utils } from 'js-data'; * const a = null; * const b = { foo: "bar" }; * console.log(utils.isNull(a)); // true * console.log(utils.isNull(b)); // false * * @method utils.isNull * @param {*} value The value to test. * @returns {boolean} Whether the provided value is `null`. * @since 3.0.0 */ isNull: function isNull(value) { return value === null; }, /** * Return whether the provided value is a number. * * @example * import { utils } from 'js-data'; * const a = 1; * const b = -1.25; * const c = '1'; * console.log(utils.isNumber(a)); // true * console.log(utils.isNumber(b)); // true * console.log(utils.isNumber(c)); // false * * @method utils.isNumber * @param {*} value The value to test. * @returns {boolean} Whether the provided value is a number. * @since 3.0.0 */ isNumber: function isNumber(value) { var type = _typeof(value); return type === 'number' || value && type === 'object' && toStr(value) === NUMBER_TAG; }, /** * Return whether the provided value is an object. * * @example * import { utils } from 'js-data'; * const a = { foo: "bar" }; * const b = 'foo bar'; * console.log(utils.isObject(a)); // true * console.log(utils.isObject(b)); // false * * @method utils.isObject * @param {*} value The value to test. * @returns {boolean} Whether the provided value is an object. * @since 3.0.0 */ isObject: function isObject(value) { return toStr(value) === OBJECT_TAG; }, /** * Return whether the provided value is a regular expression. * * @example * import { utils } from 'js-data'; * const a = /^\$.+$/ig; * const b = new RegExp('^\$.+$', 'ig'); * const c = { foo: "bar" }; * console.log(utils.isRegExp(a)); // true * console.log(utils.isRegExp(b)); // true * console.log(utils.isRegExp(c)); // false * * @method utils.isRegExp * @param {*} value The value to test. * @returns {boolean} Whether the provided value is a regular expression. * @since 3.0.0 */ isRegExp: function isRegExp(value) { return toStr(value) === REGEXP_TAG; }, /** * Return whether the provided value is a string or a number. * * @example * import { utils } from 'js-data'; * console.log(utils.isSorN('')); // true * console.log(utils.isSorN(-1.65)); // true * console.log(utils.isSorN('my string')); // true * console.log(utils.isSorN({})); // false * console.log(utils.isSorN([1,2,4])); // false * * @method utils.isSorN * @param {*} value The value to test. * @returns {boolean} Whether the provided value is a string or a number. * @since 3.0.0 */ isSorN: function isSorN(value) { return utils.isString(value) || utils.isNumber(value); }, /** * Return whether the provided value is a string. * * @example * import { utils } from 'js-data'; * console.log(utils.isString('')); // true * console.log(utils.isString('my string')); // true * console.log(utils.isString(100)); // false * console.log(utils.isString([1,2,4])); // false * * @method utils.isString * @param {*} value The value to test. * @returns {boolean} Whether the provided value is a string. * @since 3.0.0 */ isString: function isString(value) { return typeof value === 'string' || value && _typeof(value) === 'object' && toStr(value) === STRING_TAG; }, /** * Return whether the provided value is a `undefined`. * * @example * import { utils } from 'js-data'; * const a = undefined; * const b = { foo: "bar"}; * console.log(utils.isUndefined(a)); // true * console.log(utils.isUndefined(b.baz)); // true * console.log(utils.isUndefined(b)); // false * console.log(utils.isUndefined(b.foo)); // false * * @method utils.isUndefined * @param {*} value The value to test. * @returns {boolean} Whether the provided value is a `undefined`. * @since 3.0.0 */ isUndefined: function isUndefined(value) { return value === undefined; }, /** * Mix in logging capabilities to the target. * * @example * import { utils } from 'js-data'; * const a = { foo: "bar"}; * * // Add standard logging to an object * utils.logify(a); * a.log('info', 'test log info'); // output 'test log info' to console. * * // Toggle debug output of an object * a.dbg('test debug output'); // does not output because debug is off. * a.debug = true; * a.dbg('test debug output'); // output 'test debug output' to console. * * @method utils.logify * @param {*} target The target. * @since 3.0.0 */ logify: function logify(target) { utils.addHiddenPropsToTarget(target, { dbg: function dbg() { if (utils.isFunction(this.log)) { for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } this.log.apply(this, ['debug'].concat(args)); } }, log: function log(level) { for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { args[_key4 - 1] = arguments[_key4]; } if (level && !args.length) { args.push(level); level = 'debug'; } if (level === 'debug' && !this.debug) { return; } var prefix = "".concat(level.toUpperCase(), ": (").concat(this.name || this.constructor.name, ")"); if (utils.isFunction(console[level])) { var _console; (_console = console)[level].apply(_console, [prefix].concat(args)); } else { var _console2; (_console2 = console).log.apply(_console2, [prefix].concat(args)); } } }); }, /** * Adds the given record to the provided array only if it's not already in the * array. * * @example * import { utils } from 'js-data'; * const colors = ['red', 'green', 'yellow']; * * console.log(colors.length); // 3 * utils.noDupeAdd(colors, 'red'); * console.log(colors.length); // 3, red already exists * * utils.noDupeAdd(colors, 'blue'); * console.log(colors.length); // 4, blue was added * * @method utils.noDupeAdd * @param {array} array The array. * @param {*} record The value to add. * @param {Function} fn Callback function passed to {@link utils.findIndex}. * @since 3.0.0 */ noDupeAdd: function noDupeAdd(array, record, fn) { if (!array) { return; } var index = this.findIndex(array, fn); if (index < 0) { array.push(record); } }, /** * Return a shallow copy of the provided object, minus the properties * specified in `keys`. * * @example * import { utils } from 'js-data'; * const a = { name: 'John', $hashKey: 1214910 }; * * let b = utils.omit(a, ['$hashKey']); * console.log(b); // { name: 'John' } * * @method utils.omit * @param {object} props The object to copy. * @param {string[]} keys Array of strings, representing properties to skip. * @returns {Object} Shallow copy of `props`, minus `keys`. * @since 3.0.0 */ omit: function omit(props, keys) { var _props = {}; utils.forOwn(props, function (value, key) { if (keys.indexOf(key) === -1) { _props[key] = value; } }); return _props; }, /** * Return a shallow copy of the provided object, but only include the * properties specified in `keys`. * * @example * import { utils } from 'js-data'; * const a = { name: 'John', $hashKey: 1214910 }; * * let b = utils.pick(a, ['$hashKey']); * console.log(b); // { $hashKey: 1214910 } * * @method utils.pick * @param {object} props The object to copy. * @param {string[]} keys Array of strings, representing properties to keep. * @returns {Object} Shallow copy of `props`, but only including `keys`. * @since 3.0.0 */ pick: function pick(props, keys) { return keys.reduce(function (map, key) { map[key] = props[key]; return map; }, {}); }, /** * Return a plain copy of the given value. * * @example * import { utils } from 'js-data'; * const a = { name: 'John' }; * let b = utils.plainCopy(a); * console.log(a === b); // false * * @method utils.plainCopy * @param {*} value The value to copy. * @returns {*} Plain copy of `value`. * @see utils.copy * @since 3.0.0 */ plainCopy: function plainCopy(value) { return utils.copy(value, undefined, undefined, undefined, undefined, true); }, /** * Shortcut for `utils.Promise.reject(value)`. * * @example * import { utils } from 'js-data'; * * utils.reject("Testing static reject").then(function (data) { * // not called * }).catch(function (reason) { * console.log(reason); // "Testing static reject" * }); * * @method utils.reject * @param {*} [value] Value with which to reject the Promise. * @returns {Promise} Promise reject with `value`. * @see utils.Promise * @since 3.0.0 */ reject: function reject(value) { return utils.Promise.reject(value); }, /** * Remove the last item found in array according to the given checker function. * * @example * import { utils } from 'js-data'; * * const colors = ['red', 'green', 'yellow', 'red']; * utils.remove(colors, (color) => color === 'red'); * console.log(colors); // ['red', 'green', 'yellow'] * * @method utils.remove * @param {array} array The array to search. * @param {Function} fn Checker function. */ remove: function remove(array, fn) { if (!array || !array.length) { return; } var index = this.findIndex(array, fn); if (index >= 0) { array.splice(index, 1); // todo should this be recursive? } }, /** * Shortcut for `utils.Promise.resolve(value)`. * * @example * import { utils } from 'js-data'; * * utils.resolve("Testing static resolve").then(function (data) { * console.log(data); // "Testing static resolve" * }).catch(function (reason) { * // not called * }); * * @param {*} [value] Value with which to resolve the Promise. * @returns {Promise} Promise resolved with `value`. * @see utils.Promise * @since 3.0.0 */ resolve: function resolve(value) { return utils.Promise.resolve(value); }, /** * Set the value at the provided key or path. * * @example * import { utils } from 'js-data'; * * const john = { * name: 'John', * age: 25, * parent: { * name: 'John's Mom', * age: 50 * } * }; * // set value by key * utils.set(john, 'id', 98); * console.log(john.id); // 98 * * // set value by path * utils.set(john, 'parent.id', 20); * console.log(john.parent.id); // 20 * * // set value by path/value map * utils.set(john, { * 'id': 1098, * 'parent': { id: 1020 }, * 'parent.age': '55' * }); * console.log(john.id); // 1098 * console.log(john.parent.id); // 1020 * console.log(john.parent.age); // 55 * * @method utils.set * @param {object} object The object on which to set a property. * @param {(string|Object)} path The key or path to the property. Can also * pass in an object of path/value pairs, which will all be set on the target * object. * @param {*} [value] The value to set. */ set: function set(object, path, value) { if (utils.isObject(path)) { utils.forOwn(path, function (value, _path) { utils.set(object, _path, value); }); } else { var parts = PATH.exec(path); if (parts) { mkdirP(object, parts[1])[parts[2]] = value; } else { object[path] = value; } } }, /** * Check whether the two provided objects are deeply equal. * * @example * import { utils } from 'js-data'; * * const objA = { * name: 'John', * id: 27, * nested: { * item: 'item 1', * colors: ['red', 'green', 'blue'] * } * }; * * const objB = { * name: 'John', * id: 27, * nested: { * item: 'item 1', * colors: ['red', 'green', 'blue'] * } * }; * * console.log(utils.deepEqual(a,b)); // true * objB.nested.colors.add('yellow'); // make a change to a nested object's array * console.