New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

core-functions

Package Overview
Dependencies
Maintainers
1
Versions
48
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

core-functions - npm Package Compare versions

Comparing version 3.0.1 to 3.0.2

copying.js

4

app-errors.js

@@ -321,3 +321,3 @@ "use strict";

function toHttpStatus(httpStatus) {
const httpStatusCode = Number.parseInt(trim(httpStatus));
const httpStatusCode = Number(trim(httpStatus));
return Number.isNaN(httpStatusCode) ? stringify(trimOrEmpty(httpStatus)) : httpStatusCode;

@@ -333,3 +333,3 @@ }

function toHttpStatusStrict(httpStatus) {
const httpStatusCode = Number.parseInt(trim(httpStatus));
const httpStatusCode = Number(trim(httpStatus));
return Number.isNaN(httpStatusCode) ? -1 : httpStatusCode;

@@ -336,0 +336,0 @@ }

@@ -19,3 +19,3 @@ /**

function distinct(array) {
return array.filter((elem, pos) => array.indexOf(elem) == pos);
return array.filter((elem, pos) => array.indexOf(elem) === pos);
}

@@ -31,3 +31,3 @@

function isDistinct(array) {
return array.every((elem, pos) => array.indexOf(elem) == pos);
return array.every((elem, pos) => array.indexOf(elem) === pos);
}

@@ -34,0 +34,0 @@

@@ -23,3 +23,2 @@ 'use strict';

function isBoolean(value) {
//return typeof value === 'boolean' || (value instanceof Boolean && typeof (value.valueOf()) === 'boolean');
return typeof value === 'boolean' || value instanceof Boolean;

@@ -26,0 +25,0 @@ }

@@ -31,2 +31,3 @@ 'use strict';

isSafeInteger: isSafeInteger,
toInteger: toInteger,

@@ -126,2 +127,14 @@ integerRegex: integerRegex,

/**
* Attempts to convert the given number-like string into an integer using `floor` if the parsed number is positive or
* `ceil` if its negative.
* @param {string|number|*} numberLike - a number-like string
* @returns {number|NaN} a parsed, integer version of the given number-like string
*/
function toInteger(numberLike) {
const n = Number(numberLike);
if (Number.isNaN(n)) return NaN;
return n >= 0 ? Math.floor(n) : Math.ceil(n);
}
/**
* Returns true if the given value is a number-like string containing a number of any precision.

@@ -199,3 +212,3 @@ * @param {string} value - the value to test

// Convert exponent into decimal format
let e = Number.parseInt(n.substring(ePos + 1));
let e = toInteger(n.substring(ePos + 1));
if (e >= 0) {

@@ -291,3 +304,3 @@ if (hasDecPoint) {

// Integer-like string, so attempt to parse to an integer
const n = Number.parseInt(value);
const n = toInteger(value);
// Check if have enough precision to hold the given integer value ... otherwise return the given value excluding

@@ -294,0 +307,0 @@ // its fractional part (if any)

@@ -7,2 +7,4 @@ 'use strict';

const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
/**

@@ -16,7 +18,18 @@ * Module containing utilities for working with objects.

valueOf: valueOf,
isTypedArray: isTypedArray,
getPropertyNames: getPropertyNames,
getPropertySymbols: getPropertySymbols,
getPropertyKeys: getPropertyKeys,
getPropertyDescriptors: getPropertyDescriptors,
getPropertyValueByKeys: getPropertyValueByKeys,
getPropertyDescriptorByKeys: getPropertyDescriptorByKeys,
getPropertyValueByCompoundName: getPropertyValueByCompoundName,
/** @deprecated */
copy: copy,
/** @deprecated */
merge: merge,
copy: copy,
getPropertyValue: getPropertyValue,
/** @deprecated */
copyNamedProperties: copyNamedProperties,
toKeyValuePairs: toKeyValuePairs
toKeyValuePairs: toKeyValuePairs,
getOwnPropertyNamesRecursively: getOwnPropertyNamesRecursively
};

@@ -35,172 +48,173 @@

