| // Currently in sync with Node.js lib/internal/util/comparisons.js |
| // https://github.com/nodejs/node/commit/112cc7c27551254aa2b17098fb774867f05ed0d9 |
| 'use strict'; |
| |
| function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } |
| |
| function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } |
| |
| function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } |
| |
| function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } |
| |
| function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } |
| |
| var regexFlagsSupported = /a/g.flags !== undefined; |
| |
| var arrayFromSet = function arrayFromSet(set) { |
| var array = []; |
| set.forEach(function (value) { |
| return array.push(value); |
| }); |
| return array; |
| }; |
| |
| var arrayFromMap = function arrayFromMap(map) { |
| var array = []; |
| map.forEach(function (value, key) { |
| return array.push([key, value]); |
| }); |
| return array; |
| }; |
| |
| var objectIs = Object.is ? Object.is : require('object-is'); |
| var objectGetOwnPropertySymbols = Object.getOwnPropertySymbols ? Object.getOwnPropertySymbols : function () { |
| return []; |
| }; |
| var numberIsNaN = Number.isNaN ? Number.isNaN : require('is-nan'); |
| |
| function uncurryThis(f) { |
| return f.call.bind(f); |
| } |
| |
| var hasOwnProperty = uncurryThis(Object.prototype.hasOwnProperty); |
| var propertyIsEnumerable = uncurryThis(Object.prototype.propertyIsEnumerable); |
| var objectToString = uncurryThis(Object.prototype.toString); |
| |
| var _require$types = require('util/').types, |
| isAnyArrayBuffer = _require$types.isAnyArrayBuffer, |
| isArrayBufferView = _require$types.isArrayBufferView, |
| isDate = _require$types.isDate, |
| isMap = _require$types.isMap, |
| isRegExp = _require$types.isRegExp, |
| isSet = _require$types.isSet, |
| isNativeError = _require$types.isNativeError, |
| isBoxedPrimitive = _require$types.isBoxedPrimitive, |
| isNumberObject = _require$types.isNumberObject, |
| isStringObject = _require$types.isStringObject, |
| isBooleanObject = _require$types.isBooleanObject, |
| isBigIntObject = _require$types.isBigIntObject, |
| isSymbolObject = _require$types.isSymbolObject, |
| isFloat32Array = _require$types.isFloat32Array, |
| isFloat64Array = _require$types.isFloat64Array; |
| |
| function isNonIndex(key) { |
| if (key.length === 0 || key.length > 10) return true; |
| |
| for (var i = 0; i < key.length; i++) { |
| var code = key.charCodeAt(i); |
| if (code < 48 || code > 57) return true; |
| } // The maximum size for an array is 2 ** 32 -1. |
| |
| |
| return key.length === 10 && key >= Math.pow(2, 32); |
| } |
| |
| function getOwnNonIndexProperties(value) { |
| return Object.keys(value).filter(isNonIndex).concat(objectGetOwnPropertySymbols(value).filter(Object.prototype.propertyIsEnumerable.bind(value))); |
| } // Taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js |
| // original notice: |
| |
| /*! |
| * The buffer module from node.js, for the browser. |
| * |
| * @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org> |
| * @license MIT |
| */ |
| |
| |
| function compare(a, b) { |
| if (a === b) { |
| return 0; |
| } |
| |
| var x = a.length; |
| var y = b.length; |
| |
| for (var i = 0, len = Math.min(x, y); i < len; ++i) { |
| if (a[i] !== b[i]) { |
| x = a[i]; |
| y = b[i]; |
| break; |
| } |
| } |
| |
| if (x < y) { |
| return -1; |
| } |
| |
| if (y < x) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| var ONLY_ENUMERABLE = undefined; |
| var kStrict = true; |
| var kLoose = false; |
| var kNoIterator = 0; |
| var kIsArray = 1; |
| var kIsSet = 2; |
| var kIsMap = 3; // Check if they have the same source and flags |
| |
| function areSimilarRegExps(a, b) { |
| return regexFlagsSupported ? a.source === b.source && a.flags === b.flags : RegExp.prototype.toString.call(a) === RegExp.prototype.toString.call(b); |
| } |
| |
| function areSimilarFloatArrays(a, b) { |
| if (a.byteLength !== b.byteLength) { |
| return false; |
| } |
| |
| for (var offset = 0; offset < a.byteLength; offset++) { |
| if (a[offset] !== b[offset]) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| function areSimilarTypedArrays(a, b) { |
| if (a.byteLength !== b.byteLength) { |
| return false; |
| } |
| |
| return compare(new Uint8Array(a.buffer, a.byteOffset, a.byteLength), new Uint8Array(b.buffer, b.byteOffset, b.byteLength)) === 0; |
| } |
| |
| function areEqualArrayBuffers(buf1, buf2) { |
| return buf1.byteLength === buf2.byteLength && compare(new Uint8Array(buf1), new Uint8Array(buf2)) === 0; |
| } |
| |
| function isEqualBoxedPrimitive(val1, val2) { |
| if (isNumberObject(val1)) { |
| return isNumberObject(val2) && objectIs(Number.prototype.valueOf.call(val1), Number.prototype.valueOf.call(val2)); |
| } |
| |
| if (isStringObject(val1)) { |
| return isStringObject(val2) && String.prototype.valueOf.call(val1) === String.prototype.valueOf.call(val2); |
| } |
| |
| if (isBooleanObject(val1)) { |
| return isBooleanObject(val2) && Boolean.prototype.valueOf.call(val1) === Boolean.prototype.valueOf.call(val2); |
| } |
| |
| if (isBigIntObject(val1)) { |
| return isBigIntObject(val2) && BigInt.prototype.valueOf.call(val1) === BigInt.prototype.valueOf.call(val2); |
| } |
| |
| return isSymbolObject(val2) && Symbol.prototype.valueOf.call(val1) === Symbol.prototype.valueOf.call(val2); |
| } // Notes: Type tags are historical [[Class]] properties that can be set by |
| // FunctionTemplate::SetClassName() in C++ or Symbol.toStringTag in JS |
| // and retrieved using Object.prototype.toString.call(obj) in JS |
| // See https://tc39.github.io/ecma262/#sec-object.prototype.tostring |
| // for a list of tags pre-defined in the spec. |
| // There are some unspecified tags in the wild too (e.g. typed array tags). |
| // Since tags can be altered, they only serve fast failures |
| // |
| // Typed arrays and buffers are checked by comparing the content in their |
| // underlying ArrayBuffer. This optimization requires that it's |
| // reasonable to interpret their underlying memory in the same way, |
| // which is checked by comparing their type tags. |
| // (e.g. a Uint8Array and a Uint16Array with the same memory content |
| // could still be different because they will be interpreted differently). |
| // |
| // For strict comparison, objects should have |
| // a) The same built-in type tags |
| // b) The same prototypes. |
| |
| |
| function innerDeepEqual(val1, val2, strict, memos) { |
| // All identical values are equivalent, as determined by ===. |
| if (val1 === val2) { |
| if (val1 !== 0) return true; |
| return strict ? objectIs(val1, val2) : true; |
| } // Check more closely if val1 and val2 are equal. |
| |
| |
| if (strict) { |
| if (_typeof(val1) !== 'object') { |
| return typeof val1 === 'number' && numberIsNaN(val1) && numberIsNaN(val2); |
| } |
| |
| if (_typeof(val2) !== 'object' || val1 === null || val2 === null) { |
| return false; |
| } |
| |
| if (Object.getPrototypeOf(val1) !== Object.getPrototypeOf(val2)) { |
| return false; |
| } |
| } else { |
| if (val1 === null || _typeof(val1) !== 'object') { |
| if (val2 === null || _typeof(val2) !== 'object') { |
| // eslint-disable-next-line eqeqeq |
| return val1 == val2; |
| } |
| |
| return false; |
| } |
| |
| if (val2 === null || _typeof(val2) !== 'object') { |
| return false; |
| } |
| } |
| |
| var val1Tag = objectToString(val1); |
| var val2Tag = objectToString(val2); |
| |
| if (val1Tag !== val2Tag) { |
| return false; |
| } |
| |
| if (Array.isArray(val1)) { |
| // Check for sparse arrays and general fast path |
| if (val1.length !== val2.length) { |
| return false; |
| } |
| |
| var keys1 = getOwnNonIndexProperties(val1, ONLY_ENUMERABLE); |
| var keys2 = getOwnNonIndexProperties(val2, ONLY_ENUMERABLE); |
| |
| if (keys1.length !== keys2.length) { |
| return false; |
| } |
| |
| return keyCheck(val1, val2, strict, memos, kIsArray, keys1); |
| } // [browserify] This triggers on certain types in IE (Map/Set) so we don't |
| // wan't to early return out of the rest of the checks. However we can check |
| // if the second value is one of these values and the first isn't. |
| |
| |
| if (val1Tag === '[object Object]') { |
| // return keyCheck(val1, val2, strict, memos, kNoIterator); |
| if (!isMap(val1) && isMap(val2) || !isSet(val1) && isSet(val2)) { |
| return false; |
| } |
| } |
| |
| if (isDate(val1)) { |
| if (!isDate(val2) || Date.prototype.getTime.call(val1) !== Date.prototype.getTime.call(val2)) { |
| return false; |
| } |
| } else if (isRegExp(val1)) { |
| if (!isRegExp(val2) || !areSimilarRegExps(val1, val2)) { |
| return false; |
| } |
| } else if (isNativeError(val1) || val1 instanceof Error) { |
| // Do not compare the stack as it might differ even though the error itself |
| // is otherwise identical. |
| if (val1.message !== val2.message || val1.name !== val2.name) { |
| return false; |
| } |
| } else if (isArrayBufferView(val1)) { |
| if (!strict && (isFloat32Array(val1) || isFloat64Array(val1))) { |
| if (!areSimilarFloatArrays(val1, val2)) { |
| return false; |
| } |
| } else if (!areSimilarTypedArrays(val1, val2)) { |
| return false; |
| } // Buffer.compare returns true, so val1.length === val2.length. If they both |
| // only contain numeric keys, we don't need to exam further than checking |
| // the symbols. |
| |
| |
| var _keys = getOwnNonIndexProperties(val1, ONLY_ENUMERABLE); |
| |
| var _keys2 = getOwnNonIndexProperties(val2, ONLY_ENUMERABLE); |
| |
| if (_keys.length !== _keys2.length) { |
| return false; |
| } |
| |
| return keyCheck(val1, val2, strict, memos, kNoIterator, _keys); |
| } else if (isSet(val1)) { |
| if (!isSet(val2) || val1.size !== val2.size) { |
| return false; |
| } |
| |
| return keyCheck(val1, val2, strict, memos, kIsSet); |
| } else if (isMap(val1)) { |
| if (!isMap(val2) || val1.size !== val2.size) { |
| return false; |
| } |
| |
| return keyCheck(val1, val2, strict, memos, kIsMap); |
| } else if (isAnyArrayBuffer(val1)) { |
| if (!areEqualArrayBuffers(val1, val2)) { |
| return false; |
| } |
| } else if (isBoxedPrimitive(val1) && !isEqualBoxedPrimitive(val1, val2)) { |
| return false; |
| } |
| |
| return keyCheck(val1, val2, strict, memos, kNoIterator); |
| } |
| |
| function getEnumerables(val, keys) { |
| return keys.filter(function (k) { |
| return propertyIsEnumerable(val, k); |
| }); |
| } |
| |
| function keyCheck(val1, val2, strict, memos, iterationType, aKeys) { |
| // For all remaining Object pairs, including Array, objects and Maps, |
| // equivalence is determined by having: |
| // a) The same number of owned enumerable properties |
| // b) The same set of keys/indexes (although not necessarily the same order) |
| // c) Equivalent values for every corresponding key/index |
| // d) For Sets and Maps, equal contents |
| // Note: this accounts for both named and indexed properties on Arrays. |
| if (arguments.length === 5) { |
| aKeys = Object.keys(val1); |
| var bKeys = Object.keys(val2); // The pair must have the same number of owned properties. |
| |
| if (aKeys.length !== bKeys.length) { |
| return false; |
| } |
| } // Cheap key test |
| |
| |
| var i = 0; |
| |
| for (; i < aKeys.length; i++) { |
| if (!hasOwnProperty(val2, aKeys[i])) { |
| return false; |
| } |
| } |
| |
| if (strict && arguments.length === 5) { |
| var symbolKeysA = objectGetOwnPropertySymbols(val1); |
| |
| if (symbolKeysA.length !== 0) { |
| var count = 0; |
| |
| for (i = 0; i < symbolKeysA.length; i++) { |
| var key = symbolKeysA[i]; |
| |
| if (propertyIsEnumerable(val1, key)) { |
| if (!propertyIsEnumerable(val2, key)) { |
| return false; |
| } |
| |
| aKeys.push(key); |
| count++; |
| } else if (propertyIsEnumerable(val2, key)) { |
| return false; |
| } |
| } |
| |
| var symbolKeysB = objectGetOwnPropertySymbols(val2); |
| |
| if (symbolKeysA.length !== symbolKeysB.length && getEnumerables(val2, symbolKeysB).length !== count) { |
| return false; |
| } |
| } else { |
| var _symbolKeysB = objectGetOwnPropertySymbols(val2); |
| |
| if (_symbolKeysB.length !== 0 && getEnumerables(val2, _symbolKeysB).length !== 0) { |
| return false; |
| } |
| } |
| } |
| |
| if (aKeys.length === 0 && (iterationType === kNoIterator || iterationType === kIsArray && val1.length === 0 || val1.size === 0)) { |
| return true; |
| } // Use memos to handle cycles. |
| |
| |
| if (memos === undefined) { |
| memos = { |
| val1: new Map(), |
| val2: new Map(), |
| position: 0 |
| }; |
| } else { |
| // We prevent up to two map.has(x) calls by directly retrieving the value |
| // and checking for undefined. The map can only contain numbers, so it is |
| // safe to check for undefined only. |
| var val2MemoA = memos.val1.get(val1); |
| |
| if (val2MemoA !== undefined) { |
| var val2MemoB = memos.val2.get(val2); |
| |
| if (val2MemoB !== undefined) { |
| return val2MemoA === val2MemoB; |
| } |
| } |
| |
| memos.position++; |
| } |
| |
| memos.val1.set(val1, memos.position); |
| memos.val2.set(val2, memos.position); |
| var areEq = objEquiv(val1, val2, strict, aKeys, memos, iterationType); |
| memos.val1.delete(val1); |
| memos.val2.delete(val2); |
| return areEq; |
| } |
| |
| function setHasEqualElement(set, val1, strict, memo) { |
| // Go looking. |
| var setValues = arrayFromSet(set); |
| |
| for (var i = 0; i < setValues.length; i++) { |
| var val2 = setValues[i]; |
| |
| if (innerDeepEqual(val1, val2, strict, memo)) { |
| // Remove the matching element to make sure we do not check that again. |
| set.delete(val2); |
| return true; |
| } |
| } |
| |
| return false; |
| } // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#Loose_equality_using |
| // Sadly it is not possible to detect corresponding values properly in case the |
| // type is a string, number, bigint or boolean. The reason is that those values |
| // can match lots of different string values (e.g., 1n == '+00001'). |
| |
| |
| function findLooseMatchingPrimitives(prim) { |
| switch (_typeof(prim)) { |
| case 'undefined': |
| return null; |
| |
| case 'object': |
| // Only pass in null as object! |
| return undefined; |
| |
| case 'symbol': |
| return false; |
| |
| case 'string': |
| prim = +prim; |
| // Loose equal entries exist only if the string is possible to convert to |
| // a regular number and not NaN. |
| // Fall through |
| |
| case 'number': |
| if (numberIsNaN(prim)) { |
| return false; |
| } |
| |
| } |
| |
| return true; |
| } |
| |
| function setMightHaveLoosePrim(a, b, prim) { |
| var altValue = findLooseMatchingPrimitives(prim); |
| if (altValue != null) return altValue; |
| return b.has(altValue) && !a.has(altValue); |
| } |
| |
| function mapMightHaveLoosePrim(a, b, prim, item, memo) { |
| var altValue = findLooseMatchingPrimitives(prim); |
| |
| if (altValue != null) { |
| return altValue; |
| } |
| |
| var curB = b.get(altValue); |
| |
| if (curB === undefined && !b.has(altValue) || !innerDeepEqual(item, curB, false, memo)) { |
| return false; |
| } |
| |
| return !a.has(altValue) && innerDeepEqual(item, curB, false, memo); |
| } |
| |
| function setEquiv(a, b, strict, memo) { |
| // This is a lazily initiated Set of entries which have to be compared |
| // pairwise. |
| var set = null; |
| var aValues = arrayFromSet(a); |
| |
| for (var i = 0; i < aValues.length; i++) { |
| var val = aValues[i]; // Note: Checking for the objects first improves the performance for object |
| // heavy sets but it is a minor slow down for primitives. As they are fast |
| // to check this improves the worst case scenario instead. |
| |
| if (_typeof(val) === 'object' && val !== null) { |
| if (set === null) { |
| set = new Set(); |
| } // If the specified value doesn't exist in the second set its an not null |
| // object (or non strict only: a not matching primitive) we'll need to go |
| // hunting for something thats deep-(strict-)equal to it. To make this |
| // O(n log n) complexity we have to copy these values in a new set first. |
| |
| |
| set.add(val); |
| } else if (!b.has(val)) { |
| if (strict) return false; // Fast path to detect missing string, symbol, undefined and null values. |
| |
| if (!setMightHaveLoosePrim(a, b, val)) { |
| return false; |
| } |
| |
| if (set === null) { |
| set = new Set(); |
| } |
| |
| set.add(val); |
| } |
| } |
| |
| if (set !== null) { |
| var bValues = arrayFromSet(b); |
| |
| for (var _i = 0; _i < bValues.length; _i++) { |
| var _val = bValues[_i]; // We have to check if a primitive value is already |
| // matching and only if it's not, go hunting for it. |
| |
| if (_typeof(_val) === 'object' && _val !== null) { |
| if (!setHasEqualElement(set, _val, strict, memo)) return false; |
| } else if (!strict && !a.has(_val) && !setHasEqualElement(set, _val, strict, memo)) { |
| return false; |
| } |
| } |
| |
| return set.size === 0; |
| } |
| |
| return true; |
| } |
| |
| function mapHasEqualEntry(set, map, key1, item1, strict, memo) { |
| // To be able to handle cases like: |
| // Map([[{}, 'a'], [{}, 'b']]) vs Map([[{}, 'b'], [{}, 'a']]) |
| // ... we need to consider *all* matching keys, not just the first we find. |
| var setValues = arrayFromSet(set); |
| |
| for (var i = 0; i < setValues.length; i++) { |
| var key2 = setValues[i]; |
| |
| if (innerDeepEqual(key1, key2, strict, memo) && innerDeepEqual(item1, map.get(key2), strict, memo)) { |
| set.delete(key2); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| function mapEquiv(a, b, strict, memo) { |
| var set = null; |
| var aEntries = arrayFromMap(a); |
| |
| for (var i = 0; i < aEntries.length; i++) { |
| var _aEntries$i = _slicedToArray(aEntries[i], 2), |
| key = _aEntries$i[0], |
| item1 = _aEntries$i[1]; |
| |
| if (_typeof(key) === 'object' && key !== null) { |
| if (set === null) { |
| set = new Set(); |
| } |
| |
| set.add(key); |
| } else { |
| // By directly retrieving the value we prevent another b.has(key) check in |
| // almost all possible cases. |
| var item2 = b.get(key); |
| |
| if (item2 === undefined && !b.has(key) || !innerDeepEqual(item1, item2, strict, memo)) { |
| if (strict) return false; // Fast path to detect missing string, symbol, undefined and null |
| // keys. |
| |
| if (!mapMightHaveLoosePrim(a, b, key, item1, memo)) return false; |
| |
| if (set === null) { |
| set = new Set(); |
| } |
| |
| set.add(key); |
| } |
| } |
| } |
| |
| if (set !== null) { |
| var bEntries = arrayFromMap(b); |
| |
| for (var _i2 = 0; _i2 < bEntries.length; _i2++) { |
| var _bEntries$_i = _slicedToArray(bEntries[_i2], 2), |
| key = _bEntries$_i[0], |
| item = _bEntries$_i[1]; |
| |
| if (_typeof(key) === 'object' && key !== null) { |
| if (!mapHasEqualEntry(set, a, key, item, strict, memo)) return false; |
| } else if (!strict && (!a.has(key) || !innerDeepEqual(a.get(key), item, false, memo)) && !mapHasEqualEntry(set, a, key, item, false, memo)) { |
| return false; |
| } |
| } |
| |
| return set.size === 0; |
| } |
| |
| return true; |
| } |
| |
| function objEquiv(a, b, strict, keys, memos, iterationType) { |
| // Sets and maps don't have their entries accessible via normal object |
| // properties. |
| var i = 0; |
| |
| if (iterationType === kIsSet) { |
| if (!setEquiv(a, b, strict, memos)) { |
| return false; |
| } |
| } else if (iterationType === kIsMap) { |
| if (!mapEquiv(a, b, strict, memos)) { |
| return false; |
| } |
| } else if (iterationType === kIsArray) { |
| for (; i < a.length; i++) { |
| if (hasOwnProperty(a, i)) { |
| if (!hasOwnProperty(b, i) || !innerDeepEqual(a[i], b[i], strict, memos)) { |
| return false; |
| } |
| } else if (hasOwnProperty(b, i)) { |
| return false; |
| } else { |
| // Array is sparse. |
| var keysA = Object.keys(a); |
| |
| for (; i < keysA.length; i++) { |
| var key = keysA[i]; |
| |
| if (!hasOwnProperty(b, key) || !innerDeepEqual(a[key], b[key], strict, memos)) { |
| return false; |
| } |
| } |
| |
| if (keysA.length !== Object.keys(b).length) { |
| return false; |
| } |
| |
| return true; |
| } |
| } |
| } // The pair must have equivalent values for every corresponding key. |
| // Possibly expensive deep test: |
| |
| |
| for (i = 0; i < keys.length; i++) { |
| var _key = keys[i]; |
| |
| if (!innerDeepEqual(a[_key], b[_key], strict, memos)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| function isDeepEqual(val1, val2) { |
| return innerDeepEqual(val1, val2, kLoose); |
| } |
| |
| function isDeepStrictEqual(val1, val2) { |
| return innerDeepEqual(val1, val2, kStrict); |
| } |
| |
| module.exports = { |
| isDeepEqual: isDeepEqual, |
| isDeepStrictEqual: isDeepStrictEqual |
| }; |