UNPKG

chain-able

Version:

interfaces that describe their intentions.

2,108 lines (1,956 loc) 178 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.ChainAble = factory()); }(this, (function () { 'use strict'; function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } /** * @desc Checks if `value` is `undefined`. * @category Lang * * @param {*} x value * @return {boolean} isUndefined * * @since 4.0.0-alpha.1 * @memberOf is * @func isUndefined * * @see is/nullOrUndefined * @see https://github.com/infernojs/inferno/blob/master/packages/inferno-shared/src/index.ts#L57 * * @NOTE || typeof x === 'undefined' * * @example * * isUndefined(undefined) * //=> true * isUndefined(void 0) * //=> true * * isUndefined(null) * //=> false * isUndefined(NaN) * //=> false * isUndefined({}) * //=> false * isUndefined('') * //=> false * isUndefined(1) * //=> false * isUndefined(false) * //=> false * */ var _undefined = x => x === undefined; var iterator = Symbol.iterator; // typeof Symbol !== 'undefined' // ? Symbol.iterator // : '@@iterator' var instance = Symbol.hasInstance; var primitive = Symbol.toPrimitive; var prototypeOf = (obj, comparator) => Object.prototype.isPrototypeOf.call(obj, comparator); /** * The base implementation of `getTag` without fallbacks for buggy environments. * * @memberOf is * * @param {*} value The value to query. * @return {string} Returns the `toStringTag`. * * @see https://github.com/lodash/lodash/blob/master/.internal/baseGetTag.js * @TODO obj[Symbol.toStringTag] */ var toS = obj => Object.prototype.toString.call(obj); /** * @desc Checks if `value` is classified as a `Map` object. * @param {*} x value * @return {boolean} isMap * * @since 3.0.0 * @memberOf is * @func isMap * @see https://github.com/jonschlinkert/kind-of * * @example * * isMap(new Map()) * //=> true * isMap(new Map.entries()) * //=> false * isMap(new Set()) * //=> false * isMap({}) * //=> false * isMap('') * //=> false * isMap(1) * //=> false * isMap(new WeakMap) * // => false * * @example * * const e = {} * eh[Symbol.toStringTag] = '[object Map]' * isMap(eh) * * @example * * class Eh extends Map() * isMap(new Eh()) * //=> true * */ var map = x => x instanceof Map || toS(x) === '[object Map]'; /** * Checks if `value` is classified as a `Set` object. * * @since 4.3.0 * @category Lang * @param {*} x The value to check. * @return {boolean} Returns `true` if `value` is a set, else `false`. * * @example * * isSet(new Set) * // => true * * isSet(new WeakSet) * // => false * */ var set = x => x instanceof Set || toS(x) === '[object Set]'; /** * Checks if `value` is classified as a `Function` object. * @category Lang * * @param {*} x The value to check. * @return {boolean} x isFunction * * @since 3.0.0 * @memberOf is * @func isFunction * * @NOTE || x instanceof Function * * @polyfill safari=9 * The use of `Object#toString` avoids issues with the `typeof` operator * in Safari 9 which returns 'object' for typed arrays and other constructors. * there is no polyfill for this * https://github.com/krambuhl/custom-event-polyfill/issues/2 * browser usage is < 0.3%, very edge case * * @example * * isFunction(function() {}) * //=> true * isFunction(() => {}) * //=> true * isFunction(new Function()) * //=> true * * isFunction(1) * //=> false * isFunction('') * //=> false * isFunction(/abc/) * // => false */ var _function = x => typeof x === 'function'; /** * Checks if `value` is classified as a `String` **primitive**. * * @since 3.0.0 * @category Lang * @memberOf is * @param {*} x The value to check. * @returns {boolean} Returns `true` if `value` is a string, else `false`. * * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String * @see https://github.com/lodash/lodash/blob/master/isString.js * @see is/string * * @example * * isString('abc') * // => true * * isString(new String('abc')) * // => false * * isString(1) * // => false */ var stringPrimitive = x => typeof x === 'string'; /** * Checks if `value` is classified as a `String` primitive or object. * * @since 3.0.0 * @category Lang * * @memberOf is * @extends isStringPrimitive * @variation also allows String objects * * @param {*} x The value to check. * @return {boolean} Returns `true` if `value` is a string, else `false`. * * @see https://github.com/lodash/lodash/blob/master/isString.js * @see isStringPrimitive * * @example * * isString('abc') * // => true * * isString(new String('abc')) * // => true * * isString(1) * // => false */ var string = x => stringPrimitive(x) || toS(x) === '[object String]'; /** * @param {*} x value * @return {boolean} isFalse * * @since 4.0.0-alpha.1 * @memberOf is * @func isFalse * * @example * * isFalse(false) * //=> true * isFalse(true) * //=> false * isFalse(0) * //=> false * isFalse('') * //=> false * */ var _false = function isFalse(x) { return x === false }; var keys = Object.keys; var assign = Object.assign; /** * @desc default to configurable and enumerable, unless configured otherwise * @since 4.0.0 * * @param {Object} obj object to define on * @param {Primitive} name property name to define * @param {Object} descriptor object descriptor * @return {void} * * @tutorial https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty * * @example * * var desc = Object.getOwnPropertyDescriptor(obj, 'eh', {get: () => console.log('eh')}) * */ var define = function(obj, name, descriptor) { Object.defineProperty( obj, name, assign( { configurable: true, enumerable: true, }, descriptor ) ); }; var ignored = key => key === 'parent' || key === 'store' || key === 'meta' || key === 'className'; // key === 'decorated' || // key === 'transformers' || // key === 'inspect' || /* istanbul ignore next: wip build */ var dev = process.env.NODE_ENV !== 'production'; const shouldClear = (key, property) => !ignored(key) && (map(property) || set(property) || (property && property.store)); const C = SuperClass => { /* istanbul ignore next: dev */ if (dev) { if (!SuperClass || !SuperClass.prototype) { console.log({SuperClass}); throw new TypeError('did not have a super class / target base') } } /** * @desc Trait class that can inherit any class passed into compose, extended by ChainedMap & ChainedSet * * @member Chainable * @class Chainable * @category Chainable * @type {Chainable} * * @prop {Chainable | any} parent * @prop {string} className * * {@link https://github.com/iluwatar/java-design-patterns/tree/master/chain chain-pattern} * @see {@link chain-pattern} * * @see ChainedMap * @see ChainedSet * * @tests Chainable * @types Chainable */ class Chainable extends SuperClass { /** * @since 0.0.1 * @param {Chainable | any | ParentType} parent ParentType * @constructor * * @example * * class ChainedMap extends Chainable {} * const map = new ChainedMap() * map.className * //=> ChainedMap */ constructor(parent) { super(); if (parent) this.parent = parent; this.className = this.constructor.name; } /** * @desc Iterator for looping values in the store * * @since 0.5.0 * @see this.store * @type {generator} * @return {Object} {value: undefined | any, done: true | false} * * @NOTE assigned to a variable so buble ignores it * @see https://github.com/sindresorhus/quick-lru/blob/master/index.js * @see https://stackoverflow.com/questions/36976832/what-is-the-meaning-of-symbol-iterator-in-this-context * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator * @tests iteration * * @example * * const chain = new Chain().set('eh', 1) * for (var [key, val] of chain) console.log({[key]: val}) * //=> {eh: 1} * * @example * * *[Symbol.iterator](): void { for (const item of this.store) yield item } * * @example * * const {ChainedSet} = require('chain-able') * const set = new ChainedSet() * set.add('eh') * * for (const arr of set) { * const [key, val] = arr * * key * //=> 0 * * val * //=> 'eh' * * arr.length * //=> 2 * } * */ [iterator]() { const values = this.values(); const size = this.store.size; const entries = this.entries ? this.entries() : 0; const keys$$1 = entries === 0 ? new Array(size) : keys(entries); return { i: 0, next() { let i = this.i; let key = i; const val = values[i]; if (entries) key = keys$$1[i]; // done - no more values, or iteration reached size if ((_undefined(key) && _undefined(val)) || size <= i) { return {value: undefined, done: true} } this.i++; // return return {value: [key, val], done: false} }, } } /** * @desc for ending nested chains * @since 0.4.0 * @return {Chainable | any} * @see Chainable.parent * @see FactoryChain * * @example * * const parent = 'eh' * const child = newChain(parent) * child.end() * //=> 'eh' * */ end() { return this.parent } /** * @desc when the condition is true, * trueBrancher is called, * else, falseBrancher is called * * @since 4.0.0 <- added string-as-has(condition) * @since 2.0.0 * * @param {boolean | string} condition when string, checks this.get * @param {Function} [trueBrancher=Function] called when true * @param {Function} [falseBrancher=Function] called when false * @return {Chainable} @chainable * * @example * * * const prod = process.env.NODE_ENV === 'production' * chains.when(prod, c => c.set('prod', true), c => c.set('prod', false)) * * */ when(condition, trueBrancher, falseBrancher) { if (condition) { if (_function(trueBrancher)) { if (string(condition)) { if (this.get(condition)) { trueBrancher(this); } } else { trueBrancher(this); } } } else if (_function(falseBrancher)) { falseBrancher(this); } return this } /** * @desc clears the map, * goes through this properties, * calls .clear if they are instanceof Chainable or Map * * @since 4.0.0 (moved only to Chainable, added option to clear this keys) * @since 0.4.0 (in ChainedMap) * @since 0.3.0 (in Chainable) * * @param {boolean | undefined} [clearPropertiesThatAreChainLike=true] checks properties on the object, if they are `chain-like`, clears them as well * @return {Chainable} @chainable * * @see https://github.com/fliphub/flipchain/issues/2 * @see ChainedSet * @see ChainedMap * * @example * * const chain = new Chain() * chain.set('eh', 1) * chain.entries() * //=> {eh: 1} * chain.clear() * chain.entries() * //=> {} * */ clear(clearPropertiesThatAreChainLike) { this.store.clear(); if (_false(clearPropertiesThatAreChainLike)) return this const keys$$1 = keys(this); for (let k = 0; k < keys$$1.length; k++) { const key = keys$$1[k]; const property = this[key]; if (shouldClear(key, property)) this[key].clear(); } return this } /** * @desc calls .delete on this.store.map * @since 0.3.0 * * @param {Primitive} key on a Map: key referencing the value. on a Set: the index * @return {Chainable} * * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/has * @see ChainedSet * @see ChainedMap * * @example * * const chain = new Chain() * chain.set('eh', 1) * chain.get('eh') * // => 1 * chain.delete('eh', 1) * chain.get('eh') * // => undefined * */ delete(key) { this.store.delete(key); return this } /** * @since 0.3.0 * @param {any} keyOrValue key when Map, value when Set * @return {boolean} * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has * * @example * * const chain = new Chain() * chain.set('eh', 1).has('eh') * //=> true * chain.has('canada') * //=> false * */ has(keyOrValue) { return this.store.has(keyOrValue) } /** * @desc spreads the entries from ChainedMap.store.values * @since 0.4.0 * * @return {Array<any>} toArr(this.store.values()) * * @NOTE look at Chainable.constructor to ensure not to use `new Array...` * @NOTE moved from ChainedMap and ChainedSet to Chainable @2.0.2 * @NOTE this was [...] & Array.from(this.store.values()) * * {@link https://kangax.github.io/compat-table/es6/#test-Array_static_methods compat-array-static-methods} * {@link https://stackoverflow.com/questions/20069828/how-to-convert-set-to-array set-to-array} * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values mozilla-map-values} * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/values mozilla-set-values} * * @see {@link mozilla-map-values} * @see {@link mozilla-set-values} * @see {@link compat-array-static-methods} * @see {@link set-to-array} * * @example * * const chain = new Chain() * chain.set('eh', 1) * chain.values() * //=> [1] * */ values() { const vals = []; this.store.forEach(v => vals.push(v)); return vals } /** * @see http://2ality.com/2015/09/well-known-symbols-es6.html#default-tostring-tags * @since 1.0.2 * * @param {string} hint enum[default, string, number] * @return {Primitive} * * @example * * const chain = new Chain() * chain.toNumber = () => 1 * +chain; * //=> 1 * chain + 1 * //=> * * @example * * const chain = new Chain() * chain.toString = () => 'eh' * chain + '' * //=> 'eh' * */ [primitive](hint) { /* prettier-ignore */ /** * hint === 'number' * `s`tring is 115 * `n`umber is 110 * 110 & 4 = 1 * 115 & 4 = 0 * * if (hint === 'string' && this.toJSON) return this.toJSON() * else if (hint === 'number' && this.toNumber) return this.toNumber() */ if (hint === 'number' && this.toNumber) return this.toNumber() // hint === 'string' if (this.toJSON) return this.toJSON() // hint === 'default' return this.toString() } } const ChainPrototype = Chainable.prototype; /** * @private * @since 0.5.0 * @example for (var i = 0; i < chain.length; i++) * @see ChainedMap.store * @return {number} */ define(ChainPrototype, 'length', { enumerable: false, get() { return this.store.size }, }); define(ChainPrototype, instance, { enumerable: false, value: instance$$1 => instance$$1 && (prototypeOf(ChainPrototype, instance$$1) || instance$$1.store), }); return Chainable }; const c = C(class {}); /** * @since 3.0.0 * @func * @example * * class Target {} * const TargetChain = Chainable.compose(Target) * const chain = new TargetChain() * chain instanceof Target * //=> true * */ c.compose = C; var Chainable = c; /** * @param {*} x value * @return {boolean} isObjLoose * * @since 3.0.0 * @memberOf is * @func isObjLoose * @see is/obj * @see is/objWithKeys * @see is/objStrict * @see is/null * * @example * * isObjLoose(new Object()) * //=> true * isObjLoose({}) * //=> true * isObjLoose(Object.create(null)) * //=> true * isObjLoose(null) * //=> true * * isObjLoose(new Set()) * //=> false * isObjLoose(function() {}) * //=> false * isObjLoose('') * //=> false * isObjLoose(1) * //=> false * */ var objLoose = x => typeof x === 'object'; /** * @param {*} x value * @return {boolean} isNull * * @since 3.0.0 * @memberOf is * @func isNull * * @example * * isNull(null) * //=> true * * isNull(undefined) * //=> false * isNull(void 0) * //=> false * isNull({}) * //=> false * isNull('') * //=> false * isNull(1) * //=> false * */ var _null = x => x === null; /** * @desc Checks if `value` is `null` or `undefined`. * @alias isNil * @category Lang * * @param {*} x value * @return {boolean} isNullOrUndefined * * @since 4.0.0-alpha.1 * @memberOf is * @func isNullOrUndefined * * @see is/null * @see is/undefined * @see https://github.com/infernojs/inferno/blob/master/packages/inferno-shared/src/index.ts#L23 * * @example * * isNullOrUndefined(null) * //=> true * isNullOrUndefined(undefined) * //=> true * isNullOrUndefined(void 0) * //=> true * * isNullOrUndefined(NaN) * //=> false * isNullOrUndefined({}) * //=> false * isNullOrUndefined('') * //=> false * isNullOrUndefined(1) * //=> false * isNullOrUndefined(false) * //=> false * */ var nullOrUndefined = function isNullOrUndef(x) { return _undefined(x) || _null(x) }; /** * @param {*} x value * @return {boolean} isObjStrict * * @since 3.0.0 * @memberOf is * @func isObjStrict * @see is/obj * @see is/objWithKeys * @see is/objLoose * @see is/null * @see https://github.com/sindresorhus/is-obj/blob/master/index.js * @TODO !Array.isArray * * @extends isObjLoose * @variation null will not count as an object * * @example * * isObjStrict(new Object()) * //=> true * isObjStrict({}) * //=> true * isObjStrict(Object.create(null)) * //=> true * isObjStrict(null) * //=> false * * isObjStrict(new Set()) * //=> false * isObjStrict(function() {}) * //=> false * isObjStrict('') * //=> false * isObjStrict(1) * //=> false * */ var objStrict = x => !nullOrUndefined(x) && objLoose(x); /** * @func isArray * @see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray * @type {Function} * @since 3.0.0 */ var array = Array.isArray; /** * @param {*} x value * @return {boolean} isTrue * * @since 4.0.0-alpha.1 * @memberOf is * @func isTrue * * @example * * isTrue(true) * //=> true * isTrue(false) * //=> false * isTrue(1) * //=> false * isTrue('') * //=> false * */ var _true = x => x === true; /** * Checks if `value` is classified as a `RegExp` object. * * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @return {boolean} Returns `true` if `value` is a regexp, else `false`. * @see https://github.com/lodash/lodash/blob/master/isRegExp.js * * @example * * isRegExp(/abc/) * // => true * * isRegExp('/abc/') * // => false * */ var regexp = obj => obj instanceof RegExp || toS(obj) === '[object RegExp]'; /** * @param {*} x value * @return {boolean} isDate * * @since 3.0.0 * @memberOf is * @func isDate * * @example * * isDate(new Date()) * //=> true * isDate(Date.now()) * //=> false * isDate(1) * //=> false * isDate('') * //=> false * * @example * * const e = {} * eh[Symbol.toStringTag] = '[Object Date]' * isDate(eh) * //=> true * * @example * * class Eh extends Date() * isDate(new Eh()) * //=> true */ var date = x => x instanceof Date || toS(x) === '[object Date]'; /** * @desc Checks if `value` is classified as a boolean primitive or object. * @category Lang * @since 3.0.0 * * @param {*} x value * @return {boolean} isBoolean * * @extends isTrue * @extends isFalse * @see is/toS * @memberOf is * @func isBoolean * * @NOTE could also have typeof x === 'boolean' || (/true|false/).test(x) * * @example * * isBoolean(false) * //=> true * isBoolean(new Boolean(1)) * //=> true * isBoolean(1) * //=> false * isBoolean('') * //=> false * */ var boolean_1 = x => _true(x) || _false(x) || toS(x) === '[object Boolean]'; /* prettier-ignore */ /** * @desc when Array -> 'array' * when null -> 'null' * else `typeof x` * @param {any} x * @return {string} type */ var simpleKindOf = x => { return array(x) ? 'array' : _null(x) ? 'null' : typeof x }; var includes = (haystack, needle) => haystack.includes(needle); var index$4 = includes; // 1: not null object // 2: object toString is not a date or regex function isMergeableObj(val) { return objStrict(val) && !regexp(val) && !date(val) } function emptyTarget(val) { return array(val) ? [] : {} } function cloneIfNeeded(value, optsArg) { return _true(optsArg.clone) && isMergeableObj(value) ? deepmerge(emptyTarget(value), value, optsArg) : value } /* prettier-ignore */ function defaultArrayMerge(target, source, optsArg) { var destination = target.slice(); for (var i = 0; i < source.length; i++) { var v = source[i]; if (_undefined(destination[i])) { destination[i] = cloneIfNeeded(v, optsArg); } else if (isMergeableObj(v)) { destination[i] = deepmerge(target[i], v, optsArg); } // @see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_NOT // === -1 // eslint-disable-next-line prefer-includes/prefer-includes else if (!~target.indexOf(v)) { destination.push(cloneIfNeeded(v, optsArg)); } } return destination } function mergeObj(target, source, optsArg) { var destination = {}; if (isMergeableObj(target)) { var targetKeys = keys(target); for (var k = 0; k < targetKeys.length; k++) { destination[targetKeys[k]] = cloneIfNeeded(target[targetKeys[k]], optsArg); } } var sourceKeys = keys(source); for (var s = 0; s < sourceKeys.length; s++) { var key = sourceKeys[s]; if (!isMergeableObj(source[key]) || !target[key]) { destination[key] = cloneIfNeeded(source[key], optsArg); } else { destination[key] = deepmerge(target[key], source[key], optsArg); } } return destination } function deepmerge(target, source, optsArg) { if (array(source)) { const {arrayMerge} = optsArg; return array(target) ? arrayMerge(target, source, optsArg) : cloneIfNeeded(source, optsArg) } // else return mergeObj(target, source, optsArg) } /* prettier-ignore */ // eslint-disable-next-line complexity function dopemerge(obj1, obj2, opts) { // if they are identical, fastest === check if (obj1 === obj2) { return obj1 } // setup options const options = assign( { arrayMerge: defaultArrayMerge, stringToArray: true, boolToArray: false, ignoreTypes: ['null', 'undefined'], // debug: true, }, opts || {} ); const {ignoreTypes, stringToArray, boolToArray, clone} = options; // @NOTE: much better size but oh well // const ignoreTypes = ['null', 'undefined'] // const stringToArray = true // const boolToArray = false // const clone = true // check one then check the other if (_true(index$4(ignoreTypes, simpleKindOf(obj1)))) { return obj2 } else if (_true(index$4(ignoreTypes, simpleKindOf(obj2)))) { return obj1 } // @NOTE uglifier optimizes into a wicked ternary else if (boolean_1(obj1) && boolean_1(obj2)) { return boolToArray ? [obj1, obj2] : obj2 } else if (string(obj1) && string(obj2)) { return stringToArray ? [obj1, obj2] : obj1 + obj2 } else if (array(obj1) && string(obj2)) { return (clone ? obj1.slice(0) : obj1).concat([obj2]) } else if (string(obj1) && array(obj2)) { return (clone ? obj2.slice(0) : obj2).concat([obj1]) } else { return deepmerge(obj1, obj2, options) } } var dopemerge_1 = dopemerge; var index$2 = dopemerge_1; /** * @tutorial https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from * @see https://github.com/lodash/lodash/blob/master/.internal/setToArray.js * ^ could use if needed */ var from = Array.from; /** * @desc Map -> Object * @since 4.0.0 * * @param {Map} map map to reduce, calls entries, turns into an array, then object * @return {Object} reduced object * * @see ArrayFrom * * @example * * var emptyMap = new Map() * reduce(emptyMap) * // => {} * * @example * * var map = new Map() * map.set('eh', 1) * reduce(map) * // => {eh: 1} * */ var reduce = map => { let reduced = {}; // only need to do this if we actually have values in our Map if (map.size !== 0) { reduced = from(map.entries()).reduce((acc, [key, value]) => { acc[key] = value; return acc }, {}); } return reduced }; var index$6 = reduce; /** * @desc recursively reduce maps and objects that include reducable data * @since 4.0.0 * * @sig reduced => object => isMap(object) -> reduced; merge(object, reduced) * * @param {Object | any} reduced merged object and reduced * @return {Function} Function(values: Object) * * @see ChainedMap * * @example * * const map = new Map() * map.set('eh', true) * const nested = new Map() * nested.set('reduced', true) * * const chain = { * entries() { * return { * nested: reduce(nested), * key: true, * } * }, * } * const reduced = reduce(map) * reduceEntries(reduced)({chain}) * // => { * eh: true, * chain: { * nested: { * reduced: true, * key: true, * }, * }, * } * * @example * * const reducedIgnored = { * canada: { * store: chain, * }, * } * const ignored = reduceEntries(reduced)(reducedIgnored) * //=> { * eh: true, * chain: { * nested: { * reduced: true, * }, * key: true, * }, * } * */ var entries = reduced => obj => { const keys$$2 = keys(obj); for (let k = 0; k < keys$$2.length; k++) { const key = keys$$2[k]; if (ignored(key)) { continue } const val = obj[key]; if (val && _function(val.entries)) { assign(reduced, {[key]: val.entries(true) || {}}); } } return reduced }; /** * @param {*} x value * @return {boolean} isIterator * * @since 3.0.0 * @memberOf is * @func isIterator * @see https://github.com/jonschlinkert/kind-of/pull/12 * * @example * * isIterator(new Set().values()) * //=> true * isIterator(new Map.entries()) * //=> true * isIterator(new Map()) * //=> false * isIterator('') * //=> false * isIterator(1) * //=> false * * @example * * const e = {} * eh[Symbol.toStringTag] = '[Map Iterator]' * isIterator(eh) * //=> true * eh[Symbol.toStringTag] = '[Set Iterator]' * isIterator(eh) * //=> true * * @example * * class Eh extends Set() * isIterator(new Eh().values()) * //=> true * */ // eslint-disable-next-line var iterator$2 = x => ~toS(x).indexOf('Iterator'); /** * @desc anything into an array * @sig * => Array * @since 0.0.1 * * @param {any} ar turn this into an array * @return {Array} anything into an array * * @tests deps/to-arr * @types deps * * @example * * toarr([]) * // => [] * * toarr('') * // => [''] * * toarr('1,2') * // => ['1', '2'] * * toarr('1,2') * // => ['1', '2'] * * const map = new Map() * map.set('eh', true) * const arr = toarr(map.entries()) * // => ['eh', true] * * const set = new Set() * set.add('eh') * set.add(true) * const arr = toarr(map.entries()) * // => ['eh', true] * * toarr('').concat(toarr(false)).concat(toarr(null)) * // => ['', false, null] * */ var toArr = function(ar) { // @NOTE: !'' === true if (stringPrimitive(ar)) return ar.includes(',') ? ar.split(',') : [ar] else if (!ar) return [ar] else if (array(ar)) return ar else if (set(ar) || map(ar) || ar.values) { /** * @desc when using `new Set().values`... no forEach o.o * .values is also on `Object`... */ return from(ar.values(ar)) } else if (iterator$2(ar)) return from(ar) else return [ar] }; var concat = (one, two) => toArr(one || []).concat(toArr(two)); /* istanbul ignore next: wip build */ var transformers = process.env.NODE_ENV === 'production' ? 'transformers' : 'transformers'; /* istanbul ignore next: wip build */ var observers = process.env.NODE_ENV === 'production' ? 'observers' : 'observers'; /* istanbul ignore next: wip build */ var shorthands = process.env.NODE_ENV === 'production' ? 'shorthands' : 'shorthands'; /* istanbul ignore next: wip build */ var decorated = process.env.NODE_ENV === 'production' ? 'decorated' : 'decorated'; // will expand this later const isInKeyMapAsSet = x => x === observers; // @NOTE: using `[]` deopts o.o // eslint-disable-next-line // this.shorthands = new Array() /** * @since 4.0.0 * @param {Chain} _this * @return {Chain} */ function getMeta(_this) { // if we already have it, keep it if (_this.meta) return _this.meta // the store // shorthands: key -> method const store = {}; // --- uglifiable functions /** @desc initialize the store maps when we need them */ /* prettier-ignore */ const ensureInitialized = (name, value) => { if (!_undefined(store[name])) return // if ( // name === TRANSFORMERS_KEY || // name === SHORTHANDS_KEY || // name === DECORATED_KEY // ) { // store[name] = new Map() // } // else if (isInKeyMapAsSet(name)) { store[name] = new Set(); } else { store[name] = new Map(); } }; /** * @since 4.0.0 * @param {Primitive} key * @param {Primitive | undefined} [prop=undefined] * @return {boolean} */ const has = (key, prop) => { if (_undefined(prop)) return !!store[key].size else return store[key].has(prop) }; /** * @since 4.0.0 * @param {Primitive} key * @param {Primitive | undefined} [prop=undefined] * @return {any} */ const get = (key, prop) => (has(key, prop) ? store[key].get(prop) : []); /** * @since 4.0.0 * @param {Primitive} key * @param {Primitive | undefined} [prop=undefined] * @param {Primitive | undefined} [value=undefined] * @return {void} */ const set$$2 = (key, prop, value) => { const storage = store[key]; // when it's a set, we have no `prop`, we just have .add // so `prop = value` && `value = undefined` if (set(storage)) { storage.add(prop); } else { // if (!has(key, prop)) return const existing = storage.get(prop); const val = concat(existing, value); storage.set(prop, val); } }; /** * @since 4.0.0 * * @desc a single easily minifiable function, * dynamically setting & getting depending on arguments * to avoid nested property accessing * only instantiating when values are **addded** * * @param {Primitive} key * @param {Primitive | undefined} [prop=undefined] * @param {undefined | any} [value=undefined] (when no value, it's a getter) * @return {Array | Chain} depending on args */ function meta(key, prop, value) { if (process.env.NODE_ENV === 'DEBUG') { console.log('USING META', {key, prop, value}); } /* prettier-ignore */ if (_undefined(value)) { // when we want to just access the property, return an array // @example `.meta('transformers')` if (_undefined(prop)) { if (_undefined(store[key])) return [] else return store[key].size === 0 ? [] : from(store[key].values()) } // we have `key, prop` // // 1: should `prop` be a value, (isSet?) else if (isInKeyMapAsSet(key)) { ensureInitialized(key); set$$2(key, prop); } // 2: prop is a key, we want to return the [..] for that specific property // @example `.meta('transformers', 'eh')` else if (_undefined(store[key])) return [] else return toArr(get(key, prop)) } // we have `key, prop, value` else { ensureInitialized(key); // we have a value, let's add it set$$2(key, prop, value); } return _this } // for debugging meta.store = store; // meta.debug = false return meta } var meta = getMeta; var index$8 = meta; /** * @desc ChainedMapBase composer * @alias ComposeMap * @type {Composer} * @method compose * @memberOf ChainedMapBase * * @param {Class | Object | Composable} [SuperClass=Chainable] class to extend * @return {Class} ChainedMapBase * * @example * * const heh = class {} * const composed = ChainedMapBase.compose(heh) * const hehchain = new Composed() * hehchain instanceof heh * //=> true * */ const CMC = SuperClass => { /** * @classdesc this is to avoid circular requires * because MergeChain & MethodChain extend this * yet .method & .merge use those chains * * @since 4.0.0-alpha.1 * @inheritdoc * @class ChainedMapBase * @member ChainedMapBase * @category Chainable * @extends {Chainable} * @type {Chainable} * * @types ChainedMapBase * @tests ChainedMap * * @prop {Meta} meta * @prop {Map} store * * {@link https://ponyfoo.com/articles/es6-maps-in-depth pony-map} * {@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map mozilla-map} * @see {@link pony-map} * @see {@link mozilla-map} * * @see ChainedMap * @see Chainable * @see MergeChain * @see MethodChain * @see ChainedMap * */ return class ChainedMapBase extends SuperClass { /** * @param {ChainedMapBase | Chainable | ParentType | any} parent ParentType * @constructor * * @example * * class Eh extends ChainedMapBase {} * Object.keys(eh) * //=> ['store', 'meta'] * */ constructor(parent) { super(parent); this.store = new Map(); this.meta = index$8(this); } /** * @desc tap a value with a function * @modifies this.store.get(name) * @memberOf ChainedMapBase * @since 0.7.0 * @since 4.0.0-alpha.1 <- moved from transform & shorthands * * @param {string | any} name key to `.get` * @param {Function} fn function to tap with * @return {Chain} @chainable * * {@link https://github.com/sindresorhus/awesome-tap awesome-tap} * {@link https://github.com/midknight41/map-factory map-factory} * {@link https://github.com/webpack/tapable tapable} * @see {@link tapable} * * @see ChainedMapBase.set * @see ChainedMapBase.get * * @example * * chain * .set('moose', {eh: true}) * .tap('moose', moose => {moose.eh = false; return moose}) * .get('moose') * * // => {eh: false} * * @example * * const entries = new Chain() * .set('str', 'emptyish') * .tap('str', str => str + '+') * .set('arr', [1]) * .tap('arr', arr => arr.concat([2])) * .entries() * * //=> {str: 'emptyish+', arr: [1, 2]} * */ tap(name, fn) { return this.set(name, fn(this.get(name), index$2)) } /** * @desc checks each property of the object * calls the chains accordingly * * @memberOf ChainedMapBase * @since 0.5.0 * * @param {Object} obj object with functions to hydrate from * @return {Chainable} @chainable * * @TODO could also add parsing stringified * * @example * * const from = new Chain().from({eh: true}) * const eh = new Chain().set('eh', true) * eq(from, eh) * // => true * */ from(obj) { const keys$$1 = keys(obj); for (let k = 0; k < keys$$1.length; k++) { const key = keys$$1[k]; const val = obj[key]; const fn = this[key]; if (fn && fn.merge) { fn.merge(val); } else if (_function(fn)) { fn.call(this, val); } else { this.set(key, val); } } return this } /** * @desc shorthand methods, from strings to functions that call .set * @since 0.4.0 * @memberOf ChainedMapBase * * @param {Array<string>} methods decorates/extends an object with new shorthand functions to get/set * @return {ChainedMapBase} @chainable * * @example * * const chain1 = new Chain() * chain1.extend(['eh']) * * const chain2 = new Chain() * chain2.eh = val => this.set('eh', val) * * eq(chain2.eh, chain1.eh) * //=> true * */ extend(methods) { methods.forEach(method => { this.meta(shorthands, method); this[method] = value => this.set(method, value); }); return this } /** * @desc spreads the entries from ChainedMapBase.store (Map) * return store.entries, plus all chain properties if they exist * @memberOf ChainedMapBase * * @since 4.0.0 <- improved reducing * @since 0.4.0 * * @param {boolean} [chains=false] if true, returns all properties that are chains * @return {Object} * * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries mozilla-map-entries} * @see {@link mozilla-map-entries} * * @example * * map.set('a', 'alpha').set('b', 'beta').entries() * //=> {a: 'alpha', b: 'beta'} * */ entries(chains = false) { const reduced = index$6(this.store); if (chains === false) return reduced const reducer = entries(reduced); reducer(this); reducer(reduced); return reduced } /** * @desc get value for key path in the Map store * ❗ `debug` is a special key and is *not* included into .store * it goes onto .meta * * @memberOf ChainedMapBase * @since 4.0.0 <- moved debug here * @since 0.4.0 * * @param {Primitive} key Primitive data key used as map property to reference the value * @return {any} value in .store at key * * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get mozilla-map-get} * @see {@link mozilla-map-get} * * @example * * const chain = new Chain() * chain.set('eh', true) * chain.get('eh') * //=> true * * chain.get('nope') * //=> undefined * */ get(key) { if (key === 'debug') return this.meta.debug return this.store.get(key) } /** * @desc sets the value using the key on store * adds or updates an element with a specified key and value * * @memberOf ChainedMapBase * @since 0.4.0 * * @param {Primitive} key Primitive to reference the value * @param {any} value any data to store * @return {ChainedMapBase} @chainable * * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set mozilla-map-set} * @see {@link mozilla-map-set} * @see ChainedMapBase.store * * @example * * const chain = new Chain() * chain.set('eh', true) * chain.get('eh') * //=> true * */ set(key, value) { this.store.set(key, value); return this } } }; const cmc = CMC(Chainable); cmc.compose = CMC; var ChainedMapBase_1 = cmc; var debug = process.env.NODE_ENV === 'debug'; // || process.env.DEBUG = true /** * @func isObj * * Checks if `value` is the * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @since 3.0.0 * @category Lang * @param {*} value The value to check. * @return {boolean} Returns `true` if `value` is an object, else `false`. * * @memberOf is * @see https://github.com/lodash/lodash/blob/master/isObject.js * @NOTE Object.prototype.toString.call(val) === '[object Object]' * * @example * * isObject({}) * // => true * * isObject([1, 2, 3]) * // => true * * isObject(Function) * // => true * * isObject(null) * // => false */ var obj = x => objStrict(x) || _function(x); /** * @param {*} x value * @return {boolean} isError * * @memberOf is * @func isError * * @example * * isError(new Error()) * //=> true * isError(new Error().stack) * //=> false * isError(1) * //=> false * isError('') * //=> false * * @example * * const e = {} * eh[Symbol.toStringTag] = '[Object Error]' * isError(eh) * //=> true * * @example * * class Eh extends Error() * isError(new Eh()) * //=> true * */ var error$1 = x => x instanceof Error || toS(x) === '[object Error]'; /** * @param {*} x value * @return {boolean} isNumber * * @since 3.0.0 * @memberOf is * @func isNumber * @see is/real * * @example * * isNumber(1) * //=> true * isNumber(Number(1)) * //=> true * isNumber(NaN) * //=> true * * isNumber(null) * //=> false * isNumber(undefined) * //=> false * isNumber(void 0) * //=> false * isNumber({}) * //=> false * isNumber('') * //=> false * isNumber(false) * //=> false * * @NOTE was not needed except for abstract == * const isObj = require('./obj') * const isSymbol = require('./symbol') * (isObj(x) || isSymbol(x) * ? false * : (/^0x[0-9a-f]+$/i).test(x) || * (/^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/).test(x)) */ var number = x => typeof x === 'number' || toS(x) === '[object Number]'; /** * @desc turns arguments into an array, used as a util, for opt * * @since 3.0.0 * @return {Array<Arguments>} * * @see https://github.com/aretecode/awesome-deopt * @see https://github.com/petkaantonov/bluebird/wiki/Optimization-killers * * @example * * function eh() { * const args = argumentor.apply(null, arguments).slice(1) * * console.log(args) * //=> [1, 10, 100] * } * eh(0, 1, 10, 100) * */ var argumentor = function() { const len = arguments.length; const args = new Array(len); for (let i = 0; i < len; ++i) args[i] = arguments[i]; return args }; var hasOwnProperty_1 = (haystack, needle) => Object.prototype.hasOwnProperty.call(haystack, needle); var getPrototypeOf = Object.getPrototypeOf; /* eslint no-new-wrappers: "off" */ /* eslint eqeqeq: "off" */ /* eslint func-style: "off" */ /* eslint complexity: "off" */ /** * @param {Array | Object | any} xs * @param {Function} fn * @TODO: unexpectedly breaks things iterating * if you are relying on internal functionality * (such as .path, .get, .value...) with map & set * * @NOTE if there is .forEach on the obj already, use it * otherwise, call function for each * */ var forEach = function(xs, fn) { if (xs.forEach) xs.forEach(fn); else for (let i = 0; i < xs.length; i++) fn(xs[i], i, xs); }; /** * {@link https://sourcemaking.com/design_patterns/chain_of_responsibility chainofresponsibility} * * @param {Traversable} obj object to traverse * * @constructor * * @example * * traverse({}) * //=> new Traverse(obj) * */ var traverse = function(obj) { return new Traverse(obj) }; var traverse_1 = traverse; /** * @func * @class TraverseJS * @classdesc Traverse and transform objects by visiting every node on a recursive walk. * @prop {any} value * * @category traverse * @memberOf Traverse * @see deps/traverse * @category traverse * @types traverse * @tests traverse/* * * @TODO: symbol, map, set * @tutorial https://github.com/substack/js-traverse * * @param {Traversable} obj any traversable value * * @example * * traverse({}) * //=> Traverser * */ function Traverse(obj) { this.value = obj; } /** * @desc Get the element at the array path. * * @param {Array<string>} ps paths * @return {any} value at dot-prop * * @memberOf Traverse * @see this.forEach * @todo hasOwnProperty */ Traverse.prototype.get = function(ps) { let node = this.value; for (let i = 0; i < ps.length; i++) { const key = ps[i]; if (!node || !hasOwnProperty_1(node, key)) { node = undefined; break } node = node[key]; } return node }; /** * @desc Return whether the element at the array path exists. * * @param {Array<string>} pathsArray paths * @return {boolean} has element at path * * @memberOf Traverse * @see hasOwnProperty * * @example * * traverse({eh: true}).has(['eh']) * //=> true * * @example * * traverse({eh: true}).has(['canada']) * //=> false * * * @example * * traverse([0]).has([2]) * //=> false * */ Traverse.prototype.has = function(pathsArray) { let node = this.value; for (let i = 0; i < pathsArray.length; i++) { const key = pathsArray[i]; if (!node || !hasOwnProperty_1(node, key)) { return false } node = node[key]; } return true }; /** * @desc Set the element at the array path to value. * * @param {Array<string>} arrayPath paths * @param {any} value any value to assign to the element @ the path * @return {any} value passed in * * @memberOf Traverse * @see deps/dot */ Traverse.prototype.set = function(arrayPath, value) { let node = this.value; let i = 0; for (; i < arrayPath.length - 1; i++) { const key = arrayPath[i]; if (!hasOwnProperty_1(node, key)) node[key] = {}; node = node[key]; } node[arrayPath[i]] = value; return value }; /** * @desc Execute fn for each node in the object and return a new object with the results of the walk. To update nodes in the result use this.update(value). * * @method * @memberOf Traverse * @see walk * @param {Function} cb fn for each node in the object * @return {any} * * @example * var {traverse} = require('chain-able') * * var obj = {a: 1, b: 2, c: [3, 4]} * obj.c.push(obj) * * var scrubbed = traverse(obj).map(function(x) { * if (this.circular) this.remove() * }) * console.dir(scrubbed) * //=> { a: 1, b: 2, c: [ 3, 4 ] } */ Traverse.prototype.map = function(cb) { return walk(this.value, cb, true) }; /** * @desc Execute fn for each node in the object but unlike .map(), when this.update() is called it updates the object in-place. * executes a provided function once for each traversed element. * * @param {Function} callback provided callback function * @return {any} this.value * * @memberOf Traverse * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach * * @example * * var {traverse} = require('chain-able') * * var obj = [5, 6, -3, [7, 8, -2, 1], {f: 10, g: -13}] * traverse(obj).forEach(function(x) { * if (x < 0) this.