/**
* Merges the properties of the given 'from' object into the given 'to' object, only replacing same named properties in
* the 'to' object if opts.replace is true. Executes a deep merge if opts.deep is true, otherwise only does a shallow
* merge. Returns the updated 'to' object.
*
* Legacy parameters were (from, to, replace, deep), so for backward compatibility, convert any boolean opts or
* (undefined opts and boolean 4th argument) into an appropriate object opts.
*
* @param {Object} from - the 'from' object from which to get enumerable properties to be merged into the 'to' object
* @param {Object} to - the 'to' object to which to add or deep merge (or optionally replace) properties from the 'from' object
* @param {Object|undefined} [opts] - optional opts to use
* @param {boolean|undefined} [opts.replace] - whether to replace properties in the 'to' object with same named properties in the from object or not (defaults to not)
* @param {boolean|undefined} [opts.deep] - Executes a deep merge if true, otherwise only does a shallow merge (defaults to shallow)
* Returns true if object is a subclass instance of TypedArray; false otherwise.
* @param {TypedArray|*} object - the object to test
* @returns {boolean} true if object is a subclass instance of TypedArray; false otherwise
*/
function merge(from, to, opts) {
// Legacy parameters were (from, to, replace, deep), so for backward compatibility, convert any boolean opts or
// (undefined opts and boolean 4th argument) into an appropriate object opts
if (typeof opts === "boolean" || (!opts && typeof arguments[3] === "boolean")) {
opts = {replace: !!opts, deep: !!arguments[3]};
function isTypedArray(object) {
return object instanceof Int8Array || object instanceof Uint8Array || object instanceof Uint8ClampedArray ||
object instanceof Int16Array || object instanceof Uint16Array ||
object instanceof Int32Array || object instanceof Uint32Array ||
object instanceof Float32Array || object instanceof Float64Array;
}
/**
* Gets the property names of either all of the own properties of the given object (if onlyEnumerable is false);
* otherwise of only the enumerable own properties of the given object.
* Special cases when skipArrayLike is true:
* - for Arrays (& String objects), skip length and numeric index property names
* - for Buffers & TypedArrays, skip numeric index property names (also have length, but it doesn't appear in getOwnProperties)
* - for ArrayBuffers & DataViews, there is nothing extra to do, since no extra properties by default
* @param {Object} o - the object from which to collect its own property names
* @param {boolean|undefined} [onlyEnumerable] - whether to get the names of only enumerable own properties (if true) or of all own properties
* @param {boolean|undefined} [skipArrayLike] - whether to skip Array-like numeric & length properties on Arrays, Strings, Buffers & TypedArrays or not
* @returns {(string|number)[]} an array of property names (and/or indexes)
*/
function getPropertyNames(o, onlyEnumerable, skipArrayLike) {
const names = !onlyEnumerable ? Object.getOwnPropertyNames(o) : Object.keys(o);
if (!skipArrayLike) {
return names;
}
// Resolve the options from opts
const replace = !!opts && opts.replace === true;
const deep = !!opts && opts.deep === true;
// Special cases when skipArrayLike is true:
// - for Arrays (& String objects), skip length and numeric property names
// - for Buffers & TypedArrays, skip numeric property names (have length, but it doesn't appear in getOwnProperties)
// - for ArrayBuffers & DataViews, nothing to do, since no extra properties by default
return Array.isArray(o) || o instanceof String ? names.filter(n => n !== 'length' && Number.isNaN(Number(n))) :
o instanceof Buffer || isTypedArray(o) ? names.filter(n => Number.isNaN(Number(n))) : names;
}
if (!from || typeof from !== 'object') throw new TypeError(`from must be a non-null object`);
if (!to || typeof to !== 'object') throw new TypeError(`to must be a non-null object`);
/**
* Gets the property symbols of either all of the own properties of the given object (if opts.onlyEnumerable is false);
* otherwise of only the enumerable own properties of the given object.
* @param {Object} o - the object from which to collect its own property symbols
* @param {boolean|undefined} [onlyEnumerable] - whether to get the symbols of only enumerable own properties (if true) or of all own properties
* @returns {Symbol[]} an array of property symbols
*/
function getPropertySymbols(o, onlyEnumerable) {
const symbols = Object.getOwnPropertySymbols(o);
return !onlyEnumerable ? symbols : symbols.filter(symbol => propertyIsEnumerable.call(o, symbol));
}
const history = new WeakMap();
/**
* Gets all of the property keys (i.e. names and symbols) of either all of the own properties of the given object (if
* opts.onlyEnumerable is false); otherwise of only the enumerable own properties of the given object.
* @param {Object} o - the object from which to collect its own property symbols
* @param {boolean|undefined} [onlyEnumerable] - whether to get the keys (i.e. names & symbols) of only enumerable own properties (if true) or of all own properties
* @param {boolean|undefined} [skipArrayLike] - whether to skip Array-like numeric & length properties on Arrays, Strings, Buffers & TypedArrays or not
* @returns {PropertyKey[]} an array of property keys (i.e. names & symbols)
*/
function getPropertyKeys(o, onlyEnumerable, skipArrayLike) {
return getPropertyNames(o, onlyEnumerable, skipArrayLike).concat(getPropertySymbols(o, onlyEnumerable));
}
function mergeWithHistory(src, dest) {
if (history.has(src)) {
return history.get(src);
}
// Remember this merge in case the same source appears again within its own graph
history.set(src, dest);
/**
* Gets all of the own string-named & symbol-named property descriptors for the given object.
* @param {Object} o - the object from which to collect its property descriptors
* @param {boolean|undefined} [onlyEnumerable] - whether to collect ONLY enumerable own property descriptors or ALL own property descriptors (default is false, i.e. collect all)
* @param {boolean|undefined} [skipArrayLike] - whether to skip Array-like numeric & length properties on Arrays, Strings, Buffers & TypedArrays or not
* @returns {Object} a map of property descriptors by key (i.e. by name and/or symbol)
*/
function getPropertyDescriptors(o, onlyEnumerable, skipArrayLike) {
return getPropertyKeys(o, onlyEnumerable, skipArrayLike).reduce((descriptors, key) => {
descriptors[key] = Object.getOwnPropertyDescriptor(o, key);
return descriptors;
}, {});
}
// If both src and dest are arrays then special case for arrays
const srcIsArray = Array.isArray(src);
const destIsArray = Array.isArray(dest);
if (srcIsArray && destIsArray) {
// First merge all corresponding elements of src into dest
const n = Math.min(src.length, dest.length);
for (let i = 0; i < n; ++i) {
const srcElement = src[i];
const srcElementIsObject = srcElement && typeof srcElement === 'object';
const destElement = dest[i];
if (deep && srcElementIsObject && destElement && typeof destElement === 'object') {
mergeWithHistory(srcElement, destElement, replace, deep);
} else if (replace) {
dest[i] = srcElementIsObject ? copy(srcElement, {deep: true}) : srcElement;
}
}
// If src was longer than dest, then append copies of any remaining elements of src (if any) to the end of dest
for (let j = n; j < src.length; ++j) {
const srcElement = src[j];
const srcElementIsObject = srcElement && typeof srcElement === 'object';
dest.push(srcElementIsObject ? copy(srcElement, {deep: true}) : srcElement);
}
/**
* Traverses the given list of property keys starting from the given object and returns the value of the targeted
* property (if found); otherwise returns undefined.
* @param {Object} object - the object from which to start traversing
* @param {PropertyKey[]} keys - the list of property keys to be traversed to get to the targeted property
* @returns {*} the value of the targeted property (if found); otherwise undefined
*/
function getPropertyValueByKeys(object, keys) {
let next = object;
const last = keys.length - 1;
for (let i = 0; i < last; ++i) {
if (!next || typeof next !== 'object') {
return undefined;
}
next = next[keys[i]];
}
return next && typeof next === 'object' && last >= 0 ? next[keys[last]] : undefined;
}
// Arrays can also be given other properties, so special case when both arrays, skip src length and numeric property names
const allSrcNames = Object.getOwnPropertyNames(src);
const srcNames = srcIsArray && destIsArray ?
allSrcNames.filter(n => n !== 'length' && Number.isNaN(Number.parseInt(n))) : allSrcNames;
const destNames = Object.getOwnPropertyNames(dest);
for (let i = 0; i < srcNames.length; ++i) {
const name = srcNames[i];
const srcProperty = src[name];
const srcPropertyIsObject = srcProperty && typeof srcProperty === 'object';
const existsOnDest = destNames.indexOf(name) !== -1;
if (existsOnDest) {
const destProperty = dest[name];
if (deep && srcPropertyIsObject && destProperty && typeof destProperty === 'object') {
mergeWithHistory(srcProperty, destProperty, replace, deep);
} else if (replace) {
dest[name] = srcPropertyIsObject ? copy(srcProperty, {deep: true}) : srcProperty;
}
} else {
dest[name] = srcPropertyIsObject ? copy(srcProperty, {deep: true}) : srcProperty;
}
/**
* Traverses the given list of property keys starting from the given object and returns the descriptor of the targeted
* property (if found); otherwise returns undefined.
* @param {Object} object - the object from which to start traversing
* @param {PropertyKey[]} keys - the list of property keys to be traversed to get to the targeted property
* @returns {PropertyDescriptor|undefined} the descriptor of the targeted property (if found); otherwise undefined
*/
function getPropertyDescriptorByKeys(object, keys) {
if (keys.length <= 0) return undefined;
let next = object;
const last = keys.length - 1;
for (let i = 0; i < last; ++i) {
if (!next || typeof next !== 'object') {
return undefined;
}
return dest;
next = next[keys[i]];
}
return next && typeof next === 'object' && last >= 0 ? Object.getOwnPropertyDescriptor(next, keys[last]) : undefined;
}
return mergeWithHistory(from, to);
/**
* Traverses the components of the given compound or simple property name starting from the given object and returns the
* value of the targeted property (if found); otherwise returns undefined. A compound property name is one that contains
* multiple property names separated by fullstops.
* @param {Object} object - the object from which to start traversing
* @param {string} compoundOrSimpleName - the compound or simple name of the property
* @returns {*} the value of the targeted property (if found); otherwise undefined
*/
function getPropertyValueByCompoundName(object, compoundOrSimpleName) {
const names = compoundOrSimpleName.split(".").map(n => trim(n)).filter(name => isNotBlank(name));
return getPropertyValueByKeys(object, names);
}
/**
* Copies the properties of the given object into a new object. Executes a deep copy if opts.deep is true, otherwise
* only does a shallow copy. Returns the new copy of the original object or the original "object" if it was NOT a non-
* null instance of Object.
* Creates & returns a copy of the given object by copying its properties into a new object of a similar type if the
* given object is copyable (e.g. non-null, non-Promise object); otherwise simply returns the given object. Executes a
* deep copy if `opts.deep` is true; otherwise only does a shallow copy.
*
* Legacy parameters were (object, deep), so for backward compatibility, use any true opts as if it was true opts.deep
* Legacy parameters were (object, deep), so for backward compatibility, use any true/false opts as if it was true/false opts.deep
*
* @param {Object} object - the object from which to copy enumerable properties into a new object
* @param {Object|boolean|undefined} [opts] - optional opts to use (if opts is true, handles it as if opts.deep were true & does a deep copy)
* @param {boolean|undefined} [opts.deep] - Executes a deep copy if opts.deep is true, otherwise only does a shallow copy (defaults to shallow)
* @deprecated Use {link module:core-functions/copying#copy} instead
* @param {T} object - the object to be copied
* @param {CopyOpts|boolean|undefined} [opts] - optional opts to use (if opts is true/false, handles it as if opts.deep were true/false)
* @returns {T} a copy of the given object
*/
function copy(object, opts) {
if (!object || typeof object !== 'object') {
return object;
// Legacy parameters were (object, deep: boolean), so for backward compatibility, use any true/false opts as if it was true/false opts.deep
if (opts === true || opts === false) {
opts = {deep: opts};
}
// Resolve the options from opts
// Legacy parameters were (object, deep), so for backward compatibility, use any true opts as if it was true opts.deep
const deep = (!!opts && opts.deep === true) || opts === true;
const history = new WeakMap();
function newDest(object) {
return Array.isArray(object) ? new Array(object.length) : Object.create(object.__proto__);
}
function copyWithHistory(src, dest) {
if (history.has(src)) {
return history.get(src);
}
// Remember this copy in case the same source appears again within its own graph
history.set(src, dest);
const srcIsArray = Array.isArray(src);
if (srcIsArray) {
for (let i = 0; i < src.length; ++i) {
const element = src[i];
const elementIsObject = element && typeof element === 'object';
dest[i] = deep && elementIsObject ?
copyWithHistory(element, newDest(element)) : element;
}
}
// Arrays can also be given other properties, so special case for arrays, skip length and numeric property names
const allNames = Object.getOwnPropertyNames(src);
const names = srcIsArray ? allNames.filter(n => n !== 'length' && Number.isNaN(Number.parseInt(n))) : allNames;
for (let i = 0; i < names.length; ++i) {
const name = names[i];
const property = src[name];
const propertyIsObject = property && typeof property === 'object';
dest[name] = deep && propertyIsObject ?
copyWithHistory(property, newDest(property)) : property;
}
return dest;
}
return copyWithHistory(object, newDest(object));
const copying = require('./copying');
return copying.copy(object, opts);
}
/**
* Gets the value of the simple or compound named property from the given object. A compound property name is one that
* contains multiple property names separated by fullstops.
* @param {Object} object - the object from which to get the named property's value
* @param {string} propertyName - the simple or compound name of the property
* @returns {*} the value of the named property on the given object
* Merges the properties of the given 'from' object into the given 'to' object, only replacing same named properties in
* the 'to' object if opts.replace is true. Executes a deep merge if opts.deep is true, otherwise only does a shallow
* merge. Returns the updated 'to' object.
*
* Legacy parameters were (from, to, replace, deep), so for backward compatibility, convert any boolean opts or
* (undefined opts and boolean 4th argument) into an appropriate object opts.
*
* @deprecated Use {link module:core-functions/merging#merge} instead
* @param {Object} from - the 'from' object from which to get enumerable properties to be merged into the 'to' object
* @param {Object} to - the 'to' object to which to add or deep merge (or optionally replace) properties from the 'from' object
* @param {MergeOpts|undefined} [opts] - optional opts to use
*/
function getPropertyValue(object, propertyName) {
const propertyNames = propertyName.split(".").map(n => trim(n)).filter(name => isNotBlank(name));
let value = undefined;
for (let i = 0; i < propertyNames.length; ++i) {
if (!object || typeof object !== 'object') {
return undefined;
}
value = object[propertyNames[i]];
object = value;
function merge(from, to, opts) {
// Legacy parameters were (from, to, replace, deep), so for backward compatibility, convert any boolean opts or
// (undefined opts and boolean 4th argument) into an appropriate object opts
if (typeof opts === "boolean" || (!opts && typeof arguments[3] === "boolean")) {
opts = {replace: !!opts, deep: !!arguments[3]};
}
return value;
const merging = require('./merging');
return merging.merge(from, to, opts);
}
/**

@@ -215,16 +229,9 @@ * Copies ONLY the (simple or compound) named properties, which are listed in the given propertyNames array, from the

*
* @param {Object} src - the source object from which to copy the named properties
* @param {string[]} propertyNames - the list of named properties to be copied
* @param {Object|undefined} [opts] - optional opts to use
* @param {boolean|undefined} [opts.compact] - whether to create a flatter, more-compact destination object, which will use
* any compound property names as is and eliminate any unnecessary intermediate objects or rather create a more-
* conventional, less-compact destination object, which will only have simple property names and all necessary intermediate objects
* @param {boolean|undefined} [opts.deep] - executes a deep copy of each property value if the given deep flag is true, otherwise only does a shallow copy
* @param {boolean|undefined} [opts.omitIfUndefined] - whether or not to omit any named property that has an undefined value from the destination object
* @returns {Object} a new object containing copies of only the named properties from the source object
* @deprecated Use {link module:core-functions/copying#copyNamedProperties} instead
* @param {T} src - the source object from which to copy the named properties
* @param {PropertyKey[]} keys - the list of named properties to be copied
* @param {CopyNamedPropertiesOpts|undefined} [opts] - optional opts to use
* @returns {T} a new object containing copies of only the named properties from the source object
*/
function copyNamedProperties(src, propertyNames, opts) {
if (!src || typeof src !== 'object') {
return src === undefined ? undefined : src === null ? null : {};
}
function copyNamedProperties(src, keys, opts) {
// Legacy parameters were (src, propertyNames, compact, deep, omitPropertyIfUndefined), so for backward compatibility,

@@ -235,32 +242,5 @@ // convert any boolean opts or (no opts, but boolean 4th or 5th arg) into an appropriate object opts

}
// Resolve the options from opts
const compact = !!opts && opts.compact === true;
const deep = !!opts && opts.deep === true;
const deepOpts = {deep: deep};
const omitIfUndefined = !!opts && opts.omitIfUndefined === true;
const dest = {};
for (let i = 0; i < propertyNames.length; ++i) {
const propertyName = trim(propertyNames[i]);
const propertyValue = getPropertyValue(src, propertyName);
if (!omitIfUndefined || propertyValue !== undefined) {
if (compact) {
dest[propertyName] = copy(propertyValue, deepOpts);
} else {
const names = propertyName.split(".").map(n => trim(n)).filter(name => isNotBlank(name));
if (names.length > 0) {
let d = dest;
for (let j = 0; j < names.length - 1; ++j) {
const name = names[j];
if (!d[name]) {
d[name] = {};
}
d = d[name];
}
d[names[names.length - 1]] = copy(propertyValue, deepOpts);
}
}
}
}
return dest;
const copying = require('./copying');
return copying.copyNamedProperties(src, keys, opts);
}

@@ -270,8 +250,74 @@

* Extracts an array of key value pairs from the given object. Each key value pair is represented as an array containing
* a key property name followed by its associated value.
* a property key (i.e. a string/number/symbol property name) followed by its associated value.
* @param {Object} object - an object
* @param {Object|undefined} [opts] - optional options to use
* @param {boolean|undefined} [opts.onlyEnumerable] - whether to collect keys as ONLY enumerable properties or ALL own properties (default is false, i.e. collect all)
* @param {boolean|undefined} [opts.skipArrayLike] - whether to skip Array-like numeric & length properties on Arrays, Strings, Buffers & TypedArrays or not
* @param {boolean|undefined} [opts.omitSymbols] - whether to omit any & all of the object's own property symbols & their values from the results or not (default not)
* @returns {KeyValuePair[]} an array of key value pairs
*/
function toKeyValuePairs(object) {
return object && typeof object === 'object' ? Object.getOwnPropertyNames(object).map(key => [key, object[key]]) : [];
}
function toKeyValuePairs(object, opts) {
if (!object || typeof object !== 'object') return [];
const hasOpts = !!opts;
const onlyEnumerable = hasOpts && opts.onlyEnumerable === true;
const skipArrayLike = hasOpts && opts.skipArrayLike === true;
const omitSymbols = hasOpts && opts.omitSymbols === true;
const keys = omitSymbols ? getPropertyNames(object, onlyEnumerable, skipArrayLike) :
getPropertyKeys(object, onlyEnumerable, skipArrayLike);
return keys.map(key => [key, object[key]]);
}
/**
* Collects and returns all of the given object's own property names and also recursively collects and returns all of
* its object properties' names (prefixed with their parent's property names).
* @param {Object} object - the object from which to collect property names recursively
* @param {Object} [opts] - optional options to use
* @param {boolean|undefined} [opts.onlyEnumerable] - whether to collect ONLY enumerable properties or ALL own properties (default is false, i.e. collect all)
* @returns {string[]} an array of property names
*/
function getOwnPropertyNamesRecursively(object, opts) {
function isTraversable(o) {
return !!o && typeof o === 'object' && !(o instanceof Promise) && o !== console;
}
if (!isTraversable(object)) return [];
const onlyEnumerable = !!opts && opts.onlyEnumerable === true;
const history = new WeakMap();
function collect(o, oName) {
// Handle circular references by skipping all of the reference's property names, which were already collected
if (history.has(o)) return [];
// Remember this object, in case it appears again within its own graph
history.set(o, oName);
const results = [];
// If o is an array then add all of its elements' property names (if any)
if (Array.isArray(o)) {
for (let i = 0; i < o.length; ++i) {
const pName = `${oName}[${i}]`;
const value = o[i];
const propNames = isTraversable(value) ? collect(value, pName) : undefined;
if (propNames) Array.prototype.push.apply(results, propNames);
}
}
// Add object's property names
getPropertyNames(o, onlyEnumerable, true).forEach(name => {
const pName = oName ? `${oName}.${name}` : name;
results.push(pName);
const value = o[name];
const propNames = isTraversable(value) ? collect(value, pName) : undefined;
if (propNames) Array.prototype.push.apply(results, propNames);
});
return results;
}
return collect(object, '');
}
{
"name": "core-functions",
"version": "3.0.1",
"version": "3.0.2",
"description": "Core functions, utilities and classes for working with Node/JavaScript primitives and built-in objects, including strings, booleans, Promises, base 64, Arrays, Objects, standard AppErrors, etc.",

@@ -5,0 +5,0 @@ "author": "Byron du Preez",

'use strict';
const tries = require('./tries');
//const Try = tries.Try;
const Try = tries.Try;
const Success = tries.Success;

@@ -28,4 +28,4 @@ const Failure = tries.Failure;

super(message);
Object.defineProperty(this, 'message', {writable: false, enumerable: true, configurable: false});
Object.defineProperty(this, 'name', {value: this.constructor.name});
// Object.defineProperty(this, 'message', {writable: false, enumerable: true, configurable: false});
Object.defineProperty(this, 'name', {value: this.constructor.name, enumerable: false});
Object.defineProperty(this, 'resolvedOutcomes', {value: resolvedOutcomes, enumerable: false});

@@ -39,2 +39,16 @@ Object.defineProperty(this, 'unresolvedInputs', {value: unresolvedInputs, enumerable: false});

class DelayCancelledError extends Error {
/**
* Constructs a new DelayCancelledError.
* @param {number} delayMs - the original number of milliseconds of the delay
* @param {number} atMs - the number of milliseconds at which the delay was cancelled
*/
constructor(delayMs, atMs) {
super(`Delay cancelled at ${atMs} ms of ${delayMs} ms`);
Object.defineProperty(this, 'name', {value: this.constructor.name, enumerable: false});
Object.defineProperty(this, 'delayMs', {value: delayMs, enumerable: false});
Object.defineProperty(this, 'atMs', {value: atMs, enumerable: false});
}
}
/**

@@ -50,2 +64,4 @@ * Module containing Promise utility functions.

isPromiseLike: isPromiseLike,
/** Transforms the given promise-like object (or non-promise value) into a native Promise */
toPromise: toPromise,
/** Returns a function that will wrap and convert a node-style function into a Promise-returning function */

@@ -65,8 +81,16 @@ wrap: wrap,

one: one,
/** Transforms the given promise-like object (or non-promise value) into a native Promise */
toPromise: toPromise,
/** Recursively flattens the given value into either the given non-promise value or a single promise. */
flatten: flatten,
/** Returns a promise that will return a list of Success or Failure outcomes generated by sequentially chaining calls to the given function `f` with each successive element of the given array of `elements` */
chain: chain,
/** An Error subclass thrown to cancel/short-circuit a promise that is waiting for a list of promises to resolve. */
CancelledError: CancelledError
CancelledError: CancelledError,
/** An Error subclass thrown to cancel a delay promise that is waiting for a timeout to complete. */
DelayCancelledError: DelayCancelledError,
/** Installs the given cancel function onto the given cancellable object */
installCancel: installCancel,
/** Installs the given cancelTimeout function onto the given cancellable object */
installCancelTimeout: installCancelTimeout
// /** Replaces the cancel method on the given cancellable object with a new cancel method that will invoke both its original cancel method and the new, next cancellable's cancel method. */
// extendCancel: extendCancel
};

@@ -277,6 +301,6 @@

* Starts a simple timeout Promise, which will resolve after the specified delay in milliseconds. If any non-null object
* is passed into this function as the `cancellable` argument, then this function will also install a `cancelTimeout` method
* on it, which accepts a single optional mustResolve argument and which if subsequently invoked will cancel the timeout
* and either resolve the promise (if mustResolve) or reject the promise (default), but ONLY if the timeout has not
* triggered yet.
* is passed into this function as the `cancellable` argument, then this function will also install a `cancelTimeout`
* method on it (see {@link installCancelTimeout}), which accepts a single optional mustResolve argument and which if
* subsequently invoked will cancel the timeout and either resolve the promise (if mustResolve is true) or reject the
* promise with a new DelayCancelledError (default), but ONLY if the timeout has NOT triggered yet.
*

@@ -289,2 +313,3 @@ * @param {number} ms - the number of milliseconds to delay

let triggered = false;
const startMs = Date.now();
return new Promise((resolve, reject) => {

@@ -296,21 +321,19 @@ const timeout = setTimeout(() => {

// Set up a cancel method on the given cancellable object
if (cancellable && typeof cancellable === 'object') {
cancellable.cancelTimeout = (mustResolve) => {
if (!triggered) {
try {
clearTimeout(timeout);
} catch (err) {
console.error('Failed to clear timeout', err);
} finally {
if (mustResolve) {
resolve(triggered);
} else {
reject(triggered);
}
// Install a new cancelTimeout method (or extend the existing one) on the given cancellable object (if any)
installCancelTimeout(cancellable, (mustResolve) => {
if (!triggered) {
try {
clearTimeout(timeout);
} catch (err) {
console.error('Failed to clear timeout', err.stack);
} finally {
if (mustResolve) {
resolve(triggered);
} else {
reject(new DelayCancelledError(ms, Date.now() - startMs));
}
}
return triggered;
}
}
return triggered;
});
});

@@ -337,6 +360,7 @@ }

* values. If any non-null object is passed into this function as the `cancellable` argument, then this function will
* also install a `cancel` method on it, which expects no arguments and returns true if all of the promises have already
* resolved; or false otherwise. If this `cancel` method is subsequently invoked, it will attempt to short-circuit the
* `every` promise that is waiting for any remaining promises to complete by instead throwing a `CancelledError`, which
* will contain a list of any resolved outcomes and a list of any unresolved promises.
* also install a `cancel` method on it (see {@link installCancel}), which expects no arguments and returns true if all
* of the promises have already resolved; or false otherwise. If this `cancel` method is subsequently invoked, it will
* attempt to short-circuit the current `every` promise that is waiting for all of its remaining promises to complete by
* instead throwing a `CancelledError`, which will contain a list of any resolved outcomes and a list of any unresolved
* promises.
*

@@ -348,4 +372,4 @@ * Note: The returned promise should NEVER reject (UNLESS it is cancelled via the `cancellable`), since it only resolves

* @param {Object|Cancellable|*} [cancellable] - an arbitrary object onto which a `cancel` method will be installed
* @returns {Promise.<(Success|Failure)[]|CancelledError>} a promise that will resolve with a list of Success or Failure
* outcomes for the given promises (unless cancelled); otherwise reject with a `CancelledError` (if cancelled)
* @returns {Promise.<Outcomes|CancelledError>} a promise that will resolve with a list of Success or Failure outcomes
* for the given promises (if not cancelled); or reject with a `CancelledError` (if cancelled)
* @throws {Error} an error if the given `promises` is not an array

@@ -366,9 +390,7 @@ */

// Set up a cancel method on the given cancellable object (if any)
if (cancellable && typeof cancellable === 'object') {
cancellable.cancel = () => {
cancelled = true;
return completed;
}
}
// Install or extend the cancel method on the given cancellable object (if any)
installCancel(cancellable, () => {
cancelled = true;
return completed;
});

@@ -440,2 +462,49 @@ function next(i) {

/**
* Recursively flattens the given value, which is expected to be typically either a single promise or an array of
* promises, into either the given value (if its neither a promise nor an array of at least one promise) or into a
* single promise containing either a non-promise value or an array of Success or Failure non-promise outcomes.
*
* If any non-null object is passed into this function as the `cancellable` argument, then this function will also
* install a `cancel` method on it (see {@link installCancel}), which expects no arguments and returns true if all of
* the promises have already resolved; or false otherwise. If this `cancel` method is subsequently invoked, it will
* attempt to short-circuit any current `every` promise that is waiting for all of its remaining promises to complete by
* instead throwing a `CancelledError`.
*
* @param {Promise|Promise[]|*} value - the value to be flattened
* @param {Object|Cancellable|*} [cancellable] - an arbitrary object onto which a `cancel` method will be installed
* @param {Object|undefined} [opts] - optional options to use to alter the behaviour of this flatten function
* @param {Object|undefined} [opts.skipSimplifyOutcomes] - whether to skip applying `Try.simplify` to any list of outcomes or not (defaults to simplifying with `Try.simplify`)
* @returns {*|Promise.<*>|Promise.<Outcomes|CancelledError>} the given non-promise value or a single promise of one or
* more non-promise values/outcomes (if not cancelled); or a rejected promise with a `CancelledError` (if cancelled)
*/
function flatten(value, cancellable, opts) {
if (isPromiseLike(value)) {
// If value is a promise or promise-like then flatten its resolved value or rejected error
return value.then(
v => flatten(v, cancellable, opts),
err => {
throw err;
}
);
}
const isArray = Array.isArray(value);
if (isArray && value.some(v => isPromiseLike(v))) {
// If value is an array containing at least one Promise or promise-like, then first flatten all of its promises and
// then use `every` function to flatten all of its resulting promises into a single promise of outcomes
const promise = every(value.map(v => flatten(v, cancellable, opts)), cancellable);
return !opts || !opts.skipSimplifyOutcomes ? promise.then(outcomes => Try.simplify(outcomes)) : promise;
} else if (value instanceof Try) {
// If value is a Success outcome, then flatten its Success value too
return value.map(v => flatten(v, cancellable, opts));
} else if (isArray && value.some(v => v instanceof Try)) {
// If value is an array containing at least one Success or Failure outcome, then flatten any Success values too
const outcomes = value.map(v => v instanceof Success ? v.map(vv => flatten(vv, cancellable, opts)) : v);
return !opts || !opts.skipSimplifyOutcomes ? Try.simplify(outcomes) : outcomes;
}
return value;
}
/**
* Returns a promise that will return a list of Success or Failure outcomes generated by sequentially chaining calls to

@@ -445,7 +514,7 @@ * the given promise-returning function `f` with each successive input from the given array of `inputs`, such that the

* outcomes returned will be in the same sequence and have the same size as the given list of `inputs`. If any
* non-null object is passed as the `cancellable` argument, then this function will also install a `cancel` method on it,
* which expects no arguments and returns true if the chain has already completed; or false otherwise. If this `cancel`
* method is subsequently invoked, it will attempt to prematurely cancel the `chain` of calls by instead throwing a
* `CancelledError`, which will contain a list of any resolved outcomes and any unresolved inputs and which will result
* in a rejected promise being returned.
* non-null object is passed as the `cancellable` argument, then this function will also install a `cancel` method on
* it (see {@link installCancel}), which expects no arguments and returns true if the chain has already completed; or
* false otherwise. If this `cancel` method is subsequently invoked, it will attempt to prematurely cancel the `chain`
* of calls by instead throwing a `CancelledError`, which will contain a list of any resolved outcomes and any
* unresolved inputs and which will result in a rejected promise being returned.
*

@@ -456,6 +525,7 @@ * Note: The returned promise should NEVER reject (UNLESS it is cancelled via the `cancellable`), since it only resolves

*
* @param {function(input: *, index: number, inputs: *[]): (Promise.<*>|*)} f - a function that must, at least, accept a
* single input to be processed, but can optionally also accept: the index at which the input appears in the given list
* of `inputs`; and the given `inputs` too, and ideally returns a promise that will resolve with the result returned or
* reject with the error thrown
* @param {function(input: *, index: number, inputs: *[], prevOutcomes: Outcomes): (Promise.<*>|*)} f - a function that
* must, at least, accept a single input to be processed, but can optionally also accept: the index at which the input
* appears in the given list of `inputs`; the entire list of `inputs`; and a list of any and all of the Success and/or
* Failure outcomes collected so far from the first index inputs, and ideally returns a promise that will resolve with
* the result returned or reject with the error thrown
* @param {*[]} inputs - the list of inputs to be passed one at a time to the given function

@@ -483,12 +553,10 @@ * @param {Object|Cancellable|*} [cancellable] - an arbitrary object onto which a `cancel` method will be installed

// Set up a cancel method on the given cancellable object (if any)
if (cancellable && typeof cancellable === 'object') {
cancellable.cancel = () => {
cancelled = true;
return completed;
}
}
// Install or extend the cancel method on the given cancellable object (if any)
installCancel(cancellable, () => {
cancelled = true;
return completed;
});
function next(i) {
return attempt(() => f(inputs[i], i, inputs)).then(
return attempt(() => f(inputs[i], i, inputs, outcomes.slice(0, i))).then(
value => {

@@ -521,2 +589,75 @@ outcomes[i] = new Success(value);

return next(0);
}
}
/**
* Installs the given cancel function onto the given cancellable object if the cancellable does NOT already have a
* `cancel` method; otherwise installs a new `cancel` method that will invoke both the cancellable's original `cancel`
* method and the given cancel function.
* @param {Cancellable} cancellable - the cancellable object onto which to install or replace its `cancel` method
* @param {function(): boolean} cancel - the new cancel function to be installed
*/
function installCancel(cancellable, cancel) {
if (cancellable && typeof cancellable === 'object' && typeof cancel === 'function') {
const origCancel = cancellable.cancel;
if (origCancel) {
// Cancellable already has an installed cancel method, so instead replace/extend it
cancellable.cancel = () => {
const origCompleted = origCancel.call(cancellable);
const newCompleted = cancel.call(cancellable);
return newCompleted && origCompleted;
}
} else {
// Cancellable does not yet have a cancel method installed on it, so just bind the new cancel function to it
cancellable.cancel = cancel;
}
}
}
/**
* Installs the given cancelTimeout function onto the given cancellable object if the cancellable does NOT already have
* a `cancelTimeout` method; otherwise installs a new `cancelTimeout` method that will invoke both the cancellable's
* original `cancelTimeout` method and the given cancelTimeout function.
* @param {DelayCancellable} cancellable - the cancellable object onto which to install or replace its `cancelTimeout` method
* @param {function(mustResolve: boolean): boolean} cancelTimeout - the new cancelTimeout function to be installed
*/
function installCancelTimeout(cancellable, cancelTimeout) {
if (cancellable && typeof cancellable === 'object' && typeof cancelTimeout === 'function') {
const origCancelTimeout = cancellable.cancelTimeout;
if (origCancelTimeout) {
// Cancellable already has an installed cancelTimeout method, so instead replace/extend it
cancellable.cancelTimeout = (mustResolve) => {
const origTriggered = origCancelTimeout.call(cancellable, mustResolve);
const newTriggered = cancelTimeout.call(cancellable, mustResolve);
return newTriggered && origTriggered;
}
} else {
// Cancellable does not yet have a cancelTimeout method installed on it, so just bind the new cancelTimeout function to it
cancellable.cancelTimeout = cancelTimeout;
}
}
}
// /**
// * Replaces the `cancel` method on the given cancellable object with a new `cancel` method that will invoke both its
// * original `cancel` method (if any) and the given next cancellable's `cancel` method (if any).
// * @param {Cancellable} cancellable - the original cancellable, whose `cancel` method will be replaced/extended
// * @param {Cancellable} nextCancellable - the next cancellable, whose `cancel` method will be invoked by the
// * cancellable's new `cancel` method
// */
// function extendCancel(cancellable, nextCancellable) {
// if (cancellable && typeof cancellable === 'object' && nextCancellable && typeof nextCancellable === 'object') {
// const originalCancel = cancellable.cancel;
// cancellable.cancel = () => {
// let completed = true;
// if (nextCancellable.cancel) {
// const nextCompleted = nextCancellable.cancel();
// completed = completed && nextCompleted;
// }
// if (originalCancel) {
// const originalCompleted = originalCancel.call(cancellable);
// completed = completed && originalCompleted;
// }
// return completed;
// };
// }
// }

@@ -1,2 +0,2 @@

# core-functions v3.0.1
# core-functions v3.0.2

@@ -11,3 +11,5 @@ Core functions, utilities and classes for working with Node/JavaScript primitives and built-in objects, including

- booleans.js - boolean utilities
- copying.js - Object copying utilities
- dates.js - Date utilities
- merging.js - Object merging utilities
- numbers.js - number utilities

@@ -20,2 +22,4 @@ - objects.js - Object utilities

- tries.js - Try, Success and Failure classes representing the outcome of a function execution
- weak-map-copy.js - a class that emulates a copy of a WeakMap
- weak-set-copy.js - a class that emulates a copy of a WeakSet

@@ -159,2 +163,53 @@ This module is exported as a [Node.js](https://nodejs.org/) module.

### 3.0.2
- Changes to `promises` module:
- Added `installCancel` utility function that installs a `cancel` method on a cancellable, which combines any existing
`cancel` method behaviour with the given new cancel function logic
- Changed `every` and `chain` functions to use new `installCancel` utility function, which enables them to "extend"
the behaviour of their cancellables' cancel methods instead of just overwriting them
- Added `installCancelTimeout` utility function that installs a `cancelTimeout` method on a cancellable, which
combines any existing `cancelTimeout` method behaviour with the given new cancelTimeout function logic
- Changed `delay` function to use new `installCancelTimeout` utility function, which enables it to "extend" the
behaviour of its cancellable's cancel method instead of just overwriting it
- Changed `chain` function to also pass any and all previous outcomes as an optional 4th argument to the given input
function `f`, which enables an input function to use and/or react to previous outcomes in the chain
- Added new `DelayCancelledError` subclass of Error
- Changed `delay` function to throw a new `DelayCancelledError` when timeout triggers and mustResolve is false. Note
that this change is not entirely backward compatible, since it fixes the prior behaviour that was incorrectly
throwing a boolean with the triggered value as the "error"
- Added new `flatten` function to recursively reduce a given Promise or array of Promises (containing other promises
or arrays of promises) down to a single Promise (with any Success and/or Failure outcomes necessary)
- Changes to `numbers`, `objects` & `app-errors` modules:
- Replaced all usages of `Number.parseInt(...)` with more reliable & consistent `Number(...)`
e.g. parseInt('1e+22') returns 1, while Number('1e+22') returns 1e+22;
e.g. parseInt('12B3') returns 12, while Number('12B3') returns NaN
- Changes & fixes to `objects` module:
- Moved the majority of the functionality of the `copy`, `copyNamedProperties` & `merge` functions to the new `copying`
& `merging` modules
- Changed `copy`, `copyNamedProperties` & `merge` functions to simply delegate to their counterparts in the new `copying`
& `merging` modules and marked the original functions as deprecated
- Added new `isTypedArray`, `getPropertyNames`, `getPropertySymbols`, `getPropertyKeys`, `getPropertyDescriptors`,
`getPropertyValueByKeys`, `getPropertyDescriptorByKeys`, `getPropertyValueByCompoundName` & `getOwnPropertyNamesRecursively`
functions
- Added new `weak-map-copy` module to enable "copying" of `WeakMap` instances
- Added new `weak-set-copy` module to enable "copying" of `WeakSet` instances
- Added new `copying` module:
- Added new versions of `copy` & `copyNamedProperties` functions copied from `objects` module
- Major refactoring & revision of `copy` function to also support copying of property descriptors and copying of
Symbols, Dates, Buffers, ArrayBuffers, TypedArrays, DataViews, Errors, RegExps, Maps, Sets, WeakMaps & WeakSets
- Added new `deepMapKeys`, `deepMapValues`, `deepSets`, `onlyEnumerable`, `onlyValues`, `omitAccessors`, `isCopyable`,
`createCustomObject` & `copyCustomContent` options to enable more extensive customisation of the `copy` function
- Added new `configureCopyContext`, `isCopyableObject`, `copyObject`, `createObject`, `createTypedArray`,
`createDataView`, `copyContent`, `copyPropertyDescriptors`, `copyPropertyValues`, `copyPropertyDescriptor`,
`copyPropertyValue` & `copyDescriptor` supporting functions
- Added new `merging` module:
- Added new version of `merge` function copied from `objects` module
- Major refactoring & revision of `merge` function
- Added new `isMergeable` and `onlyEnumerable` options to enable more extensive customisation of the `merge` function
- Added new `configureMergeContext`, `isMergeableObject`, `mergeObject`, `resolveMergedObject`, `mergeContent`,
`areSimilarArrayLikes`, `mergePropertyDescriptors` & `mergePropertyValues` supporting functions
- Changes to `tries` module:
- Added `simplify`, `count`, `countSuccess`, `countFailure` & `describeSuccessAndFailureCounts` static methods
- Added `flatten` & `findFailure` static methods
### 3.0.1

@@ -161,0 +216,0 @@ - Changes to `promises` module:

@@ -256,13 +256,13 @@ 'use strict';

check({[name]: " 300.1 "}, AppError);
check({[name]: " 400.2 "}, BadRequest);
check({[name]: " 401.3 "}, Unauthorized);
check({[name]: " 403.4 "}, Forbidden);
check({[name]: " 404.5 "}, NotFound);
check({[name]: " 408.6 "}, RequestTimeout);
check({[name]: " 429.7 "}, TooManyRequests);
check({[name]: " 500.8 "}, InternalServerError);
check({[name]: " 502.9 "}, BadGateway);
check({[name]: " 503.0 "}, ServiceUnavailable);
check({[name]: " 504.1 "}, GatewayTimeout);
check({[name]: " 300 "}, AppError);
check({[name]: " 400 "}, BadRequest);
check({[name]: " 401 "}, Unauthorized);
check({[name]: " 403 "}, Forbidden);
check({[name]: " 404 "}, NotFound);
check({[name]: " 408 "}, RequestTimeout);
check({[name]: " 429 "}, TooManyRequests);
check({[name]: " 500 "}, InternalServerError);
check({[name]: " 502 "}, BadGateway);
check({[name]: " 503 "}, ServiceUnavailable);
check({[name]: " 504 "}, GatewayTimeout);
});

@@ -369,13 +369,13 @@

check({[name]: " 300.1 "}, InternalServerError);
check({[name]: " 400.2 "}, BadRequest);
check({[name]: " 401.3 "}, Unauthorized);
check({[name]: " 403.4 "}, Forbidden);
check({[name]: " 404.5 "}, NotFound);
check({[name]: " 408.6 "}, RequestTimeout);
check({[name]: " 429.7 "}, TooManyRequests);
check({[name]: " 500.8 "}, InternalServerError);
check({[name]: " 502.9 "}, BadGateway);
check({[name]: " 503.0 "}, ServiceUnavailable);
check({[name]: " 504.1 "}, GatewayTimeout);
check({[name]: " 300 "}, InternalServerError);
check({[name]: " 400 "}, BadRequest);
check({[name]: " 401 "}, Unauthorized);
check({[name]: " 403 "}, Forbidden);
check({[name]: " 404 "}, NotFound);
check({[name]: " 408 "}, RequestTimeout);
check({[name]: " 429 "}, TooManyRequests);
check({[name]: " 500 "}, InternalServerError);
check({[name]: " 502 "}, BadGateway);
check({[name]: " 503 "}, ServiceUnavailable);
check({[name]: " 504 "}, GatewayTimeout);
});

@@ -382,0 +382,0 @@

@@ -11,4 +11,5 @@ 'use strict';

const Objects = require('../objects');
// const valueOf = Objects.valueOf;
// const toKeyValuePairs = Objects.toKeyValuePairs;
const merge = Objects.merge;
const copy = Objects.copy;
const copyNamedProperties = Objects.copyNamedProperties;

@@ -21,3 +22,3 @@ const strings = require('../strings');

const from = {a: 1, b: '2', c: {d: 3, e: '4'}};
t.deepEqual(Objects.merge(from, {}), from, 'merge({}) must have all of from');
t.deepEqual(merge(from, {}), from, 'merge({}) must have all of from');

@@ -27,3 +28,3 @@ // merge from empty object

const to0Orig = {a: 2, b: '3', c: {d: 4, e: '5', f: 6}};
t.deepEqual(Objects.merge({}, to0), to0Orig, 'merge with from empty must have all of original to0');
t.deepEqual(merge({}, to0), to0Orig, 'merge with from empty must have all of original to0');

@@ -33,6 +34,6 @@ // shallow merge without replace (all same properties)

const to1Orig = {a: 2, b: '3', c: {d: 4, e: '5', f: 6}};
t.deepEqual(Objects.merge(from, to1), to1Orig, 'shallow merge without replace must still be original to');
t.deepEqual(merge(from, to1), to1Orig, 'shallow merge without replace must still be original to');
// shallow merge with replace (all same properties)
const merge1 = Objects.merge(from, to1, true);
const merge1 = merge(from, to1, true);
t.notDeepEqual(merge1, {a: 2, b: '3', c: {d: 4, e: '5', f: 6}}, 'shallow merge with replace must not be original to');

@@ -44,6 +45,6 @@ t.deepEqual(merge1, from, 'shallow merge with replace must have all of from');

const to2Orig = {a: 2, b: '3', c: {d: 4, e: '5', f: 6}, z: 'ZZZ'};
t.deepEqual(Objects.merge(from, to2), to2Orig, 'shallow merge without replace must still be original to2');
t.deepEqual(merge(from, to2), to2Orig, 'shallow merge without replace must still be original to2');
// shallow merge with replace (with to properties not in from)
const merge2 = Objects.merge(from, to2, true);
const merge2 = merge(from, to2, true);
t.notDeepEqual(merge2, to2Orig, 'shallow merge with replace must not be original to2');

@@ -60,3 +61,3 @@ t.notDeepEqual(merge2, from, 'shallow merge with replace must not be from');

const to3 = {a: 2, b: '3', c: {d: 4, e: '5', f: 6}, z: 'ZZZ'};
t.deepEqual(Objects.merge(from, to3, true, true), {
t.deepEqual(merge(from, to3, true, true), {
a: 1,

@@ -71,3 +72,3 @@ b: '2',

const to4Orig = {a: 2, b: '2', c: {d: 3, e: '5', f: 6, y: 'Y'}, x: 'X', z: 'ZZZ'};
t.deepEqual(Objects.merge(from, to4, false, true), to4Orig, 'deep merge without replace must have all of to4 and only extras of from');
t.deepEqual(merge(from, to4, false, true), to4Orig, 'deep merge without replace must have all of to4 and only extras of from');

@@ -90,3 +91,3 @@ // check that functions get merged in too

const expected5 = {a: a1, b: b, c: c, z: 'Z1'};
t.deepEqual(Objects.merge(from5, to5, false, false), expected5, 'deep merge without replace must have all functions of to5 and only extra functions of from5');
t.deepEqual(merge(from5, to5, false, false), expected5, 'deep merge without replace must have all functions of to5 and only extra functions of from5');
t.equal(to5.a, a1, 'to5.a must be function a1');

@@ -102,3 +103,3 @@ t.equal(to5.b, b, 'to5.b must be function b');

const expected6 = {a: a2, b: b, c: c, x: x, y: 'y1', z: 'Z2'};
t.deepEqual(Objects.merge(from5, to6, true, false), expected6, 'deep merge with replace must have all functions of from5 and only extra functions of to6');
t.deepEqual(merge(from5, to6, true, false), expected6, 'deep merge with replace must have all functions of from5 and only extra functions of to6');
t.equal(to6.a, a2, 'to6.a must be function a2');

@@ -111,3 +112,3 @@ t.equal(to6.x, x, 'to6.x must be function x');

const expected7 = {a: a2, b: b, c: c, x: x, y: 'y1', z: 'Z2'};
t.deepEqual(Objects.merge(from7, to7, true, true), expected7, 'deep merge with replace must have all functions of from7 and only extra functions of to7');
t.deepEqual(merge(from7, to7, true, true), expected7, 'deep merge with replace must have all functions of from7 and only extra functions of to7');

@@ -120,3 +121,3 @@ function d() {

const expected8 = {o: {a: a1, b: b, x: x, y: 'y1'}, c: c, d: d, z: 'Z1'};
t.deepEqual(Objects.merge(from8, to8, false, true), expected8, 'deep merge without replace must have all functions of to8 and only extra functions of from8');
t.deepEqual(merge(from8, to8, false, true), expected8, 'deep merge without replace must have all functions of to8 and only extra functions of from8');

@@ -126,3 +127,3 @@ const from9 = {o: {a: a2, b: b}, c: c, z: 'Z2'};

const expected9 = {o: {a: a2, b: b, x: x, y: 'y1'}, c: c, d: d, z: 'Z2'};
t.deepEqual(Objects.merge(from9, to9, true, true), expected9, 'deep merge with replace must have all functions of from9 and only extra functions of to9');
t.deepEqual(merge(from9, to9, true, true), expected9, 'deep merge with replace must have all functions of from9 and only extra functions of to9');

@@ -132,3 +133,3 @@ const from10 = {o: {a: a2, b: b}, c: c, z: 'Z2'};

const expected10 = {o: {a: a2, b: b}, c: c, d: d, z: 'Z2'};
t.deepEqual(Objects.merge(from10, to10, true, false), expected10, 'shallow merge with replace must have all of from10 and only extra top-level properties of to10');
t.deepEqual(merge(from10, to10, true, false), expected10, 'shallow merge with replace must have all of from10 and only extra top-level properties of to10');

@@ -138,3 +139,3 @@ const from11 = {o: {a: a2, b: b}, c: c, z: 'Z2'};

const expected11 = {o: {a: a1, x: x, y: 'y1'}, c: c, d: d, z: 'Z1'};
t.deepEqual(Objects.merge(from11, to11, false, false), expected11, 'shallow merge with replace must have all of to11 and only extra top-level properties of from11');
t.deepEqual(merge(from11, to11, false, false), expected11, 'shallow merge with replace must have all of to11 and only extra top-level properties of from11');

@@ -150,3 +151,3 @@ // Create infinite loops (non-DAGs)

Objects.merge(o3, c3, false, true);
merge(o3, c3, false, true);
//EEK t.deepEqual fails with "Maximum call stack size exceeded"

@@ -168,33 +169,33 @@ //t.deepEqual(c3, o3, 'deep copy must be deep equal to o3');

// Non-objects
t.deepEqual(Objects.copy(undefined, false), undefined, 'shallow copy of undefined is undefined');
t.deepEqual(Objects.copy(undefined, true), undefined, 'deep copy of undefined is undefined');
t.deepEqual(copy(undefined, false), undefined, 'shallow copy of undefined is undefined');
t.deepEqual(copy(undefined, true), undefined, 'deep copy of undefined is undefined');
t.deepEqual(Objects.copy(null, false), null, 'shallow copy of null is null');
t.deepEqual(Objects.copy(null, true), null, 'deep copy of null is null');
t.deepEqual(copy(null, false), null, 'shallow copy of null is null');
t.deepEqual(copy(null, true), null, 'deep copy of null is null');
t.deepEqual(Objects.copy('', false), '', `shallow copy of '' is ''`);
t.deepEqual(Objects.copy('', true), '', `deep copy of '' is ''`);
t.deepEqual(copy('', false), '', `shallow copy of '' is ''`);
t.deepEqual(copy('', true), '', `deep copy of '' is ''`);
t.deepEqual(Objects.copy('abc', false), 'abc', `shallow copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy('abc', true), 'abc', `deep copy of 'abc' is 'abc'`);
t.deepEqual(copy('abc', false), 'abc', `shallow copy of 'abc' is 'abc'`);
t.deepEqual(copy('abc', true), 'abc', `deep copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy(123, false), 123, 'shallow copy of 123 is 123');
t.deepEqual(Objects.copy(123, true), 123, 'deep copy of 123 is 123');
t.deepEqual(copy(123, false), 123, 'shallow copy of 123 is 123');
t.deepEqual(copy(123, true), 123, 'deep copy of 123 is 123');
t.deepEqual(Objects.copy(123.456, false), 123.456, 'shallow copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(123.456, true), 123.456, 'deep copy of 123.456 is 123.456');
t.deepEqual(copy(123.456, false), 123.456, 'shallow copy of 123.456 is 123.456');
t.deepEqual(copy(123.456, true), 123.456, 'deep copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(true, false), true, 'shallow copy of true is true');
t.deepEqual(Objects.copy(true, true), true, 'deep copy of true is true');
t.deepEqual(copy(true, false), true, 'shallow copy of true is true');
t.deepEqual(copy(true, true), true, 'deep copy of true is true');
t.deepEqual(Objects.copy(false, false), false, 'shallow copy of false is false');
t.deepEqual(Objects.copy(false, false), false, 'deep copy of false is false');
t.deepEqual(copy(false, false), false, 'shallow copy of false is false');
t.deepEqual(copy(false, false), false, 'deep copy of false is false');
// Empty object
t.deepEqual(Objects.copy({}, false), {}, 'shallow copy of {} is {}');
t.deepEqual(Objects.copy({}, true), {}, 'deep copy of {} is {}');
t.deepEqual(copy({}, false), {}, 'shallow copy of {} is {}');
t.deepEqual(copy({}, true), {}, 'deep copy of {} is {}');
// Empty array
t.deepEqual(Objects.copy([], false), [], 'shallow copy of [] is []');
t.deepEqual(Objects.copy([], true), [], 'deep copy of [] is []');
t.deepEqual(copy([], false), [], 'shallow copy of [] is []');
t.deepEqual(copy([], true), [], 'deep copy of [] is []');

@@ -204,39 +205,2 @@ t.end();

test('copy with non-objects & empty object & empty array', t => {
// Non-objects
t.deepEqual(Objects.copy(undefined, false), undefined, 'shallow copy of undefined is undefined');
t.deepEqual(Objects.copy(undefined, true), undefined, 'deep copy of undefined is undefined');
t.deepEqual(Objects.copy(null, false), null, 'shallow copy of null is null');
t.deepEqual(Objects.copy(null, true), null, 'deep copy of null is null');
t.deepEqual(Objects.copy('', false), '', `shallow copy of '' is ''`);
t.deepEqual(Objects.copy('', true), '', `deep copy of '' is ''`);
t.deepEqual(Objects.copy('abc', false), 'abc', `shallow copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy('abc', true), 'abc', `deep copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy(123, false), 123, 'shallow copy of 123 is 123');
t.deepEqual(Objects.copy(123, true), 123, 'deep copy of 123 is 123');
t.deepEqual(Objects.copy(123.456, false), 123.456, 'shallow copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(123.456, true), 123.456, 'deep copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(true, false), true, 'shallow copy of true is true');
t.deepEqual(Objects.copy(true, true), true, 'deep copy of true is true');
t.deepEqual(Objects.copy(false, false), false, 'shallow copy of false is false');
t.deepEqual(Objects.copy(false, false), false, 'deep copy of false is false');
// Empty object
t.deepEqual(Objects.copy({}, false), {}, 'shallow copy of {} is {}');
t.deepEqual(Objects.copy({}, true), {}, 'deep copy of {} is {}');
// Empty array
t.deepEqual(Objects.copy([], false), [], 'shallow copy of [] is []');
t.deepEqual(Objects.copy([], true), [], 'deep copy of [] is []');
t.end();
});
test('copy', t => {

@@ -255,3 +219,3 @@ function a() {

const o1 = {o: {a: a, x: 'X', p: {b: b, y: 'Y'}}, c: c, z: 'Z'};
const c1 = Objects.copy(o1, false);
const c1 = copy(o1, false);
t.deepEqual(c1, o1, 'shallow copy circular - c1 must be deep equal to o1');

@@ -263,3 +227,3 @@ t.notEqual(c1, o1, 'shallow copy circular - c1 must not be o1');

const o2 = {o: {a: a, x: 'X', p: {b: b, y: 'Y'}}, c: c, z: 'Z'};
const c2 = Objects.copy(o2, true);
const c2 = copy(o2, true);
t.deepEqual(c2, o2, 'deep copy circular - c2 must be deep equal to o2');

@@ -274,3 +238,3 @@ t.notEqual(c2, o2, 'deep copy circular - c2 must not be o2');

o3.o.p.o3Again = o3;
const c3 = Objects.copy(o3, true);
const c3 = copy(o3, true);
//EEK t.deepEqual fails with "Maximum call stack size exceeded"

@@ -297,3 +261,3 @@ //t.deepEqual(c3, o3, 'deep copy must be deep equal to o3');

const a0 = [];
const c0 = Objects.copy(a0, false);
const c0 = copy(a0, false);
t.ok(Array.isArray(c0), `shallow copy ${stringify(c0)} must be an array`);

@@ -305,3 +269,3 @@ t.notEqual(c0, a0, `shallow copy ${stringify(c0)} must not be same instance`);

const a1 = [];
const c1 = Objects.copy(a1, true);
const c1 = copy(a1, true);
t.ok(Array.isArray(c1), `deep copy ${stringify(c1)} must be an array`);

@@ -313,3 +277,3 @@ t.notEqual(c1, a1, `deep copy ${stringify(c1)} must NOT be same instance`);

const a2 = [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]];
const c2 = Objects.copy(a2, false);
const c2 = copy(a2, false);

@@ -327,3 +291,3 @@ t.ok(Array.isArray(c2), `shallow copy ${stringify(c2)} must be an array`);

const a3 = [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]];
const c3 = Objects.copy(a3, true);
const c3 = copy(a3, true);

@@ -353,3 +317,3 @@ t.ok(Array.isArray(c3), `deep copy ${stringify(c3)} must be an array`);

const a0 = {a: []};
const c0 = Objects.copy(a0, false);
const c0 = copy(a0, false);
t.ok(!Array.isArray(c0), `shallow copy ${stringify(c0)} must NOT be an array`);

@@ -361,3 +325,3 @@ t.notEqual(c0, a0, `shallow copy ${stringify(c0)} must not be same instance`);

const a1 = {a: []};
const c1 = Objects.copy(a1, true);
const c1 = copy(a1, true);
t.ok(!Array.isArray(c1), `deep copy ${stringify(c1)} must NOT be an array`);

@@ -369,3 +333,3 @@ t.notEqual(c1, a1, `deep copy ${stringify(c1)} must NOT be same instance`);

const a2 = {a: [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]]};
const c2 = Objects.copy(a2, false);
const c2 = copy(a2, false);

@@ -382,3 +346,3 @@ t.ok(!Array.isArray(c2), `shallow copy ${stringify(c2)} must NOT be an array`);

const a3 = {a: [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]]};
const c3 = Objects.copy(a3, true);
const c3 = copy(a3, true);

@@ -407,3 +371,3 @@ t.ok(!Array.isArray(c3), `deep copy ${stringify(c3)} must NOT be an array`);

const b0 = [];
const c0 = Objects.merge(a0, b0, false, false);
const c0 = merge(a0, b0, false, false);
t.ok(Array.isArray(c0), `shallow merge ${stringify(c0)} must be an array`);

@@ -416,3 +380,3 @@ t.notEqual(c0, a0, `shallow merge ${stringify(c0)} must not be same instance`);

const b1 = [];
const c1 = Objects.merge(a1, b1, false, true);
const c1 = merge(a1, b1, false, true);
t.ok(Array.isArray(c1), `deep merge ${stringify(c1)} must be an array`);

@@ -429,3 +393,3 @@ t.notEqual(c1, a1, `deep merge ${stringify(c1)} must NOT be same instance`);

const b0 = [];
const c0 = Objects.merge(a0, b0, false, false);
const c0 = merge(a0, b0, false, false);
t.ok(Array.isArray(c0), `shallow merge ${stringify(c0)} must be an array`);

@@ -438,3 +402,3 @@ t.notEqual(c0, a0, `shallow merge ${stringify(c0)} must not be same instance`);

const b1 = [];
const c1 = Objects.merge(a1, b1, false, true);
const c1 = merge(a1, b1, false, true);
t.ok(Array.isArray(c1), `deep merge ${stringify(c1)} must be an array`);

@@ -452,3 +416,3 @@ t.notEqual(c1, a1, `deep merge ${stringify(c1)} must NOT be same instance`);

const x0 = [1, 2, 3];
Objects.merge(a0, b0, false, false);
merge(a0, b0, false, false);
t.ok(Array.isArray(b0), `shallow merge ${stringify(b0)} must be an array`);

@@ -462,3 +426,3 @@ t.notEqual(b0, a0, `shallow merge ${stringify(b0)} must not be same instance`);

const x1 = ["1", 2, "3"];
const c1 = Objects.merge(a1, b1, false, true);
const c1 = merge(a1, b1, false, true);
t.ok(Array.isArray(c1), `deep merge ${stringify(c1)} must be an array`);

@@ -476,3 +440,3 @@ t.notEqual(c1, a1, `deep merge ${stringify(c1)} must NOT be same instance`);

const x0 = [9, "8", "3", 4.0];
Objects.merge(a0, b0, false, false);
merge(a0, b0, false, false);
t.ok(Array.isArray(b0), `shallow merge ${stringify(b0)} must be an array`);

@@ -486,3 +450,3 @@ t.notEqual(b0, a0, `shallow merge ${stringify(b0)} must not be same instance`);

const x1 = ["1", 2, "3", 4.0];
Objects.merge(a1, b1, true, false);
merge(a1, b1, true, false);
t.ok(Array.isArray(b1), `deep merge ${stringify(b1)} must be an array`);

@@ -496,3 +460,3 @@ t.notEqual(b1, a1, `deep merge ${stringify(b1)} must NOT be same instance`);

const x2 = ["9", 8, 3, 4.0];
Objects.merge(a2, b2, false, true);
merge(a2, b2, false, true);
t.ok(Array.isArray(b2), `deep merge ${stringify(b2)} must be an array`);

@@ -506,3 +470,3 @@ t.notEqual(b2, a2, `deep merge ${stringify(b2)} must not be same instance`);

const x4 = ["1", 2, "3", 4.0];
Objects.merge(a4, b4, true, true);
merge(a4, b4, true, true);
t.ok(Array.isArray(b4), `deep merge ${stringify(b4)} must be an array`);

@@ -520,3 +484,3 @@ t.notEqual(b4, a4, `deep merge ${stringify(b4)} must NOT be same instance`);

const x0 = ["1", 2, "3", 4.0, [5]];
Objects.merge(a0, b0, false, false);
merge(a0, b0, false, false);
t.ok(Array.isArray(b0), `shallow merge ${stringify(b0)} must be an array`);

@@ -530,3 +494,3 @@ t.notEqual(b0, a0, `shallow merge ${stringify(b0)} must not be same instance`);

const x1 = [9, "8", "3", 4.0, [5]];
Objects.merge(a1, b1, true, false);
merge(a1, b1, true, false);
t.ok(Array.isArray(b1), `deep merge ${stringify(b1)} must be an array`);

@@ -540,3 +504,3 @@ t.notEqual(b1, a1, `deep merge ${stringify(b1)} must NOT be same instance`);

const x2 = [1, 2, 3, 4.0, [5]];
Objects.merge(a2, b2, false, true);
merge(a2, b2, false, true);
t.ok(Array.isArray(b2), `deep merge ${stringify(b2)} must be an array`);

@@ -550,3 +514,3 @@ t.notEqual(b2, a2, `deep merge ${stringify(b2)} must not be same instance`);

const x4 = [9, 8, "3", 4.0, [5]];
Objects.merge(a4, b4, true, true);
merge(a4, b4, true, true);
t.ok(Array.isArray(b4), `deep merge ${stringify(b4)} must be an array`);

@@ -566,3 +530,3 @@ t.notEqual(b4, a4, `deep merge ${stringify(b4)} must NOT be same instance`);

Objects.merge(a1, b1, false, false);
merge(a1, b1, false, false);

@@ -584,3 +548,3 @@ t.ok(Array.isArray(b1), `shallow merge ${stringify(b1)} must be an array`);

Objects.merge(a2, b2, true, false);
merge(a2, b2, true, false);

@@ -613,3 +577,3 @@ t.ok(Array.isArray(b2), `shallow merge ${stringify(b2)} must be an array`);

}]];
Objects.merge(a3, b3, false, true);
merge(a3, b3, false, true);

@@ -631,3 +595,3 @@ t.ok(Array.isArray(b3), `deep merge ${stringify(b3)} must be an array`);

}]];
Objects.merge(a4, b4, true, true);
merge(a4, b4, true, true);

@@ -652,3 +616,3 @@ t.ok(Array.isArray(b4), `deep merge ${stringify(b4)} must be an array`);

const x0 = {a: [1, 2], b: {c: [6]}};
Objects.merge(a0, b0, false, false);
merge(a0, b0, false, false);
t.ok(!Array.isArray(b0), `shallow merge ${stringify(b0)} must NOT be an array`);

@@ -663,3 +627,3 @@ t.ok(Array.isArray(b0.a), `shallow merge ${stringify(b0.a)} must be an array`);

const x1 = {a: [3], b: {c: [4, 5]}};
Objects.merge(a1, b1, true, false);
merge(a1, b1, true, false);
t.ok(!Array.isArray(b1), `shallow merge ${stringify(b1)} must NOT be an array`);

@@ -674,3 +638,3 @@ t.ok(Array.isArray(b1.a), `shallow merge ${stringify(b1.a)} must be an array`);

const x2 = {a: [1, 2], b: {c: [6, 5]}};
Objects.merge(a2, b2, false, true);
merge(a2, b2, false, true);
t.ok(!Array.isArray(b2), `deep merge ${stringify(b2)} must NOT be an array`);

@@ -685,3 +649,3 @@ t.ok(Array.isArray(b2.a), `deep merge ${stringify(b2.a)} must be an array`);

const x3 = {a: [3, 2], b: {c: [4, 5]}};
Objects.merge(a3, b3, true, true);
merge(a3, b3, true, true);
t.ok(!Array.isArray(b3), `deep merge ${stringify(b3)} must NOT be an array`);

@@ -705,3 +669,3 @@ t.ok(Array.isArray(b3.a), `deep merge ${stringify(b3.a)} must be an array`);

Objects.merge(a0, b0, false, false);
merge(a0, b0, false, false);

@@ -718,3 +682,3 @@ t.ok(Array.isArray(b0), `shallow merge ${stringify(b0)} must be an array`);

Objects.merge(a1, b1, true, false);
merge(a1, b1, true, false);

@@ -731,3 +695,3 @@ t.ok(Array.isArray(b1), `shallow merge ${stringify(b1)} must be an array`);

Objects.merge(a2, b2, false, true);
merge(a2, b2, false, true);

@@ -743,3 +707,3 @@ t.ok(Array.isArray(b2), `deep merge ${stringify(b2)} must be an array`);

const x3 = [9,7]; x3.a = [3,2]; x3.b = {c: [4,5]};
Objects.merge(a3, b3, true, true);
merge(a3, b3, true, true);
t.ok(Array.isArray(b3), `deep merge ${stringify(b3)} must be an array`);

@@ -763,6 +727,6 @@ t.deepEqual(b3, x3, `deep merge ${stringify(b3)} must be deep equal`);

Objects.merge(a0, b0, false, false);
merge(a0, b0, false, false);
t.ok(!Array.isArray(b0), `shallow merge ${stringify(b0)} must not be an array`);
t.deepEqual(b0, x0, `shallow merge ${stringify(b0)} must be deep equal`);
// t.deepEqual(b0, x0, `shallow merge ${stringify(b0)} must be deep equal`);
t.deepEqual(b0.a, x0.a, `shallow merge a ${stringify(b0.a)} must be deep equal`);

@@ -776,6 +740,6 @@ t.deepEqual(b0.b, x0.b, `shallow merge b ${stringify(b0.b)} must be deep equal`);

Objects.merge(a1, b1, true, false);
merge(a1, b1, true, false);
t.ok(!Array.isArray(b1), `shallow merge ${stringify(b1)} must not be an array`);
t.deepEqual(b1, x1, `shallow merge ${stringify(b1)} must be deep equal`);
// t.deepEqual(b1, x1, `shallow merge ${stringify(b1)} must be deep equal`);
t.deepEqual(b1.a, x1.a, `shallow merge a ${stringify(b1.a)} must be deep equal`);

@@ -789,6 +753,6 @@ t.deepEqual(b1.b, x1.b, `shallow merge b ${stringify(b1.b)} must be deep equal`);

Objects.merge(a2, b2, false, true);
merge(a2, b2, false, true);
t.ok(!Array.isArray(b2), `deep merge ${stringify(b2)} must not be an array`);
t.deepEqual(b2, x2, `deep merge ${stringify(b2)} must be deep equal`);
// t.deepEqual(b2, x2, `deep merge ${stringify(b2)} must be deep equal`);
t.deepEqual(b2.a, x2.a, `deep merge a ${stringify(b2.a)} must be deep equal`);

@@ -802,6 +766,6 @@ t.deepEqual(b2.b, x2.b, `deep merge b ${stringify(b2.b)} must be deep equal`);

Objects.merge(a3, b3, true, true);
merge(a3, b3, true, true);
t.ok(!Array.isArray(b3), `deep merge ${stringify(b3)} must not be an array`);
t.deepEqual(b3, x3, `deep merge ${stringify(b3)} must be deep equal`);
// t.deepEqual(b3, x3, `deep merge ${stringify(b3)} must be deep equal`);
t.deepEqual(b3.a, x3.a, `deep merge a ${stringify(b3.a)} must be deep equal`);

@@ -817,31 +781,32 @@ t.deepEqual(b3.b, x3.b, `deep merge b ${stringify(b3.b)} must be deep equal`);

const omit = true;
t.deepEqual(Objects.copyNamedProperties(undefined, ['a.b.c'], compact, deep, omit), undefined, `(undefined, ['a.b.c'], compact, deep, omit) must be undefined`);
t.deepEqual(Objects.copyNamedProperties(null, ['a.b.c'], compact, deep, omit), null, `(null, ['a.b.c'], compact, deep, omit) must be null`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], compact, deep, omit), {}, `({}, ['a.b.c'], compact, deep, omit) must be {}`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], compact, deep, !omit), {'a.b.c': undefined}, `({}, ['a.b.c'], compact, deep, !omit) must be {'a.b.c': undefined}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], compact, deep, omit), {}, `([] ['a.b.c'], compact, deep, omit) must be {}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], compact, deep, !omit), {'a.b.c': undefined}, `([], ['a.b.c'], compact, deep, !omit) must be {'a.b.c': undefined}`);
t.deepEqual(copyNamedProperties(undefined, ['a.b.c'], compact, deep, omit), undefined, `(undefined, ['a.b.c'], compact, deep, omit) must be undefined`);
t.deepEqual(copyNamedProperties(null, ['a.b.c'], compact, deep, omit), null, `(null, ['a.b.c'], compact, deep, omit) must be null`);
t.deepEqual(copyNamedProperties({}, ['a.b.c'], compact, deep, omit), {}, `({}, ['a.b.c'], compact, deep, omit) must be {}`);
t.deepEqual(copyNamedProperties({}, ['a.b.c'], compact, deep, !omit), {'a.b.c': undefined}, `({}, ['a.b.c'], compact, deep, !omit) must be {'a.b.c': undefined}`);
t.deepEqual(copyNamedProperties([], ['a.b.c'], compact, deep, omit), {}, `([] ['a.b.c'], compact, deep, omit) must be {}`);
t.deepEqual(copyNamedProperties([], ['a.b.c'], compact, deep, !omit), {'a.b.c': undefined}, `([], ['a.b.c'], compact, deep, !omit) must be {'a.b.c': undefined}`);
const o = {a: 1, b: {c: 'c', d: {e: 'e'}}};
t.deepEqual(Objects.copyNamedProperties(o, ['a'], compact, deep, !omit), {a: 1}, `(o, [a], compact, deep, !omit) must be {a: 1}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b'], compact, deep, !omit), {b: {c: 'c', d: {e: 'e'}}}, `(o, [b], compact, deep, !omit) must be {b: {c: 'c', d: {e: 'e'}}}`);
t.deepEqual(copyNamedProperties(o, ['a'], compact, deep, !omit), {a: 1}, `(o, [a], compact, deep, !omit) must be {a: 1}`);
t.deepEqual(copyNamedProperties(o, ['b'], compact, deep, !omit), {b: {c: 'c', d: {e: 'e'}}}, `(o, [b], compact, deep, !omit) must be {b: {c: 'c', d: {e: 'e'}}}`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], compact, deep, !omit).b, o.b, `(o, [b], compact, deep, !omit).b must NOT be o.b`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], undefined, !deep, !omit).b, o.b, `(o, [b], !deep, !omit).b must NOT be o.b`);
t.notEqual(copyNamedProperties(o, ['b'], compact, deep, !omit).b, o.b, `(o, [b], compact, deep, !omit).b must NOT be o.b`);
t.equal(copyNamedProperties(o, ['b'], undefined, !deep, !omit).b, o.b, `(o, [b], !deep, !omit).b must be o.b`);
t.notEqual(copyNamedProperties(o, ['b'], undefined, deep, !omit).b, o.b, `(o, [b], deep, !omit).b must NOT be o.b`);
t.equal(Objects.copyNamedProperties(o, ['b'], compact, deep, !omit).b.c, o.b.c, `(o, [b], compact, deep, !omit).b.c must equal o.b.c`);
t.equal(Objects.copyNamedProperties(o, ['b'], undefined, !deep, !omit).b.c, o.b.c, `(o, [b], !deep, !omit).b.c must equal o.b.c`);
t.equal(copyNamedProperties(o, ['b'], compact, deep, !omit).b.c, o.b.c, `(o, [b], compact, deep, !omit).b.c must equal o.b.c`);
t.equal(copyNamedProperties(o, ['b'], undefined, !deep, !omit).b.c, o.b.c, `(o, [b], !deep, !omit).b.c must equal o.b.c`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], compact, deep, !omit).b.d, o.b.d, `(o, [b], compact, deep, !omit).b.d must NOT be o.b.d`);
t.equal(Objects.copyNamedProperties(o, ['b'], undefined, !deep, !omit).b.d, o.b.d, `(o, [b], !deep, !omit).b.d must be o.b.d`);
t.notEqual(copyNamedProperties(o, ['b'], compact, deep, !omit).b.d, o.b.d, `(o, [b], compact, deep, !omit).b.d must NOT be o.b.d`);
t.equal(copyNamedProperties(o, ['b'], undefined, !deep, !omit).b.d, o.b.d, `(o, [b], !deep, !omit).b.d must be o.b.d`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.c'], compact, deep, !omit), {'b.c': 'c'}, `(o, [b.c], compact, deep, !omit) must be {'b.c': 'c'}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d'], compact, deep, !omit), {'b.d': {e: 'e'}}, `(o, [b.d], compact, deep, !omit) must be {'b.d': {e: 'e'}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d.e'], compact, deep, !omit), {'b.d.e': 'e'}, `(o, [b.d.e], compact, deep, !omit) must be {'b.d.e': 'e'}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], compact, deep, !omit), {'x.y.z': undefined}, `(o, [x.y.z], compact, deep, !omit) must be {'x.y.z': undefined}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], compact, deep, omit), {}, `(o, [x.y.z], compact, deep, omit) must be {}`);
t.deepEqual(copyNamedProperties(o, ['b.c'], compact, deep, !omit), {'b.c': 'c'}, `(o, [b.c], compact, deep, !omit) must be {'b.c': 'c'}`);
t.deepEqual(copyNamedProperties(o, ['b.d'], compact, deep, !omit), {'b.d': {e: 'e'}}, `(o, [b.d], compact, deep, !omit) must be {'b.d': {e: 'e'}}`);
t.deepEqual(copyNamedProperties(o, ['b.d.e'], compact, deep, !omit), {'b.d.e': 'e'}, `(o, [b.d.e], compact, deep, !omit) must be {'b.d.e': 'e'}`);
t.deepEqual(copyNamedProperties(o, ['x.y.z'], compact, deep, !omit), {'x.y.z': undefined}, `(o, [x.y.z], compact, deep, !omit) must be {'x.y.z': undefined}`);
t.deepEqual(copyNamedProperties(o, ['x.y.z'], compact, deep, omit), {}, `(o, [x.y.z], compact, deep, omit) must be {}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b'], compact, deep, !omit), o, `(o, [a,b], compact, deep, !omit) must equal o`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d'], compact, deep, !omit), {a: 1, 'b.c': 'c', 'b.d': {e: 'e'}}, `(o, [a,b], compact, deep, !omit) must equal {a: 1, 'b.c': 'c', 'b.d': {e: 'e'}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d.e'], compact, deep, !omit), {a: 1, 'b.c': 'c', 'b.d.e': 'e'}, `(o, [a,b], compact, deep, !omit) must equal {a: 1, 'b.c': 'c', 'b.d.e': 'e'}`);
t.deepEqual(copyNamedProperties(o, ['a', 'b'], compact, deep, !omit), o, `(o, [a,b], compact, deep, !omit) must equal o`);
t.deepEqual(copyNamedProperties(o, ['a', 'b.c', 'b.d'], compact, deep, !omit), {a: 1, 'b.c': 'c', 'b.d': {e: 'e'}}, `(o, [a,b], compact, deep, !omit) must equal {a: 1, 'b.c': 'c', 'b.d': {e: 'e'}}`);
t.deepEqual(copyNamedProperties(o, ['a', 'b.c', 'b.d.e'], compact, deep, !omit), {a: 1, 'b.c': 'c', 'b.d.e': 'e'}, `(o, [a,b], compact, deep, !omit) must equal {a: 1, 'b.c': 'c', 'b.d.e': 'e'}`);

@@ -855,33 +820,35 @@ t.end();

const omit = true;
t.deepEqual(Objects.copyNamedProperties(undefined, ['a.b.c'], !compact, deep, omit), undefined, `(undefined, ['a.b.c'], !compact, deep, omit) must be undefined`);
t.deepEqual(Objects.copyNamedProperties(null, ['a.b.c'], !compact, deep, omit), null, `(null, ['a.b.c'], !compact, deep, omit) must be null`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], !compact, deep, omit), {}, `({}, ['a.b.c'], !compact, deep, omit) must be {}`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], !compact, deep, !omit), {a: {b: {c: undefined}}}, `({}, ['a.b.c'], !compact, deep, !omit) must be {a: {b: {c: undefined}}}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], !compact, deep, omit), {}, `([] ['a.b.c'], !compact, deep, omit) must be {}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], !compact, deep, !omit), {a: {b: {c: undefined}}}, `([], ['a.b.c'], !compact, deep, !omit) must be {a: {b: {c: undefined}}}`);
t.deepEqual(copyNamedProperties(undefined, ['a.b.c'], !compact, deep, omit), undefined, `(undefined, ['a.b.c'], !compact, deep, omit) must be undefined`);
t.deepEqual(copyNamedProperties(null, ['a.b.c'], !compact, deep, omit), null, `(null, ['a.b.c'], !compact, deep, omit) must be null`);
t.deepEqual(copyNamedProperties({}, ['a.b.c'], !compact, deep, omit), {}, `({}, ['a.b.c'], !compact, deep, omit) must be {}`);
t.deepEqual(copyNamedProperties({}, ['a.b.c'], !compact, deep, !omit), {a: {b: {c: undefined}}}, `({}, ['a.b.c'], !compact, deep, !omit) must be {a: {b: {c: undefined}}}`);
t.deepEqual(copyNamedProperties([], ['a.b.c'], !compact, deep, omit), {}, `([] ['a.b.c'], !compact, deep, omit) must be {}`);
t.deepEqual(copyNamedProperties([], ['a.b.c'], !compact, deep, !omit), {a: {b: {c: undefined}}}, `([], ['a.b.c'], !compact, deep, !omit) must be {a: {b: {c: undefined}}}`);
const o = {a: 1, b: {c: 'c', d: {e: 'e'}}};
t.deepEqual(Objects.copyNamedProperties(o, ['a'], !compact, deep, !omit), {a: 1}, `(o, [a], !compact, deep, !omit) must be {a: 1}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b'], !compact, deep, !omit), {b: {c: 'c', d: {e: 'e'}}}, `(o, [b], !compact, deep, !omit) must be {b: {c: 'c', d: {e: 'e'}}}`);
t.deepEqual(copyNamedProperties(o, ['a'], !compact, deep, !omit), {a: 1}, `(o, [a], !compact, deep, !omit) must be {a: 1}`);
t.deepEqual(copyNamedProperties(o, ['b'], !compact, deep, !omit), {b: {c: 'c', d: {e: 'e'}}}, `(o, [b], !compact, deep, !omit) must be {b: {c: 'c', d: {e: 'e'}}}`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], !compact, deep, !omit).b, o.b, `(o, [b], !compact, deep, !omit).b must NOT be o.b`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], undefined, !deep, !omit).b, o.b, `(o, [b], !deep, !omit).b must NOT be o.b`);
t.notEqual(copyNamedProperties(o, ['b'], !compact, deep, !omit).b, o.b, `(o, [b], !compact, deep, !omit).b must NOT be o.b`);
t.equal(copyNamedProperties(o, ['b'], undefined, !deep, !omit).b, o.b, `(o, [b], !deep, !omit).b must be o.b`);
t.equal(copyNamedProperties(o, ['b'], !compact, !deep, !omit).b, o.b, `(o, [b], !compact, !deep, !omit}).b must be o.b`);
t.notEqual(copyNamedProperties(o, ['b'], undefined, deep, !omit).b, o.b, `(o, [b], deep, !omit}).b must NOT be o.b`);
t.equal(Objects.copyNamedProperties(o, ['b'], !compact, deep, !omit).b.c, o.b.c, `(o, [b], !compact, deep, !omit).b.c must equal o.b.c`);
t.equal(Objects.copyNamedProperties(o, ['b'], undefined, !deep, !omit).b.c, o.b.c, `(o, [b], !deep, !omit).b.c must equal o.b.c`);
t.equal(copyNamedProperties(o, ['b'], !compact, deep, !omit).b.c, o.b.c, `(o, [b], !compact, deep, !omit).b.c must equal o.b.c`);
t.equal(copyNamedProperties(o, ['b'], undefined, !deep, !omit).b.c, o.b.c, `(o, [b], !deep, !omit).b.c must equal o.b.c`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], !compact, deep, !omit).b.d, o.b.d, `(o, [b], !compact, deep, !omit).b.d must NOT be o.b.d`);
t.equal(Objects.copyNamedProperties(o, ['b'], undefined, !deep, !omit).b.d, o.b.d, `(o, [b], !deep, !omit).b.d must be o.b.d`);
t.notEqual(copyNamedProperties(o, ['b'], !compact, deep, !omit).b.d, o.b.d, `(o, [b], !compact, deep, !omit).b.d must NOT be o.b.d`);
t.equal(copyNamedProperties(o, ['b'], undefined, !deep, !omit).b.d, o.b.d, `(o, [b], !deep, !omit).b.d must be o.b.d`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.c'], !compact, deep, !omit), {b: {c: 'c'}}, `(o, [b.c], !compact, deep, !omit) must be {b: {c: 'c'}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d'], !compact, deep, !omit), {b: {d: {e: 'e'}}}, `(o, [b.d], !compact, deep, !omit) must be {b: {d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d.e'], !compact, deep, !omit), {b: {d: {e: 'e'}}}, `(o, [b.d.e], !compact, deep, !omit) must be {b: {d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], !compact, deep, !omit), {x: {y: {z: undefined}}}, `(o, [x.y.z], !compact, deep, !omit) must be {x: {y: {z: undefined}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], !compact, deep, omit), {}, `(o, [x.y.z], !compact, deep, omit) must be {}`);
t.deepEqual(copyNamedProperties(o, ['b.c'], !compact, deep, !omit), {b: {c: 'c'}}, `(o, [b.c], !compact, deep, !omit) must be {b: {c: 'c'}}`);
t.deepEqual(copyNamedProperties(o, ['b.d'], !compact, deep, !omit), {b: {d: {e: 'e'}}}, `(o, [b.d], !compact, deep, !omit) must be {b: {d: {e: 'e'}}}`);
t.deepEqual(copyNamedProperties(o, ['b.d.e'], !compact, deep, !omit), {b: {d: {e: 'e'}}}, `(o, [b.d.e], !compact, deep, !omit) must be {b: {d: {e: 'e'}}}`);
t.deepEqual(copyNamedProperties(o, ['x.y.z'], !compact, deep, !omit), {x: {y: {z: undefined}}}, `(o, [x.y.z], !compact, deep, !omit) must be {x: {y: {z: undefined}}}`);
t.deepEqual(copyNamedProperties(o, ['x.y.z'], !compact, deep, omit), {}, `(o, [x.y.z], !compact, deep, omit) must be {}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b'], !compact, deep, !omit), o, `(o, [a,b], !compact, deep, !omit) must equal o`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d'], !compact, deep, !omit), {a: 1, b: {c: 'c', d: {e: 'e'}}}, `(o, [a,b], !compact, deep, !omit) must equal {a: 1, b: {c: 'c', d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d.e'], !compact, deep, !omit), {a: 1, b: {c: 'c', d: {e: 'e'}}}, `(o, [a,b], !compact, deep, !omit) must equal {a: 1, b: {c: 'c', d: {e: 'e'}}}`);
t.deepEqual(copyNamedProperties(o, ['a', 'b'], !compact, deep, !omit), o, `(o, [a,b], !compact, deep, !omit) must equal o`);
t.deepEqual(copyNamedProperties(o, ['a', 'b.c', 'b.d'], !compact, deep, !omit), {a: 1, b: {c: 'c', d: {e: 'e'}}}, `(o, [a,b], !compact, deep, !omit) must equal {a: 1, b: {c: 'c', d: {e: 'e'}}}`);
t.deepEqual(copyNamedProperties(o, ['a', 'b.c', 'b.d.e'], !compact, deep, !omit), {a: 1, b: {c: 'c', d: {e: 'e'}}}, `(o, [a,b], !compact, deep, !omit) must equal {a: 1, b: {c: 'c', d: {e: 'e'}}}`);
t.end();
});

@@ -13,2 +13,3 @@ 'use strict';

const toKeyValuePairs = Objects.toKeyValuePairs;
const getOwnPropertyNamesRecursively = Objects.getOwnPropertyNamesRecursively;

@@ -18,2 +19,6 @@ const strings = require('../strings');

// =====================================================================================================================
// valueOf
// =====================================================================================================================
test('valueOf', t => {

@@ -58,396 +63,102 @@ function check(value, expected) {

test('merge', t => {
// merge into empty object
const from = {a: 1, b: '2', c: {d: 3, e: '4'}};
t.deepEqual(Objects.merge(from, {}), from, 'merge({}) must have all of from');
// merge from empty object
const to0 = {a: 2, b: '3', c: {d: 4, e: '5', f: 6}};
const to0Orig = {a: 2, b: '3', c: {d: 4, e: '5', f: 6}};
t.deepEqual(Objects.merge({}, to0), to0Orig, 'merge with from empty must have all of original to0');
// shallow merge without replace (all same properties)
const to1 = {a: 2, b: '3', c: {d: 4, e: '5', f: 6}};
const to1Orig = {a: 2, b: '3', c: {d: 4, e: '5', f: 6}};
t.deepEqual(Objects.merge(from, to1), to1Orig, 'shallow merge without replace must still be original to');
// shallow merge with replace (all same properties)
const merge1 = Objects.merge(from, to1, {replace: true});
t.notDeepEqual(merge1, {a: 2, b: '3', c: {d: 4, e: '5', f: 6}}, 'shallow merge with replace must not be original to');
t.deepEqual(merge1, from, 'shallow merge with replace must have all of from');
// shallow merge without replace (with to properties not in from)
const to2 = {a: 2, b: '3', c: {d: 4, e: '5', f: 6}, z: 'ZZZ'};
const to2Orig = {a: 2, b: '3', c: {d: 4, e: '5', f: 6}, z: 'ZZZ'};
t.deepEqual(Objects.merge(from, to2), to2Orig, 'shallow merge without replace must still be original to2');
// shallow merge with replace (with to properties not in from)
const merge2 = Objects.merge(from, to2, {replace: true});
t.notDeepEqual(merge2, to2Orig, 'shallow merge with replace must not be original to2');
t.notDeepEqual(merge2, from, 'shallow merge with replace must not be from');
t.deepEqual(merge2, {
a: 1,
b: '2',
c: {d: 3, e: '4'},
z: 'ZZZ'
}, 'shallow merge with replace must have all of from + only top-level extra original to2 properties');
// deep must preserve inner extras
const to3 = {a: 2, b: '3', c: {d: 4, e: '5', f: 6}, z: 'ZZZ'};
t.deepEqual(Objects.merge(from, to3, {replace: true, deep: true}), {
a: 1,
b: '2',
c: {d: 3, e: '4', f: 6},
z: 'ZZZ'
}, 'deep merge with replace must have all of from + all extra original to2 properties');
// deep without replace must NOT replace any matching properties
const to4 = {a: 2, c: {e: '5', f: 6, y: 'Y'}, x: 'X', z: 'ZZZ'};
const to4Orig = {a: 2, b: '2', c: {d: 3, e: '5', f: 6, y: 'Y'}, x: 'X', z: 'ZZZ'};
t.deepEqual(Objects.merge(from, to4, {replace: false, deep: true}), to4Orig, 'deep merge without replace must have all of to4 and only extras of from');
// check that functions get merged in too
function a1() {
}
function a2() {
}
function b() {
}
function c() {
}
const from5 = {a: a2, b: b, c: c, z: 'Z2'};
const to5 = {a: a1, z: 'Z1'};
const expected5 = {a: a1, b: b, c: c, z: 'Z1'};
t.deepEqual(Objects.merge(from5, to5, {replace: false, deep: false}), expected5, 'deep merge without replace must have all functions of to5 and only extra functions of from5');
t.equal(to5.a, a1, 'to5.a must be function a1');
t.equal(to5.b, b, 'to5.b must be function b');
t.equal(to5.c, c, 'to5.c must be function c');
t.equal(to5.z, 'Z1', 'to5.z must be Z1');
function x() {
}
const to6 = {a: a1, x: x, y: 'y1', z: 'Z1'};
const expected6 = {a: a2, b: b, c: c, x: x, y: 'y1', z: 'Z2'};
t.deepEqual(Objects.merge(from5, to6, {replace: true, deep: false}), expected6, 'deep merge with replace must have all functions of from5 and only extra functions of to6');
t.equal(to6.a, a2, 'to6.a must be function a2');
t.equal(to6.x, x, 'to6.x must be function x');
t.equal(to6.z, 'Z2', 'to5.z must be Z2');
const from7 = {a: a2, b: b, c: c, z: 'Z2'};
const to7 = {a: a1, x: x, y: 'y1', z: 'Z1'};
const expected7 = {a: a2, b: b, c: c, x: x, y: 'y1', z: 'Z2'};
t.deepEqual(Objects.merge(from7, to7, {replace: true, deep: true}), expected7, 'deep merge with replace must have all functions of from7 and only extra functions of to7');
function d() {
}
const from8 = {o: {a: a2, b: b}, c: c, z: 'Z2'};
const to8 = {o: {a: a1, x: x, y: 'y1'}, d: d, z: 'Z1'};
const expected8 = {o: {a: a1, b: b, x: x, y: 'y1'}, c: c, d: d, z: 'Z1'};
t.deepEqual(Objects.merge(from8, to8, {replace: false, deep: true}), expected8, 'deep merge without replace must have all functions of to8 and only extra functions of from8');
const from9 = {o: {a: a2, b: b}, c: c, z: 'Z2'};
const to9 = {o: {a: a1, x: x, y: 'y1'}, d: d, z: 'Z1'};
const expected9 = {o: {a: a2, b: b, x: x, y: 'y1'}, c: c, d: d, z: 'Z2'};
t.deepEqual(Objects.merge(from9, to9, {replace: true, deep: true}), expected9, 'deep merge with replace must have all functions of from9 and only extra functions of to9');
const from10 = {o: {a: a2, b: b}, c: c, z: 'Z2'};
const to10 = {o: {a: a1, x: x, y: 'y1'}, d: d, z: 'Z1'};
const expected10 = {o: {a: a2, b: b}, c: c, d: d, z: 'Z2'};
t.deepEqual(Objects.merge(from10, to10, {replace: true, deep: false}), expected10, 'shallow merge with replace must have all of from10 and only extra top-level properties of to10');
const from11 = {o: {a: a2, b: b}, c: c, z: 'Z2'};
const to11 = {o: {a: a1, x: x, y: 'y1'}, d: d, z: 'Z1'};
const expected11 = {o: {a: a1, x: x, y: 'y1'}, c: c, d: d, z: 'Z1'};
t.deepEqual(Objects.merge(from11, to11, {replace: false, deep: false}), expected11, 'shallow merge with replace must have all of to11 and only extra top-level properties of from11');
// Create infinite loops (non-DAGs)
const o3 = {o: {a: a1, x: 'X', p: {b: b, y: 'Y'}}, c: c, z: 'Z'};
o3.o.o3Again = o3;
o3.o.p.o3Again = o3;
const c3 = {o: {a: a2, x: 'X2', p: {b: b, y: 'Y2'}}, c: c, z: 'Z2'};
c3.o.o3Again = c3;
c3.o.p.o3Again = c3;
Objects.merge(o3, c3, {replace: false, deep: true});
//EEK t.deepEqual fails with "Maximum call stack size exceeded"
//t.deepEqual(c3, o3, 'deep copy must be deep equal to o3');
t.deepEqual(Object.getOwnPropertyNames(c3).sort(), Object.getOwnPropertyNames(o3).sort(), 'deep merge circular - c3 must have same names as o3');
t.deepEqual(Object.getOwnPropertyNames(c3.o).sort(), Object.getOwnPropertyNames(o3.o).sort(), 'deep merge circular - c3.o must have same names as o3.o');
t.deepEqual(Object.getOwnPropertyNames(c3.o.p).sort(), Object.getOwnPropertyNames(o3.o.p).sort(), 'deep merge circular - c3.o.p must have same names as o3.o.p');
t.notEqual(c3, o3, 'deep merge circular - c3 must not be o3');
t.notEqual(c3.o, o3.o, 'deep merge circular - c3.o must not be o3.o');
t.notEqual(c3.o.p, o3.o.p, 'deep merge circular - c3.o.p must not be o3.o.p');
t.equal(c3, c3.o.o3Again, 'deep merge circular - c3 must be c3.o.o3Again');
t.equal(c3, c3.o.p.o3Again, 'deep merge circular - c3 must be c3.o.p.o3Again');
t.end();
});
test('copy with non-objects & empty object & empty array', t => {
// Non-objects
t.deepEqual(Objects.copy(undefined, {deep: false}), undefined, 'shallow copy of undefined is undefined');
t.deepEqual(Objects.copy(undefined, {deep: true}), undefined, 'deep copy of undefined is undefined');
t.deepEqual(Objects.copy(null, {deep: false}), null, 'shallow copy of null is null');
t.deepEqual(Objects.copy(null, {deep: true}), null, 'deep copy of null is null');
t.deepEqual(Objects.copy('', {deep: false}), '', `shallow copy of '' is ''`);
t.deepEqual(Objects.copy('', {deep: true}), '', `deep copy of '' is ''`);
t.deepEqual(Objects.copy('abc', {deep: false}), 'abc', `shallow copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy('abc', {deep: true}), 'abc', `deep copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy(123, {deep: false}), 123, 'shallow copy of 123 is 123');
t.deepEqual(Objects.copy(123, {deep: true}), 123, 'deep copy of 123 is 123');
t.deepEqual(Objects.copy(123.456, {deep: false}), 123.456, 'shallow copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(123.456, {deep: true}), 123.456, 'deep copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(true, {deep: false}), true, 'shallow copy of true is true');
t.deepEqual(Objects.copy(true, {deep: true}), true, 'deep copy of true is true');
t.deepEqual(Objects.copy(false, {deep: false}), false, 'shallow copy of false is false');
t.deepEqual(Objects.copy(false, {deep: false}), false, 'deep copy of false is false');
// Empty object
t.deepEqual(Objects.copy({}, {deep: false}), {}, 'shallow copy of {} is {}');
t.deepEqual(Objects.copy({}, {deep: true}), {}, 'deep copy of {} is {}');
// Empty array
t.deepEqual(Objects.copy([], {deep: false}), [], 'shallow copy of [] is []');
t.deepEqual(Objects.copy([], {deep: true}), [], 'deep copy of [] is []');
t.end();
});
test('copy with non-objects & empty object & empty array', t => {
// Non-objects
t.deepEqual(Objects.copy(undefined, {deep: false}), undefined, 'shallow copy of undefined is undefined');
t.deepEqual(Objects.copy(undefined, {deep: true}), undefined, 'deep copy of undefined is undefined');
t.deepEqual(Objects.copy(null, {deep: false}), null, 'shallow copy of null is null');
t.deepEqual(Objects.copy(null, {deep: true}), null, 'deep copy of null is null');
t.deepEqual(Objects.copy('', {deep: false}), '', `shallow copy of '' is ''`);
t.deepEqual(Objects.copy('', {deep: true}), '', `deep copy of '' is ''`);
t.deepEqual(Objects.copy('abc', {deep: false}), 'abc', `shallow copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy('abc', {deep: true}), 'abc', `deep copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy(123, {deep: false}), 123, 'shallow copy of 123 is 123');
t.deepEqual(Objects.copy(123, {deep: true}), 123, 'deep copy of 123 is 123');
t.deepEqual(Objects.copy(123.456, {deep: false}), 123.456, 'shallow copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(123.456, {deep: true}), 123.456, 'deep copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(true, {deep: false}), true, 'shallow copy of true is true');
t.deepEqual(Objects.copy(true, {deep: true}), true, 'deep copy of true is true');
t.deepEqual(Objects.copy(false, {deep: false}), false, 'shallow copy of false is false');
t.deepEqual(Objects.copy(false, {deep: false}), false, 'deep copy of false is false');
// Empty object
t.deepEqual(Objects.copy({}, {deep: false}), {}, 'shallow copy of {} is {}');
t.deepEqual(Objects.copy({}, {deep: true}), {}, 'deep copy of {} is {}');
// Empty array
t.deepEqual(Objects.copy([], {deep: false}), [], 'shallow copy of [] is []');
t.deepEqual(Objects.copy([], {deep: true}), [], 'deep copy of [] is []');
t.end();
});
test('copy', t => {
function a() {
}
function b() {
}
function c() {
}
//function d() {}
const o1 = {o: {a: a, x: 'X', p: {b: b, y: 'Y'}}, c: c, z: 'Z'};
const c1 = Objects.copy(o1, {deep: false});
t.deepEqual(c1, o1, 'shallow copy circular - c1 must be deep equal to o1');
t.notEqual(c1, o1, 'shallow copy circular - c1 must not be o1');
t.equal(c1.o, o1.o, 'shallow copy circular - c1.o must be o1.o');
t.equal(c1.o.p, o1.o.p, 'shallow copy circular - c1.o.p must be o1.o.p');
const o2 = {o: {a: a, x: 'X', p: {b: b, y: 'Y'}}, c: c, z: 'Z'};
const c2 = Objects.copy(o2, {deep: true});
t.deepEqual(c2, o2, 'deep copy circular - c2 must be deep equal to o2');
t.notEqual(c2, o2, 'deep copy circular - c2 must not be o2');
t.notEqual(c2.o, o2.o, 'deep copy circular - c2.o must not be o2.o');
t.notEqual(c2.o.p, o2.o.p, 'deep copy circular - c2.o.p must not be o2.o.p');
// Create infinite loops (non-DAGs)
const o3 = {o: {a: a, x: 'X', p: {b: b, y: 'Y'}}, c: c, z: 'Z'};
o3.o.o3Again = o3;
o3.o.p.o3Again = o3;
const c3 = Objects.copy(o3, {deep: true});
//EEK t.deepEqual fails with "Maximum call stack size exceeded"
//t.deepEqual(c3, o3, 'deep copy must be deep equal to o3');
t.deepEqual(Object.getOwnPropertyNames(c3), Object.getOwnPropertyNames(o3), 'deep copy circular - c3 must have same names as o3');
t.deepEqual(Object.getOwnPropertyNames(c3.o), Object.getOwnPropertyNames(o3.o), 'deep copy circular - c3.o must have same names as o3.o');
t.deepEqual(Object.getOwnPropertyNames(c3.o.p), Object.getOwnPropertyNames(o3.o.p), 'deep copy circular - c3.o.p must have same names as o3.o.p');
t.notEqual(c3, o3, 'deep copy circular - c3 must not be o3');
t.notEqual(c3.o, o3.o, 'deep copy circular - c3.o must not be o3.o');
t.notEqual(c3.o.p, o3.o.p, 'deep copy circular - c3.o.p must not be o3.o.p');
t.equal(c3, c3.o.o3Again, 'deep copy circular - c3 must be c3.o.o3Again');
t.equal(c3, c3.o.p.o3Again, 'deep copy circular - c3 must be c3.o.p.o3Again');
t.end();
});
// =====================================================================================================================
// copy arrays
// toKeyValuePairs
// =====================================================================================================================
test('copy arrays', t => {
// Shallow copy of empty array
const a0 = [];
const c0 = Objects.copy(a0, {deep: false});
t.ok(Array.isArray(c0), `shallow copy ${stringify(c0)} must be an array`);
t.notEqual(c0, a0, `shallow copy ${stringify(c0)} must not be same instance`);
t.deepEqual(c0, a0, `shallow copy ${stringify(c0)} must be deep equal`);
test('toKeyValuePairs', t => {
// Applied to non-objects
let expected = [];
t.deepEqual(toKeyValuePairs(undefined), expected, `toKeyValuePairs(undefined) must be ${stringify(expected)}`);
t.deepEqual(toKeyValuePairs(null), expected, `toKeyValuePairs(null) must be ${stringify(expected)}`);
t.deepEqual(toKeyValuePairs('abc'), expected, `toKeyValuePairs('abc') must be ${stringify(expected)}`);
t.deepEqual(toKeyValuePairs(123), expected, `toKeyValuePairs(123) must be ${stringify(expected)}`);
// Deep copy of empty array
const a1 = [];
const c1 = Objects.copy(a1, {deep: true});
t.ok(Array.isArray(c1), `deep copy ${stringify(c1)} must be an array`);
t.notEqual(c1, a1, `deep copy ${stringify(c1)} must NOT be same instance`);
t.deepEqual(c1, a1, `deep copy ${stringify(c1)} must be deep equal`);
// Applied to objects
expected = [];
t.deepEqual(toKeyValuePairs({}), expected, `toKeyValuePairs({}) must be ${stringify(expected)}`);
// Shallow copy of complex array
const a2 = [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]];
const c2 = Objects.copy(a2, {deep: false});
expected = [['a', 1], ['b', {c:2}], ['d', '3']];
t.deepEqual(toKeyValuePairs({a:1, b:{c:2}, d:'3'}), expected, `toKeyValuePairs({a:1, b:{c:2}, d:'3'}) must be ${stringify(expected)}`);
t.ok(Array.isArray(c2), `shallow copy ${stringify(c2)} must be an array`);
t.equal(c2.length, a2.length, `shallow copy ${stringify(c2)} lengths must be same`);
t.notEqual(c2, a2, `shallow copy ${stringify(c2)} must NOT be same instance`);
t.deepEqual(c2, a2, `shallow copy ${stringify(c2)} must be deep equal`);
for (let i = 0; i < a2.length; ++i) {
t.equal(c2[i], a2[i], `shallow copy [${i}] ${stringify(c2[i])} must be equal`);
t.deepEqual(c2[i], a2[i], `shallow copy [${i}] ${stringify(c2[i])} must be deep equal`);
}
expected = [['d', '3'], ['b', {c:2}], ['a', 1]];
t.deepEqual(toKeyValuePairs({d:'3', b:{c:2}, a:1}), expected, `toKeyValuePairs({d:'3', b:{c:2}, a:1}) must be ${stringify(expected)}`);
// Deep copy of complex array
const a3 = [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]];
const c3 = Objects.copy(a3, {deep: true});
// Not meant to be applied to arrays, but if so ...
expected = [['length', 0]];
t.deepEqual(toKeyValuePairs([]), expected, `toKeyValuePairs([]) must be ${stringify(expected)}`);
expected = [['0', '1'], ['1', 2], ['2', '3'], ['length', 3]];
t.deepEqual(toKeyValuePairs(['1', 2, '3']), expected, `toKeyValuePairs(['1', 2, '3']) must be ${stringify(expected)}`);
t.ok(Array.isArray(c3), `deep copy ${stringify(c3)} must be an array`);
t.equal(c3.length, a3.length, `deep copy ${stringify(c3)} lengths must be same`);
t.notEqual(c3, a3, `deep copy ${stringify(c3)} must NOT be same instance`);
t.deepEqual(c3, a3, `deep copy ${stringify(c3)} must be deep equal`);
for (let i = 0; i < a3.length; ++i) {
if (a3[i] && typeof a3[i] === 'object')
t.notEqual(c3[i], a3[i], `deep copy [${i}] ${stringify(c3[i])} must NOT be same instance`);
else
t.equal(c3[i], a3[i], `deep copy [${i}] ${stringify(c3[i])} must be equal`);
expected = [['0', '3'], ['1', 2], ['2', '1'], ['length', 3]];
t.deepEqual(toKeyValuePairs(['3',2,'1']), expected, `toKeyValuePairs(['3',2,'1']) must be ${stringify(expected)}`);
t.deepEqual(c3[i], a3[i], `deep copy [${i}] ${stringify(c3[i])} must be deep equal`);
}
t.end();
});
// =====================================================================================================================
// copy objects with arrays
// =====================================================================================================================
test('getOwnPropertyNamesRecursively', t => {
// const skip = true;
t.deepEqual(getOwnPropertyNamesRecursively(undefined), [], `getOwnPropertyNamesRecursively(undefined) must be []`);
t.deepEqual(getOwnPropertyNamesRecursively(null), [], `getOwnPropertyNamesRecursively(null) must be []`);
t.deepEqual(getOwnPropertyNamesRecursively(123), [], `getOwnPropertyNamesRecursively(123) must be []`);
t.deepEqual(getOwnPropertyNamesRecursively("Abc"), [], `getOwnPropertyNamesRecursively("Abc") must be []`);
test('copy objects with arrays', t => {
// Shallow copy of object with empty array
const a0 = {a: []};
const c0 = Objects.copy(a0, {deep: false});
t.ok(!Array.isArray(c0), `shallow copy ${stringify(c0)} must NOT be an array`);
t.notEqual(c0, a0, `shallow copy ${stringify(c0)} must not be same instance`);
t.deepEqual(c0, a0, `shallow copy ${stringify(c0)} must be deep equal`);
t.deepEqual(getOwnPropertyNamesRecursively({a:1}), ['a'], `getOwnPropertyNamesRecursively({a:1}) must be ['a']`);
t.deepEqual(getOwnPropertyNamesRecursively({a:1, b:2, c:3}), ['a', 'b', 'c'], `getOwnPropertyNamesRecursively({a:1, b:2, c:3}) must be ['a, 'b', 'c']`);
t.deepEqual(getOwnPropertyNamesRecursively({a: {b:2}, c:3}), ['a', 'a.b', 'c'], `getOwnPropertyNamesRecursively({a: {b:2}, c:3}) must be ['a, 'a.b', 'c']`);
// Deep copy of empty array
const a1 = {a: []};
const c1 = Objects.copy(a1, {deep: true});
t.ok(!Array.isArray(c1), `deep copy ${stringify(c1)} must NOT be an array`);
t.notEqual(c1, a1, `deep copy ${stringify(c1)} must NOT be same instance`);
t.deepEqual(c1, a1, `deep copy ${stringify(c1)} must be deep equal`);
const expected1 = ['a', 'a.b', 'a.b.c', 'a.b.d', 'a.b.d.e', 'a.b.d.f', 'a.b.d.f.g', 'a.b.d.f.g[0].h', 'a.b.d.f.g[1].i'];
const o1 = {a: {b: {c: 1, d: {e: 2, f: {g: [{h: 3}, {i: 4}]}}}}};
t.deepEqual(getOwnPropertyNamesRecursively(o1), expected1, `getOwnPropertyNamesRecursively({a: {b: {c: 1, d: {e: 2, f: {g: [{h: 3}, {i: 4}]}}}}}) must be [${JSON.stringify(expected1)}]`);
// Shallow copy of complex array
const a2 = {a: [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]]};
const c2 = Objects.copy(a2, {deep: false});
const expected2 = ['a', 'a.b', 'a.b.c', 'a.b.d', 'a.b.d.e', 'a.b.d.f', 'a.b.d.f.g', 'a.b.d.f.g[0].h', 'a.b.d.f.g[1].i'];
const o2 = {a: {b: {c: 1, d: {e: 2, f: {g: [{h: 3}, {i: 4}]}}}}};
t.deepEqual(getOwnPropertyNamesRecursively(o2), expected2, `getOwnPropertyNamesRecursively({a: {b: {c: 1, d: {e: 2, f: {g: [{h: 3}, {i: 4}]}}}}}) must be [${JSON.stringify(expected2)}]`);
t.ok(!Array.isArray(c2), `shallow copy ${stringify(c2)} must NOT be an array`);
t.notEqual(c2, a2, `shallow copy ${stringify(c2)} must NOT be same instance`);
t.deepEqual(c2, a2, `shallow copy ${stringify(c2)} must be deep equal`);
for (let i = 0; i < a2.length; ++i) {
t.equal(c2[i], a2[i], `shallow copy [${i}] ${stringify(c2[i])} must be equal`);
t.deepEqual(c2[i], a2[i], `shallow copy [${i}] ${stringify(c2[i])} must be deep equal`);
}
// Test handling of circular reference
const o3 = {a: {b: {c: 1, d: {e: 2, f: {g: [{h: 3}, {i: 4}]}}}}};
o3.a.thisAgain = o3;
o3.thisAgain = o3;
const expected3 = ['a', 'a.b', 'a.b.c', 'a.b.d', 'a.b.d.e', 'a.b.d.f', 'a.b.d.f.g', 'a.b.d.f.g[0].h', 'a.b.d.f.g[1].i', 'a.thisAgain', 'thisAgain'];
t.deepEqual(getOwnPropertyNamesRecursively(o3), expected3, `getOwnPropertyNamesRecursively({a: {b: {c: 1, d: {e: 2, f: {g: [{h: 3}, {i: 4}]}}}}}) must be [${JSON.stringify(expected3)}]`);
// Deep copy of complex array
const a3 = {a: [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]]};
const c3 = Objects.copy(a3, {deep: true});
// Test handling of circular reference
const o4 = {a: {b: {c: 1, d: {e: 2, f: {g: [{h: 3}, {i: 4}]}}}}};
o4.a.thisAgain = o4;
o4.thisAgain = o4;
const expected4 = ['a', 'a.b', 'a.b.c', 'a.b.d', 'a.b.d.e', 'a.b.d.f', 'a.b.d.f.g', 'a.b.d.f.g[0].h', 'a.b.d.f.g[1].i', 'a.thisAgain', 'thisAgain'];
t.deepEqual(getOwnPropertyNamesRecursively(o4), expected4, `getOwnPropertyNamesRecursively({a: {b: {c: 1, d: {e: 2, f: {g: [{h: 3}, {i: 4}]}}}}}) must be [${JSON.stringify(expected4)}]`);
t.ok(!Array.isArray(c3), `deep copy ${stringify(c3)} must NOT be an array`);
t.notEqual(c3, a3, `deep copy ${stringify(c3)} must NOT be same instance`);
t.deepEqual(c3, a3, `deep copy ${stringify(c3)} must be deep equal`);
for (let n of Object.getOwnPropertyNames(a3)) {
if (a3[n] && typeof a3[n] === 'object')
t.notEqual(c3[n], a3[n], `deep copy [${n}] ${stringify(c3[n])} must NOT be same instance`);
else
t.equal(c3[n], a3[n], `deep copy [${n}] ${stringify(c3[n])} must be equal`);
t.deepEqual(c3[n], a3[n], `deep copy [${n}] ${stringify(c3[n])} must be deep equal`);
}
t.end();
});
// =====================================================================================================================
// merge arrays
// =====================================================================================================================
test('merge empty arrays', t => {
// Shallow merge of empty array
const a0 = [];
const b0 = [];
const c0 = Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(Array.isArray(c0), `shallow merge ${stringify(c0)} must be an array`);
t.notEqual(c0, a0, `shallow merge ${stringify(c0)} must not be same instance`);
t.deepEqual(c0, a0, `shallow merge ${stringify(c0)} must be deep equal`);
test(`getOwnPropertyNamesRecursively with onlyEnumerable`, t => {
const onlyE = true;
const skip = true;
// Deep merge of empty array
const a1 = [];
const b1 = [];
const c1 = Objects.merge(a1, b1, {replace: false, deep: true});
t.ok(Array.isArray(c1), `deep merge ${stringify(c1)} must be an array`);
t.notEqual(c1, a1, `deep merge ${stringify(c1)} must NOT be same instance`);
t.deepEqual(c1, a1, `deep merge ${stringify(c1)} must be deep equal`);
const o1 = {a: 1, b: 2, c: {d: 4, e: {f: {g: 7}, h: 8}}, i: {j: 10, k: 11}};
Object.defineProperty(o1, 'b', {enumerable: false});
Object.defineProperty(o1.c, 'd', {enumerable: false});
Object.defineProperty(o1.c.e, 'f', {enumerable: false});
Object.defineProperty(o1.c.e, 'h', {enumerable: false});
Object.defineProperty(o1, 'i', {enumerable: false});
Object.defineProperty(o1.i, 'k', {enumerable: false});
t.end();
});
let x1 = ['a', 'b', 'c', 'c.d', 'c.e', 'c.e.f', 'c.e.f.g', 'c.e.h', 'i', 'i.j', 'i.k'];
t.deepEqual(getOwnPropertyNamesRecursively(o1, {onlyEnumerable: !onlyE}), x1, `getOwnPropertyNamesRecursively(o1, {!onlyE, !skip}) must be ${stringify(x1)}`);
t.deepEqual(getOwnPropertyNamesRecursively(o1, {onlyEnumerable: !onlyE}), x1, `getOwnPropertyNamesRecursively(o1, {!onlyE, skip}) must be ${stringify(x1)}`);
x1 = ['a', 'c', 'c.e'];
t.deepEqual(getOwnPropertyNamesRecursively(o1, {onlyEnumerable: onlyE}), x1, `getOwnPropertyNamesRecursively(o1, {onlyE, !skip}) must be ${stringify(x1)}`);
t.deepEqual(getOwnPropertyNamesRecursively(o1, {onlyEnumerable: onlyE}), x1, `getOwnPropertyNamesRecursively(o1, {onlyE, skip}) must be ${stringify(x1)}`);
test('merge simple arrays to empty arrays', t => {
// Shallow merge of simple array to empty
const a0 = [1, 2, 3];
const b0 = [];
const c0 = Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(Array.isArray(c0), `shallow merge ${stringify(c0)} must be an array`);
t.notEqual(c0, a0, `shallow merge ${stringify(c0)} must not be same instance`);
t.deepEqual(c0, a0, `shallow merge ${stringify(c0)} must be deep equal`);
const o2 = {a: 1, b: 2, c: {d: 4, e: {f: {g: 7}, h: 8}}, i: {j: 10, k: 11}};
Object.defineProperty(o2, 'a', {enumerable: false});
Object.defineProperty(o2, 'c', {enumerable: false});
Object.defineProperty(o2.c, 'e', {enumerable: false});
Object.defineProperty(o2.c.e.f, 'g', {enumerable: false});
Object.defineProperty(o2.i, 'j', {enumerable: false});
// Deep merge of empty array
const a1 = [1, 2, 3];
const b1 = [];
const c1 = Objects.merge(a1, b1, {replace: false, deep: true});
t.ok(Array.isArray(c1), `deep merge ${stringify(c1)} must be an array`);
t.notEqual(c1, a1, `deep merge ${stringify(c1)} must NOT be same instance`);
t.deepEqual(c1, a1, `deep merge ${stringify(c1)} must be deep equal`);
let x2 = ['a', 'b', 'c', 'c.d', 'c.e', 'c.e.f', 'c.e.f.g', 'c.e.h', 'i', 'i.j', 'i.k'];
t.deepEqual(getOwnPropertyNamesRecursively(o2, {onlyEnumerable: !onlyE}), x2, `getOwnPropertyNamesRecursively(o2, {!onlyE, !skip}) must be ${stringify(x2)}`);
x2 = ['b', 'i', 'i.k'];
t.deepEqual(getOwnPropertyNamesRecursively(o2, {onlyEnumerable: onlyE}), x2, `getOwnPropertyNamesRecursively(o2, {onlyE, !skip}) must be ${stringify(x2)}`);

@@ -457,459 +168,17 @@ t.end();

test('merge empty arrays to simple arrays', t => {
// Shallow merge of simple array to empty
const a0 = [];
const b0 = [1, 2, 3];
const x0 = [1, 2, 3];
Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(Array.isArray(b0), `shallow merge ${stringify(b0)} must be an array`);
t.notEqual(b0, a0, `shallow merge ${stringify(b0)} must not be same instance`);
t.deepEqual(b0, x0, `shallow merge ${stringify(b0)} must be deep equal`);
test('getPropertyValueByCompoundName', t => {
t.equal(Objects.getPropertyValueByCompoundName(undefined, 'a.b.c'), undefined, `undefined.a.b.c must be undefined`);
t.equal(Objects.getPropertyValueByCompoundName(null, 'a.b.c'), undefined, `null.a.b.c must be undefined`);
t.equal(Objects.getPropertyValueByCompoundName({}, 'a.b.c'), undefined, `{}.a.b.c must be undefined`);
t.equal(Objects.getPropertyValueByCompoundName([], 'a.b.c'), undefined, `[].a.b.c must be undefined`);
// Deep merge of empty array
const a1 = [];
const b1 = ["1", 2, "3"];
const x1 = ["1", 2, "3"];
const c1 = Objects.merge(a1, b1, {replace: false, deep: true});
t.ok(Array.isArray(c1), `deep merge ${stringify(c1)} must be an array`);
t.notEqual(c1, a1, `deep merge ${stringify(c1)} must NOT be same instance`);
t.deepEqual(c1, x1, `deep merge ${stringify(c1)} must be deep equal`);
t.end();
});
test('merge simple arrays to shorter arrays', t => {
// Shallow merge without replace
const a0 = ["1", 2, "3", 4.0];
const b0 = [9, "8"];
const x0 = [9, "8", "3", 4.0];
Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(Array.isArray(b0), `shallow merge ${stringify(b0)} must be an array`);
t.notEqual(b0, a0, `shallow merge ${stringify(b0)} must not be same instance`);
t.deepEqual(b0, x0, `shallow merge ${stringify(b0)} must be deep equal`);
// Shallow merge with replace
const a1 = ["1", 2, "3", 4.0];
const b1 = [9, "8"];
const x1 = ["1", 2, "3", 4.0];
Objects.merge(a1, b1, {replace: true, deep: false});
t.ok(Array.isArray(b1), `deep merge ${stringify(b1)} must be an array`);
t.notEqual(b1, a1, `deep merge ${stringify(b1)} must NOT be same instance`);
t.deepEqual(b1, x1, `deep merge ${stringify(b1)} must be deep equal`);
// Deep merge without replace
const a2 = [1, 2, 3, 4.0];
const b2 = ["9", 8];
const x2 = ["9", 8, 3, 4.0];
Objects.merge(a2, b2, {replace: false, deep: true});
t.ok(Array.isArray(b2), `deep merge ${stringify(b2)} must be an array`);
t.notEqual(b2, a2, `deep merge ${stringify(b2)} must not be same instance`);
t.deepEqual(b2, x2, `deep merge ${stringify(b2)} must be deep equal`);
// Deep merge with replace
const a4 = ["1", 2, "3", 4.0];
const b4 = [9, 8];
const x4 = ["1", 2, "3", 4.0];
Objects.merge(a4, b4, {replace: true, deep: true});
t.ok(Array.isArray(b4), `deep merge ${stringify(b4)} must be an array`);
t.notEqual(b4, a4, `deep merge ${stringify(b4)} must NOT be same instance`);
t.deepEqual(b4, x4, `deep merge ${stringify(b4)} must be deep equal`);
t.end();
});
test('merge simple arrays to longer arrays', t => {
// Shallow merge without replace
const a0 = [9, "8"];
const b0 = ["1", 2, "3", 4.0, [5]];
const x0 = ["1", 2, "3", 4.0, [5]];
Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(Array.isArray(b0), `shallow merge ${stringify(b0)} must be an array`);
t.notEqual(b0, a0, `shallow merge ${stringify(b0)} must not be same instance`);
t.deepEqual(b0, x0, `shallow merge ${stringify(b0)} must be deep equal`);
// Shallow merge with replace
const a1 = [9, "8"];
const b1 = ["1", 2, "3", 4.0, [5]];
const x1 = [9, "8", "3", 4.0, [5]];
Objects.merge(a1, b1, {replace: true, deep: false});
t.ok(Array.isArray(b1), `deep merge ${stringify(b1)} must be an array`);
t.notEqual(b1, a1, `deep merge ${stringify(b1)} must NOT be same instance`);
t.deepEqual(b1, x1, `deep merge ${stringify(b1)} must be deep equal`);
// Deep merge without replace
const a2 = ["9", 8];
const b2 = [1, 2, 3, 4.0, [5]];
const x2 = [1, 2, 3, 4.0, [5]];
Objects.merge(a2, b2, {replace: false, deep: true});
t.ok(Array.isArray(b2), `deep merge ${stringify(b2)} must be an array`);
t.notEqual(b2, a2, `deep merge ${stringify(b2)} must not be same instance`);
t.deepEqual(b2, x2, `deep merge ${stringify(b2)} must be deep equal`);
// Deep merge with replace
const a4 = [9, 8];
const b4 = ["1", 2, "3", 4.0, [5]];
const x4 = [9, 8, "3", 4.0, [5]];
Objects.merge(a4, b4, {replace: true, deep: true});
t.ok(Array.isArray(b4), `deep merge ${stringify(b4)} must be an array`);
t.notEqual(b4, a4, `deep merge ${stringify(b4)} must NOT be same instance`);
t.deepEqual(b4, x4, `deep merge ${stringify(b4)} must be deep equal`);
t.end();
});
test('merge complex arrays', t => {
// Shallow merge without replace
const a1 = [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]];
const b1 = [2, 3, "4", null, undefined, {b: 2}, [9, 8, "7", undefined, null, {d: 3, e: [8, 9.0]}]];
const x1 = [2, 3, "4", null, undefined, b1[5], b1[6]];
Objects.merge(a1, b1, {replace: false, deep: false});
t.ok(Array.isArray(b1), `shallow merge ${stringify(b1)} must be an array`);
t.equal(b1.length, a1.length, `shallow merge ${stringify(b1)} lengths must be same`);
t.notEqual(b1, a1, `shallow merge ${stringify(b1)} must NOT be same instance`);
t.deepEqual(b1, x1, `shallow merge ${stringify(b1)} must be deep equal`);
t.equal(b1[5], x1[5], `shallow merge ${stringify(b1[5])} must be same instance`);
t.deepEqual(b1[5], x1[5], `shallow merge ${stringify(b1[5])} must be deep equal`);
// Shallow merge with replace
const a2 = [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]];
const b2 = [2, 3, "4", null, undefined, {b: 2}, [9, 8, "7", undefined, null, {d: 3, e: [8, 9.0]}]];
const x2 = [1, 2, "3", undefined, null, a2[5], a2[6]];
Objects.merge(a2, b2, {replace: true, deep: false});
t.ok(Array.isArray(b2), `shallow merge ${stringify(b2)} must be an array`);
t.equal(b2.length, a2.length, `shallow merge ${stringify(b2)} lengths must be same`);
t.notEqual(b2, a2, `shallow merge ${stringify(b2)} must NOT be same instance`);
t.deepEqual(b2, a2, `shallow merge ${stringify(b2)} must be deep equal a2`);
t.deepEqual(b2, x2, `shallow merge ${stringify(b2)} must be deep equal`);
t.notEqual(b2[5], a2[5], `shallow merge ${stringify(b2[5])} must NOT be same instance`);
t.deepEqual(b2[5], a2[5], `shallow merge ${stringify(b2[5])} must be deep equal`);
t.notEqual(b2[6], a2[6], `shallow merge ${stringify(b2[6])} must NOT be same instance`);
t.deepEqual(b2[6], a2[6], `shallow merge ${stringify(b2[6])} must be deep equal`);
t.notEqual(b2[6][5], a2[6][5], `shallow merge ${stringify(b2[6][5])} must NOT be same instance`);
t.deepEqual(b2[6][5], a2[6][5], `shallow merge ${stringify(b2[6][5])} must be deep equal`);
// Deep merge without replace
const a3 = [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7], e: [8, 9]}]];
const b3 = [9, 8, "7", null, undefined, {b: 2}, [1, 2, "3", undefined, null, {d: 3, c: [6, 5], e: [4]}]];
const x3 = [9, 8, "7", null, undefined, {b: 2, a: 1}, [1, 2, "3", undefined, null, {
d: 3,
c: [6, 5],
b: 2,
e: [4, 9]
}]];
Objects.merge(a3, b3, {replace: false, deep: true});
t.ok(Array.isArray(b3), `deep merge ${stringify(b3)} must be an array`);
t.equal(b3.length, a3.length, `deep merge ${stringify(b3)} lengths must be same`);
t.notEqual(b3, a3, `deep merge ${stringify(b3)} must NOT be same instance`);
t.deepEqual(b3, x3, `deep merge ${stringify(b3)} must be deep equal`);
// Deep merge with replace
const a4 = [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7], e: [8, 9]}]];
const b4 = [9, 8, "7", null, undefined, {b: 2}, [1, 2, "3", undefined, null, {d: 3, c: [6, 5], e: [4]}]];
const x4 = [1, 2, "3", undefined, null, {b: 2, a: 1}, [4, 5, "6", null, undefined, {
d: 3,
c: [7, 5],
e: [8, 9],
b: 2
}]];
Objects.merge(a4, b4, {replace: true, deep: true});
t.ok(Array.isArray(b4), `deep merge ${stringify(b4)} must be an array`);
t.equal(b4.length, a4.length, `deep merge ${stringify(b4)} lengths must be same`);
t.notEqual(b4, a4, `deep merge ${stringify(b4)} must NOT be same instance`);
t.deepEqual(b4, x4, `deep merge ${stringify(b4)} must be deep equal`);
t.end();
});
// =====================================================================================================================
// merge objects with arrays
// =====================================================================================================================
test('merge objects with arrays', t => {
// Shallow merge without replace
const a0 = {a: [3], b: {c: [4, 5]}};
const b0 = {a: [1, 2], b: {c: [6]}};
const x0 = {a: [1, 2], b: {c: [6]}};
Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(!Array.isArray(b0), `shallow merge ${stringify(b0)} must NOT be an array`);
t.ok(Array.isArray(b0.a), `shallow merge ${stringify(b0.a)} must be an array`);
t.notEqual(b0, a0, `shallow merge ${stringify(b0)} must not be same instance`);
t.deepEqual(b0, x0, `shallow merge ${stringify(b0)} must be deep equal`);
// Shallow merge with replace
const a1 = {a: [3], b: {c: [4, 5]}};
const b1 = {a: [1, 2], b: {c: [6]}};
const x1 = {a: [3], b: {c: [4, 5]}};
Objects.merge(a1, b1, {replace: true, deep: false});
t.ok(!Array.isArray(b1), `shallow merge ${stringify(b1)} must NOT be an array`);
t.ok(Array.isArray(b1.a), `shallow merge ${stringify(b1.a)} must be an array`);
t.notEqual(b1, a1, `shallow merge ${stringify(b1)} must NOT be same instance`);
t.deepEqual(b1, x1, `shallow merge ${stringify(b1)} must be deep equal`);
// Deep merge without replace
const a2 = {a: [3], b: {c: [4, 5]}};
const b2 = {a: [1, 2], b: {c: [6]}};
const x2 = {a: [1, 2], b: {c: [6, 5]}};
Objects.merge(a2, b2, {replace: false, deep: true});
t.ok(!Array.isArray(b2), `deep merge ${stringify(b2)} must NOT be an array`);
t.ok(Array.isArray(b2.a), `deep merge ${stringify(b2.a)} must be an array`);
t.notEqual(b2, a2, `deep merge ${stringify(b2)} must NOT be same instance`);
t.deepEqual(b2, x2, `deep merge ${stringify(b2)} must be deep equal`);
// Deep merge with replace
const a3 = {a: [3], b: {c: [4, 5]}};
const b3 = {a: [1, 2], b: {c: [6]}};
const x3 = {a: [3, 2], b: {c: [4, 5]}};
Objects.merge(a3, b3, {replace: true, deep: true});
t.ok(!Array.isArray(b3), `deep merge ${stringify(b3)} must NOT be an array`);
t.ok(Array.isArray(b3.a), `deep merge ${stringify(b3.a)} must be an array`);
t.notEqual(b3, a3, `deep merge ${stringify(b3)} must NOT be same instance`);
t.deepEqual(b3, x3, `deep merge ${stringify(b3)} must be deep equal`);
t.end();
});
// =====================================================================================================================
// merge object with array
// =====================================================================================================================
test('merge objects with arrays', t => {
// Shallow merge without replace
const a0 = {a: [3], b: {c: [4, 5]}};
const b0 = ["9", "8"]; b0.a = [1, 2]; b0.b = {c: [6]};
const x0 = ["9", "8"]; x0.a = [1, 2]; x0.b = {c: [6]};
Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(Array.isArray(b0), `shallow merge ${stringify(b0)} must be an array`);
t.deepEqual(b0, x0, `shallow merge ${stringify(b0)} must be deep equal`);
t.deepEqual(b0.a, x0.a, `shallow merge a ${stringify(b0.a)} must be deep equal`);
t.deepEqual(b0.b, x0.b, `shallow merge b ${stringify(b0.b)} must be deep equal`);
// Shallow merge with replace
const a1 = {a: [3], b: {c: [4, 5]}};
const b1 = ["9", "8"]; b1.a = [1, 2]; b1.b = {c: [6]};
const x1 = ["9", "8"]; x1.a = [3]; x1.b = {c: [4, 5]};
Objects.merge(a1, b1, {replace: true, deep: false});
t.ok(Array.isArray(b1), `shallow merge ${stringify(b1)} must be an array`);
t.deepEqual(b1, x1, `shallow merge ${stringify(b1)} must be deep equal`);
t.deepEqual(b1.a, x1.a, `shallow merge a ${stringify(b1.a)} must be deep equal`);
t.deepEqual(b1.b, x1.b, `shallow merge b ${stringify(b1.b)} must be deep equal`);
// Deep merge without replace
const a2 = {a: [3], b: {c: [4,5]}};
const b2 = [9,7]; b2.a = [1,2]; b2.b = {c: [6]};
const x2 = [9,7]; x2.a = [1,2]; x2.b = {c: [6,5]};
Objects.merge(a2, b2, {replace: false, deep: true});
t.ok(Array.isArray(b2), `deep merge ${stringify(b2)} must be an array`);
t.deepEqual(b2, x2, `deep merge ${stringify(b2)} must be deep equal`);
t.deepEqual(b2.a, x2.a, `deep merge a ${stringify(b2.a)} must be deep equal`);
t.deepEqual(b2.b, x2.b, `deep merge b ${stringify(b2.b)} must be deep equal`);
// Deep merge with replace
const a3 = {a: [3], b: {c: [4,5]}};
const b3 = [9,7]; b3.a = [1,2]; b3.b = {c: [6]};
const x3 = [9,7]; x3.a = [3,2]; x3.b = {c: [4,5]};
Objects.merge(a3, b3, {replace: true, deep: true});
t.ok(Array.isArray(b3), `deep merge ${stringify(b3)} must be an array`);
t.deepEqual(b3, x3, `deep merge ${stringify(b3)} must be deep equal`);
t.deepEqual(b3.a, x3.a, `deep merge a ${stringify(b3.a)} must be deep equal`);
t.deepEqual(b3.b, x3.b, `deep merge b ${stringify(b3.b)} must be deep equal`);
t.end();
});
// =====================================================================================================================
// merge array with object
// =====================================================================================================================
test('merge array with object', t => {
// Shallow merge without replace
const a0 = ["9", "8"]; a0.a = [3]; a0.b = {c: [4, 5]};
const b0 = {a: [1, 2], b: {c: [6]}};
const x0 = {"0": "9", "1": "8", "length": 2, a: [1, 2], b: {c: [6]}};
Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(!Array.isArray(b0), `shallow merge ${stringify(b0)} must not be an array`);
t.deepEqual(b0, x0, `shallow merge ${stringify(b0)} must be deep equal`);
t.deepEqual(b0.a, x0.a, `shallow merge a ${stringify(b0.a)} must be deep equal`);
t.deepEqual(b0.b, x0.b, `shallow merge b ${stringify(b0.b)} must be deep equal`);
// Shallow merge with replace
const a1 = ["9", "8"]; a1.a = [3]; a1.b = {c: [4, 5]};
const b1 = {a: [1, 2], b: {c: [6]}};
const x1 = {"0": "9", "1": "8", "length": 2, a: [3], b: {c: [4,5]}};
Objects.merge(a1, b1, {replace: true, deep: false});
t.ok(!Array.isArray(b1), `shallow merge ${stringify(b1)} must not be an array`);
t.deepEqual(b1, x1, `shallow merge ${stringify(b1)} must be deep equal`);
t.deepEqual(b1.a, x1.a, `shallow merge a ${stringify(b1.a)} must be deep equal`);
t.deepEqual(b1.b, x1.b, `shallow merge b ${stringify(b1.b)} must be deep equal`);
// Deep merge without replace
const a2 = ["9", "8"]; a2.a = [3]; a2.b = {c: [4, 5]};
const b2 = {a: [1, 2], b: {c: [6]}};
const x2 = {"0": "9", "1": "8", "length": 2, a: [1,2], b: {c: [6,5]}};
Objects.merge(a2, b2, {replace: false, deep: true});
t.ok(!Array.isArray(b2), `deep merge ${stringify(b2)} must not be an array`);
t.deepEqual(b2, x2, `deep merge ${stringify(b2)} must be deep equal`);
t.deepEqual(b2.a, x2.a, `deep merge a ${stringify(b2.a)} must be deep equal`);
t.deepEqual(b2.b, x2.b, `deep merge b ${stringify(b2.b)} must be deep equal`);
// Deep merge with replace
const a3 = ["9", "8"]; a3.a = [3]; a3.b = {c: [4, 5]};
const b3 = {a: [1, 2], b: {c: [6]}};
const x3 = {"0": "9", "1": "8", "length": 2, a: [3,2], b: {c: [4,5]}};
Objects.merge(a3, b3, {replace: true, deep: true});
t.ok(!Array.isArray(b3), `deep merge ${stringify(b3)} must not be an array`);
t.deepEqual(b3, x3, `deep merge ${stringify(b3)} must be deep equal`);
t.deepEqual(b3.a, x3.a, `deep merge a ${stringify(b3.a)} must be deep equal`);
t.deepEqual(b3.b, x3.b, `deep merge b ${stringify(b3.b)} must be deep equal`);
t.end();
});
test('getPropertyValue', t => {
t.equal(Objects.getPropertyValue(undefined, 'a.b.c'), undefined, `undefined.a.b.c must be undefined`);
t.equal(Objects.getPropertyValue(null, 'a.b.c'), undefined, `null.a.b.c must be undefined`);
t.equal(Objects.getPropertyValue({}, 'a.b.c'), undefined, `{}.a.b.c must be undefined`);
t.equal(Objects.getPropertyValue([], 'a.b.c'), undefined, `[].a.b.c must be undefined`);
const o = {a: 1, b: {c: 'c', d: {e: 'e'}}};
t.deepEqual(Objects.getPropertyValue(o, 'a'), 1, 'o.a must be 1');
t.deepEqual(Objects.getPropertyValue(o, 'b'), {c: 'c', d: {e: 'e'}}, `o.b must be {c: 'c', d: {e: 'e'}}`);
t.deepEqual(Objects.getPropertyValue(o, 'b.c'), 'c', `o.b.c must be 'c'`);
t.deepEqual(Objects.getPropertyValue(o, 'b.d'), {e: 'e'}, `o.b.d must be {e: 'e'}`);
t.deepEqual(Objects.getPropertyValue(o, 'b.d.e'), 'e', `o.b.d.e must be 'e'`);
t.deepEqual(Objects.getPropertyValue(o, 'x.y.z'), undefined, `o.x.y.z must be undefined`);
t.deepEqual(Objects.getPropertyValueByCompoundName(o, 'a'), 1, 'o.a must be 1');
t.deepEqual(Objects.getPropertyValueByCompoundName(o, 'b'), {c: 'c', d: {e: 'e'}}, `o.b must be {c: 'c', d: {e: 'e'}}`);
t.deepEqual(Objects.getPropertyValueByCompoundName(o, 'b.c'), 'c', `o.b.c must be 'c'`);
t.deepEqual(Objects.getPropertyValueByCompoundName(o, 'b.d'), {e: 'e'}, `o.b.d must be {e: 'e'}`);
t.deepEqual(Objects.getPropertyValueByCompoundName(o, 'b.d.e'), 'e', `o.b.d.e must be 'e'`);
t.deepEqual(Objects.getPropertyValueByCompoundName(o, 'x.y.z'), undefined, `o.x.y.z must be undefined`);
t.end();
});
test('copyNamedProperties - compact', t => {
const compact = true;
const deep = true;
const omit = true;
t.deepEqual(Objects.copyNamedProperties(undefined, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}), undefined, `(undefined, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}) must be undefined`);
t.deepEqual(Objects.copyNamedProperties(null, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}), null, `(null, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}) must be null`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}), {}, `({}, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}) must be {}`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {'a.b.c': undefined}, `({}, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {'a.b.c': undefined}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}), {}, `([] ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}) must be {}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {'a.b.c': undefined}, `([], ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {'a.b.c': undefined}`);
const o = {a: 1, b: {c: 'c', d: {e: 'e'}}};
t.deepEqual(Objects.copyNamedProperties(o, ['a'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {a: 1}, `(o, [a], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {a: 1}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {b: {c: 'c', d: {e: 'e'}}}, `(o, [b], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {b: {c: 'c', d: {e: 'e'}}}`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], {compact: compact, deep: deep, omitIfUndefined: !omit}).b, o.b, `(o, [b], {compact: compact, deep: deep, omitIfUndefined: !omit}).b must NOT be o.b`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], {deep: !deep, omitIfUndefined: !omit}).b, o.b, `(o, [b], {deep: !deep, omitIfUndefined: !omit}).b must NOT be o.b`);
t.equal(Objects.copyNamedProperties(o, ['b'], {compact: compact, deep: deep, omitIfUndefined: !omit}).b.c, o.b.c, `(o, [b], {compact: compact, deep: deep, omitIfUndefined: !omit}).b.c must equal o.b.c`);
t.equal(Objects.copyNamedProperties(o, ['b'], {deep: !deep, omitIfUndefined: !omit}).b.c, o.b.c, `(o, [b], {deep: !deep, omitIfUndefined: !omit}).b.c must equal o.b.c`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], {compact: compact, deep: deep, omitIfUndefined: !omit}).b.d, o.b.d, `(o, [b], {compact: compact, deep: deep, omitIfUndefined: !omit}).b.d must NOT be o.b.d`);
t.equal(Objects.copyNamedProperties(o, ['b'], {deep: !deep, omitIfUndefined: !omit}).b.d, o.b.d, `(o, [b], {deep: !deep, omitIfUndefined: !omit}).b.d must be o.b.d`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.c'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {'b.c': 'c'}, `(o, [b.c], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {'b.c': 'c'}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {'b.d': {e: 'e'}}, `(o, [b.d], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {'b.d': {e: 'e'}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d.e'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {'b.d.e': 'e'}, `(o, [b.d.e], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {'b.d.e': 'e'}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {'x.y.z': undefined}, `(o, [x.y.z], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {'x.y.z': undefined}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], {compact: compact, deep: deep, omitIfUndefined: omit}), {}, `(o, [x.y.z], {compact: compact, deep: deep, omitIfUndefined: omit}) must be {}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b'], {compact: compact, deep: deep, omitIfUndefined: !omit}), o, `(o, [a,b], {compact: compact, deep: deep, omitIfUndefined: !omit}) must equal o`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {a: 1, 'b.c': 'c', 'b.d': {e: 'e'}}, `(o, [a,b], {compact: compact, deep: deep, omitIfUndefined: !omit}) must equal {a: 1, 'b.c': 'c', 'b.d': {e: 'e'}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d.e'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {a: 1, 'b.c': 'c', 'b.d.e': 'e'}, `(o, [a,b], {compact: compact, deep: deep, omitIfUndefined: !omit}) must equal {a: 1, 'b.c': 'c', 'b.d.e': 'e'}`);
t.end();
});
test('copyNamedProperties - non-compact', t => {
const compact = true;
const deep = true;
const omit = true;
t.deepEqual(Objects.copyNamedProperties(undefined, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}), undefined, `(undefined, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}) must be undefined`);
t.deepEqual(Objects.copyNamedProperties(null, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}), null, `(null, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}) must be null`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}), {}, `({}, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}) must be {}`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {a: {b: {c: undefined}}}, `({}, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {a: {b: {c: undefined}}}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}), {}, `([] ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}) must be {}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {a: {b: {c: undefined}}}, `([], ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {a: {b: {c: undefined}}}`);
const o = {a: 1, b: {c: 'c', d: {e: 'e'}}};
t.deepEqual(Objects.copyNamedProperties(o, ['a'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {a: 1}, `(o, [a], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {a: 1}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {b: {c: 'c', d: {e: 'e'}}}, `(o, [b], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {b: {c: 'c', d: {e: 'e'}}}`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], {compact: !compact, deep: deep, omitIfUndefined: !omit}).b, o.b, `(o, [b], {compact: !compact, deep: deep, omitIfUndefined: !omit}).b must NOT be o.b`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], {deep: !deep, omitIfUndefined: !omit}).b, o.b, `(o, [b], {deep: !deep, omitIfUndefined: !omit}).b must NOT be o.b`);
t.equal(Objects.copyNamedProperties(o, ['b'], {compact: !compact, deep: deep, omitIfUndefined: !omit}).b.c, o.b.c, `(o, [b], {compact: !compact, deep: deep, omitIfUndefined: !omit}).b.c must equal o.b.c`);
t.equal(Objects.copyNamedProperties(o, ['b'], {deep: !deep, omitIfUndefined: !omit}).b.c, o.b.c, `(o, [b], {deep: !deep, omitIfUndefined: !omit}).b.c must equal o.b.c`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], {compact: !compact, deep: deep, omitIfUndefined: !omit}).b.d, o.b.d, `(o, [b], {compact: !compact, deep: deep, omitIfUndefined: !omit}).b.d must NOT be o.b.d`);
t.equal(Objects.copyNamedProperties(o, ['b'], {deep: !deep, omitIfUndefined: !omit}).b.d, o.b.d, `(o, [b], {deep: !deep, omitIfUndefined: !omit}).b.d must be o.b.d`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.c'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {b: {c: 'c'}}, `(o, [b.c], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {b: {c: 'c'}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {b: {d: {e: 'e'}}}, `(o, [b.d], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {b: {d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d.e'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {b: {d: {e: 'e'}}}, `(o, [b.d.e], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {b: {d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {x: {y: {z: undefined}}}, `(o, [x.y.z], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {x: {y: {z: undefined}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], {compact: !compact, deep: deep, omitIfUndefined: omit}), {}, `(o, [x.y.z], {compact: !compact, deep: deep, omitIfUndefined: omit}) must be {}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), o, `(o, [a,b], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must equal o`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {a: 1, b: {c: 'c', d: {e: 'e'}}}, `(o, [a,b], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must equal {a: 1, b: {c: 'c', d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d.e'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {a: 1, b: {c: 'c', d: {e: 'e'}}}, `(o, [a,b], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must equal {a: 1, b: {c: 'c', d: {e: 'e'}}}`);
t.end();
});
test('toKeyValuePairs', t => {
// Applied to non-objects
let expected = [];
t.deepEqual(toKeyValuePairs(undefined), expected, `toKeyValuePairs(undefined) must be ${stringify(expected)}`);
t.deepEqual(toKeyValuePairs(null), expected, `toKeyValuePairs(null) must be ${stringify(expected)}`);
t.deepEqual(toKeyValuePairs('abc'), expected, `toKeyValuePairs('abc') must be ${stringify(expected)}`);
t.deepEqual(toKeyValuePairs(123), expected, `toKeyValuePairs(123) must be ${stringify(expected)}`);
// Applied to objects
expected = [];
t.deepEqual(toKeyValuePairs({}), expected, `toKeyValuePairs({}) must be ${stringify(expected)}`);
expected = [['a', 1], ['b', {c:2}], ['d', '3']];
t.deepEqual(toKeyValuePairs({a:1, b:{c:2}, d:'3'}), expected, `toKeyValuePairs({a:1, b:{c:2}, d:'3'}) must be ${stringify(expected)}`);
expected = [['d', '3'], ['b', {c:2}], ['a', 1]];
t.deepEqual(toKeyValuePairs({d:'3', b:{c:2}, a:1}), expected, `toKeyValuePairs({d:'3', b:{c:2}, a:1}) must be ${stringify(expected)}`);
// Not meant to be applied to arrays, but if so ...
expected = [['length', 0]];
t.deepEqual(toKeyValuePairs([]), expected, `toKeyValuePairs([]) must be ${stringify(expected)}`);
expected = [['0', '1'], ['1', 2], ['2', '3'], ['length', 3]];
t.deepEqual(toKeyValuePairs(['1', 2, '3']), expected, `toKeyValuePairs(['1', 2, '3']) must be ${stringify(expected)}`);
expected = [['0', '3'], ['1', 2], ['2', '1'], ['length', 3]];
t.deepEqual(toKeyValuePairs(['3',2,'1']), expected, `toKeyValuePairs(['3',2,'1']) must be ${stringify(expected)}`);
t.end();
});
{
"name": "core-functions-tests",
"version": "3.0.1",
"version": "3.0.2",
"author": "Byron du Preez",

@@ -5,0 +5,0 @@ "license": "Apache-2.0",

@@ -442,3 +442,3 @@ 'use strict';

_attempts: 1,
_lastExecutedAt: "2016-12-01T05:09:09.119Z",
_began: "2016-12-01T05:09:09.119Z",
_result: undefined,

@@ -454,3 +454,3 @@ _error: undefined,

attempts: this._attempts,
lastExecutedAt: this._lastExecutedAt,
began: this._began,
subTasks: this._subTasks

@@ -461,9 +461,9 @@ };

// default behaviour must use toJSON method
checkNoOpts(task, wrapInString ? '[object Object]' : '{"name":"Task1","executable":true,"state":{"code":"Unstarted","completed":false,"rejected":false},"attempts":1,"lastExecutedAt":"2016-12-01T05:09:09.119Z","subTasks":[]}');
checkNoOpts(task, wrapInString ? '[object Object]' : '{"name":"Task1","executable":true,"state":{"code":"Unstarted","completed":false,"rejected":false},"attempts":1,"began":"2016-12-01T05:09:09.119Z","subTasks":[]}');
// explicit !avoidToJSONMethods must use toJSON method
checkWithOpts(task, {avoidToJSONMethods: false}, wrapInString ? '[object Object]' : '{"name":"Task1","executable":true,"state":{"code":"Unstarted","completed":false,"rejected":false},"attempts":1,"lastExecutedAt":"2016-12-01T05:09:09.119Z","subTasks":[]}');
checkWithOpts(task, {avoidToJSONMethods: false}, wrapInString ? '[object Object]' : '{"name":"Task1","executable":true,"state":{"code":"Unstarted","completed":false,"rejected":false},"attempts":1,"began":"2016-12-01T05:09:09.119Z","subTasks":[]}');
// explicit avoidToJSONMethods must NOT use toJSON method
checkWithOpts(task, {avoidToJSONMethods: true}, wrapInString ? '[object Object]' : '{"name":"Task1","definition":{"name":"Task1","executable":true,"execute":[Function: anonymous],"subTaskDefs":[],"parent":undefined},"executable":true,"execute":[Function: anonymous],"_subTasks":[],"_subTasksByName":{},"parent":undefined,"_state":{"code":"Unstarted","completed":false,"error":undefined,"rejected":false,"reason":undefined},"_attempts":1,"_lastExecutedAt":"2016-12-01T05:09:09.119Z","_result":undefined,"_error":undefined,"_slaveTasks":[],"_frozen":true,"toJSON":[Function: toJSON]}');
checkWithOpts(task, {avoidToJSONMethods: true}, wrapInString ? '[object Object]' : '{"name":"Task1","definition":{"name":"Task1","executable":true,"execute":[Function: anonymous],"subTaskDefs":[],"parent":undefined},"executable":true,"execute":[Function: anonymous],"_subTasks":[],"_subTasksByName":{},"parent":undefined,"_state":{"code":"Unstarted","completed":false,"error":undefined,"rejected":false,"reason":undefined},"_attempts":1,"_began":"2016-12-01T05:09:09.119Z","_result":undefined,"_error":undefined,"_slaveTasks":[],"_frozen":true,"toJSON":[Function: toJSON]}');
}

@@ -470,0 +470,0 @@

@@ -133,2 +133,157 @@ 'use strict';

}
/**
* Simplifies the given list of Success and/or Failure outcomes to a list of values if and ONLY if every outcome is a
* Success; otherwise simply returns the given list.
* @param {Array.<*>|Outcomes} outcomes - a list of Success and/or Failure outcomes to potentially simplify
* @returns {Array.<*>|Outcomes} a simplified list of Success values (if all were Success outcomes) or the given list
*/
static simplify(outcomes) {
return outcomes.every(o => o instanceof Success) ? outcomes.map(o => o.value) : outcomes;
}
/**
* Returns a count of the number of outcomes that match the given predicate in the given list of outcomes.
* @param {Outcomes} outcomes - a list of outcomes
* @param {function(outcome: Outcome): boolean} predicate - the predicate to use to determine whether an outcome should be counted or not
* @returns {number} the number of outcomes that match the given predicate
*/
static count(outcomes, predicate) {
return outcomes.reduce((acc, o) => acc + (predicate(o) ? 1 : 0), 0);
}
/**
* Returns a count of the number of Success outcomes in the given list of outcomes.
* @param {Outcomes} outcomes - a list of outcomes
* @returns {number} the number of Success outcomes
*/
static countSuccess(outcomes) {
return outcomes.reduce((acc, o) => acc + (o instanceof Success ? 1 : 0), 0);
}
/**
* Returns a count of the number of Failure outcomes in the given list of outcomes.
* @param {Outcomes} outcomes - a list of outcomes
* @returns {number} the number of Failure outcomes
*/
static countFailure(outcomes) {
return outcomes.reduce((acc, o) => acc + (o instanceof Failure ? 1 : 0), 0);
}
/**
* Returns a description of the number of successes followed by the number of failures in the given list of outcomes.
* @param {Outcomes} outcomes - a list of outcomes
* @returns {string} the number of Failure outcomes
*/
static describeSuccessAndFailureCounts(outcomes) {
const successCount = Try.countSuccess(outcomes);
const failureCount = Try.countFailure(outcomes);
return `${successCount} success${successCount !== 1 ? 'es' : ''} & ${failureCount} failure${failureCount !== 1 ? 's' : ''}`;
}
/**
* Recursively flattens the given value, which is expected to be typically zero or more Try values containing zero or
* more Try values (containing ...), by unpacking/flattening Array and Try values up to the specified depth. If depth
* is deep enough to flatten all Array & Try values, then returns either a single successful value (if the given value
* is effectively a single value) or a single flattened array of successful values or throws the error of the first
* Failure found (if any and if opts.keepFailures is false).
* @param {*|*[]|Outcome|Outcomes} value - the value to be flattened
* @param {number|undefined} [depth] - the optional maximum depth to which to flatten recursively (defaults to MAX_SAFE_INTEGER if undefined)
* @param {Object|undefined} [opts] - optional options to use to alter the behaviour
* @param {boolean|undefined} [opts.keepFailures] - if true, collects and preserves any Failure outcomes as is;
* otherwise flattens Failures too and throws the error of the first Failure found (defaults to false)
* @returns {*|*[]} a single successful value or an array of zero or more successful values or throws an error
* @throws {Error} the error of the first Failure found (if any and opts.keepFailures is false)
*/
static flatten(value, depth, opts) {
const maxDepth = depth === undefined ? Number.MAX_SAFE_INTEGER : depth;
const TryType = opts && opts.keepFailures ? Success : Try;
const history = new WeakMap();
function collect(value, depth) {
const isArray = Array.isArray(value);
const v = isArray ? new Array(value.length) : value;
// Avoid circular references
if (value && typeof value === 'object') {
if (history.has(value))
return history.get(value);
history.set(value, v);
}
if (value instanceof TryType) {
const vv = value.get();
// Recurse deeper if maximum depth has not been reached yet
return depth > 0 ? collect(vv, depth - 1) : vv;
}
if (isArray) {
// Recurse deeper if maximum depth has not been reached yet & if its still worthwhile to do so
const mustTraverse = depth > 0 && value.some(e => (e instanceof TryType) || Array.isArray(e));
for (let i = 0; i < value.length; ++i) {
const e = value[i];
const vv = e instanceof TryType ? e.get() : e;
v[i] = mustTraverse ? collect(vv, depth - 1) : vv;
}
return v;
}
return v;
}
return collect(value, maxDepth);
}
/**
* Recursively searches for a Failure on the given value, which is expected to be typically one or more Try values
* containing one or more Try values (containing ...).
* @param {*|*[]|Outcome|Outcomes} value - the value to be recursively searched
* @param {number|undefined} [depth] - the optional maximum depth to which to search recursively (defaults to MAX_SAFE_INTEGER if undefined)
* @returns {Failure|undefined} the first Failure found (if any); otherwise undefined
*/
static findFailure(value, depth) {
const maxDepth = depth === undefined ? Number.MAX_SAFE_INTEGER : depth;
const history = new WeakMap();
function find(value, depth) {
// Avoid circular references
if (value && typeof value === 'object') {
if (history.has(value))
return undefined;
history.set(value, value);
}
if (value instanceof Failure)
return value;
if (value instanceof Success) {
// Search deeper if maximum depth has not been reached yet
return depth > 0 ? find(value.value, depth - 1) : undefined;
}
if (Array.isArray(value)) {
// Recurse deeper if maximum depth has not been reached yet & if its still worthwhile to do so (otherwise just
// return undefined)
const f = value.find(e => e instanceof Failure);
// const f = depth > 0 ? value.find(e => e instanceof Failure) : undefined;
if (f)
return f;
// Search deeper if maximum depth has not been reached yet & if its still worthwhile to do so
if (depth > 0 && value.some(e => (e instanceof Success) || Array.isArray(e))) {
for (let e of value) {
const f = find(e instanceof Success ? e.value : e, depth - 1);
if (f)
return f;
}
}
}
// return value instanceof Failure ? value : undefined;
return undefined;
}
return find(value, maxDepth);
}
}

@@ -135,0 +290,0 @@

@@ -16,3 +16,3 @@ /**

/**
* @typedef {(Success|Failure)[]} Outcomes - represents a list of Success or Failure outcomes
* @typedef {Outcome[]} Outcomes - represents a list of Success and/or Failure outcomes
*/

@@ -52,4 +52,4 @@

/**
* @typedef {[string, *]} KeyValuePair - represents a key and value pair/tuple, which is implemented as an array
* containing a string key followed by any type of value
* @typedef {[PropertyKey, *]} KeyValuePair - represents a key and value pair/tuple, which is implemented as an array
* containing a property key (i.e. a string/number/symbol property name) followed by any type of value
*/

@@ -115,1 +115,105 @@

*/
// objects module
/**
* @typedef {Object} PropertyDescriptor - a property descriptor (e.g. return value of Object.getOwnPropertyDescriptor)
* @property {boolean} [configurable = false] - whether the property is configurable or not
* @property {boolean} [enumerable = false] - whether the property is enumerable or not
* @property {*} [value] - the optional value assigned to the property
* @property {boolean} [writable = false] - whether property is writable (true) or read-only (false)
* @property {Function} [get] - an optional getter to use to get the property's value
* @property {Function} [set] - an optional setter to use to set the property's value
*/
/**
* @typedef {Object} CopyOpts - options to use with {@link module:core-functions/objects#copy}
* @property {boolean|undefined} [deep] - Executes a deep copy if deep is true, otherwise only does a shallow copy (defaults to shallow)
* @property {boolean|undefined} [deepMapKeys] - Executes a deep copy of any Map's keys if true (AND if `deep` is true), otherwise only does a shallow copy (defaults to shallow)
* Rationale: Generally don't want to copy a Map's Object keys, since a Map uses reference equality to compare keys
* @property {boolean|undefined} [deepMapValues] - Executes a deep copy of any Map's values if true (AND if `deep` is true), otherwise only does a shallow copy (defaults to shallow)
* Rationale: Generally don't want to copy a Map's Object values, since Maps are typically used to cache values for look-up purposes
* @property {boolean|undefined} [deepSets] - Executes a deep copy of any Set's elements if true (AND if `deep` is true), otherwise only does a shallow copy (defaults to shallow)
* Rationale: Generally don't want to copy a Set's Object elements, since a Set uses reference equality to compare elements & identify duplicates
* @property {boolean|undefined} [onlyEnumerable] - whether to copy ONLY enumerable properties or ALL own properties (default is false, i.e. copy all)
* @property {boolean|undefined} [onlyValues] - whether to copy ONLY property values & ignore descriptors (if true) or copy property descriptors (if false - default)
* @property {boolean|undefined} [omitAccessors] - whether to omit any descriptors' accessors & replace them with value properties with cloned values (if true) or to copy accessors & ignore cloned values when accessors are present (if false) (default is false, i.e. copy accessors)
* @property {IsCopyable|undefined} [isCopyable] - an optional `isCopyable` function to further determine whether an object is copyable or not
* @property {CreateObject|undefined} [createCustomObject] - an optional `createCustomObject` function that can be used to customize the creation of target objects during copying
* @property {CopyContent|undefined} [copyCustomContent] - an optional `copyCustomContent` function that can be used to customize the copying of any special case content or internal state from one object to another object during copying
*/
/**
* @typedef {CopyOpts} MergeOpts - options to use with {@link module:core-functions/objects#merge}
* @property {boolean|undefined} [replace] - whether to replace properties in the `to` object with same named properties in the `from` object or not (defaults to not)
* @property {IsMergeable|undefined} [isMergeable] - an optional `isMergeable` function to be used to determine whether an object can be the target of a `merge` or not
* @property {boolean|undefined} [deep] - Executes a deep merge if true, otherwise only does a shallow merge (defaults to shallow)
* @property {boolean|undefined} [onlyEnumerable] - whether to merge ONLY enumerable properties or ALL own properties (default is false, i.e. merge all)
*/
/**
* @typedef {CopyOpts} CopyNamedPropertiesOpts - options to use with {@link module:core-functions/objects#copyNamedProperties}
* @property {boolean|undefined} [compact] - whether to create a flatter, more-compact destination object, which will use
* any compound property names as is and eliminate any unnecessary intermediate objects or rather create a more-
* conventional, less-compact destination object, which will only have simple property names and any & all intermediate objects needed
* @property {boolean|undefined} [deep] - executes a deep copy of each property value if the given deep flag is true, otherwise only does a shallow copy
* @property {boolean|undefined} [omitIfUndefined] - whether or not to omit any named property that has an undefined value from the destination object
*/
/**
* @typedef {function(o: Object): boolean} IsCopyable - an `isCopyable` function to further determine whether an object
* is copyable or not (default implementation: {@link module:core-functions/copying#isCopyableObject})
*/
/**
* @typedef {function(o: Object): boolean} IsMergeable - an `isMergeable` function to further determine whether an object
* can be the target of a `merge` or not (default implementation: {@link module:core-functions/merging#isMergeableObject})
*/
/**
* @typedef {Object} T - a type of object
*/
/**
* @typedef {function(like: T, context: CopyContext): (T|*)} CreateObject - a `createCustomObject` function that can be
* used to customize the creation of an object similar to a `like` object during copying (called by
* {@link module:core-functions/objects#createObject})
*/
/**
* @typedef {function(src: T, dest: T, context: CopyContext): boolean} CopyContent - a `copyCustomContent` function that
* can be used to customize the copying of any special case content or internal state from the `src` object to the `dest`
* object during copying (called by {@link module:core-functions/objects#copyContent}). Your custom `copyCustomContent`
* function should return true if it completely handled the copying of the content and the rest of the `copyContent`
* function's logic MUST be skipped; or false if not.
*/
/**
* @typedef {Object} CopyContext - a completely configured context to use during copying, which contains the settings, the `isCopyable`, `createCustomObject` & `copyCustomContent` functions and the `history` cache to use
* @property {boolean} deep - Executes a deep copy if deep is true, otherwise only does a shallow copy (defaults to shallow)
* @property {boolean} deepMapKeys - Executes a deep copy of any Map's keys if true (AND if `deep` is true), otherwise only does a shallow copy (defaults to shallow)
* Rationale: Generally don't want to copy a Map's Object keys, since a Map uses reference equality to compare keys
* @property {boolean} deepMapValues - Executes a deep copy of any Map's values if true (AND if `deep` is true), otherwise only does a shallow copy (defaults to shallow)
* Rationale: Generally don't want to copy a Map's Object values, since Maps are typically used to cache values for look-up purposes
* @property {boolean} deepSets - Executes a deep copy of any Set's elements if true (AND if `deep` is true), otherwise only does a shallow copy (defaults to shallow)
* Rationale: Generally don't want to copy a Set's Object elements, since a Set uses reference equality to compare elements & identify duplicates
* @property {boolean} onlyEnumerable - whether to copy ONLY enumerable properties or ALL own properties (default is false, i.e. copy all)
* @property {boolean} onlyValues - whether to copy ONLY property values & ignore descriptors (if true) or copy property descriptors (if false - default)
* @property {boolean} omitAccessors - whether to omit any descriptors' accessors & replace them with value properties with cloned values (if true) or to copy accessors & ignore cloned values when accessors are present (if false) (default is false, i.e. copy accessors)
* @property {IsCopyable} isCopyable - an `isCopyable` function to be used to determine whether an object is copyable or not (defaults to `isCopyableObject`)
* @property {CreateObject|undefined} [createCustomObject] - an optional `createCustomObject` function that can be used to customize the creation of target objects during copying
* @property {CopyContent|undefined} [copyCustomContent] - an optional `copyCustomContent` function that can be used to customize the copying of any special case content or internal state from one object to another object during copying
* @property {WeakMap} history - a history cache of objects copied so far
*/
/**
* @typedef {CopyContext} MergeContext - a completely configured context to use during merging, which contains the settings, the `isCopyable`, `isMergeable`, `createCustomObject` & `copyCustomContent` functions and the `history` cache to use
* @property {boolean} replace - whether to replace properties in the `to` object with same named properties in the `from` object or not
* @property {IsMergeable} isMergeable - an `isMergeable` function to be used to determine whether an object can be the target of a `merge` or not (defaults to `isMergeableObject`)
* @property {CopyContext} deepCopyContext - a clone of this context to use for any deep copying needed during merge
* @property {CopyContext} shallowCopyContext - a clone of this context to use for any shallow copying needed during merge
*/
/**
*@typedef {Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} TypedArray - A TypedArray subclass instance
*/

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc