var getOwnPropertyNames = Object.getOwnPropertyNames, getOwnPropertySymbols = Object.getOwnPropertySymbols; var hasOwnProperty = Object.prototype.hasOwnProperty; /** * Combine two comparators into a single comparators. */ function combineComparators(comparatorA, comparatorB) { return function isEqual(a, b, state) { return comparatorA(a, b, state) && comparatorB(a, b, state); }; } /** * Wrap the provided `areItemsEqual` method to manage the circular state, allowing * for circular references to be safely included in the comparison without creating * stack overflows. */ function createIsCircular(areItemsEqual) { return function isCircular(a, b, state) { if (!a || !b || typeof a !== 'object' || typeof b !== 'object') { return areItemsEqual(a, b, state); } var cache = state.cache; var cachedA = cache.get(a); var cachedB = cache.get(b); if (cachedA && cachedB) { return cachedA === b && cachedB === a; } cache.set(a, b); cache.set(b, a); var result = areItemsEqual(a, b, state); cache.delete(a); cache.delete(b); return result; }; } /** * Get the properties to strictly examine, which include both own properties that are * not enumerable and symbol properties. */ function getStrictProperties(object) { return getOwnPropertyNames(object).concat(getOwnPropertySymbols(object)); } /** * Whether the object contains the property passed as an own property. */ var hasOwn = Object.hasOwn || (function (object, property) { return hasOwnProperty.call(object, property); }); /** * Whether the values passed are strictly equal or both NaN. */ function sameValueZeroEqual(a, b) { return a || b ? a === b : a === b || (a !== a && b !== b); } var OWNER = '_owner'; var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor, keys = Object.keys; /** * Whether the arrays are equal in value. */ function areArraysEqual(a, b, state) { var index = a.length; if (b.length !== index) { return false; } while (index-- > 0) { if (!state.equals(a[index], b[index], index, index, a, b, state)) { return false; } } return true; } /** * Whether the dates passed are equal in value. */ function areDatesEqual(a, b) { return sameValueZeroEqual(a.getTime(), b.getTime()); } /** * Whether the `Map`s are equal in value. */ function areMapsEqual(a, b, state) { if (a.size !== b.size) { return false; } var matchedIndices = {}; var aIterable = a.entries(); var index = 0; var aResult; var bResult; while ((aResult = aIterable.next())) { if (aResult.done) { break; } var bIterable = b.entries(); var hasMatch = false; var matchIndex = 0; while ((bResult = bIterable.next())) { if (bResult.done) { break; } var _a = aResult.value, aKey = _a[0], aValue = _a[1]; var _b = bResult.value, bKey = _b[0], bValue = _b[1]; if (!hasMatch && !matchedIndices[matchIndex] && (hasMatch = state.equals(aKey, bKey, index, matchIndex, a, b, state) && state.equals(aValue, bValue, aKey, bKey, a, b, state))) { matchedIndices[matchIndex] = true; } matchIndex++; } if (!hasMatch) { return false; } index++; } return true; } /** * Whether the objects are equal in value. */ function areObjectsEqual(a, b, state) { var properties = keys(a); var index = properties.length; if (keys(b).length !== index) { return false; } var property; // Decrementing `while` showed faster results than either incrementing or // decrementing `for` loop and than an incrementing `while` loop. Declarative // methods like `some` / `every` were not used to avoid incurring the garbage // cost of anonymous callbacks. while (index-- > 0) { property = properties[index]; if (property === OWNER && (a.$$typeof || b.$$typeof) && a.$$typeof !== b.$$typeof) { return false; } if (!hasOwn(b, property) || !state.equals(a[property], b[property], property, property, a, b, state)) { return false; } } return true; } /** * Whether the objects are equal in value with strict property checking. */ function areObjectsEqualStrict(a, b, state) { var properties = getStrictProperties(a); var index = properties.length; if (getStrictProperties(b).length !== index) { return false; } var property; var descriptorA; var descriptorB; // Decrementing `while` showed faster results than either incrementing or // decrementing `for` loop and than an incrementing `while` loop. Declarative // methods like `some` / `every` were not used to avoid incurring the garbage // cost of anonymous callbacks. while (index-- > 0) { property = properties[index]; if (property === OWNER && (a.$$typeof || b.$$typeof) && a.$$typeof !== b.$$typeof) { return false; } if (!hasOwn(b, property)) { return false; } if (!state.equals(a[property], b[property], property, property, a, b, state)) { return false; } descriptorA = getOwnPropertyDescriptor(a, property); descriptorB = getOwnPropertyDescriptor(b, property); if ((descriptorA || descriptorB) && (!descriptorA || !descriptorB || descriptorA.configurable !== descriptorB.configurable || descriptorA.enumerable !== descriptorB.enumerable || descriptorA.writable !== descriptorB.writable)) { return false; } } return true; } /** * Whether the primitive wrappers passed are equal in value. */ function arePrimitiveWrappersEqual(a, b) { return sameValueZeroEqual(a.valueOf(), b.valueOf()); } /** * Whether the regexps passed are equal in value. */ function areRegExpsEqual(a, b) { return a.source === b.source && a.flags === b.flags; } /** * Whether the `Set`s are equal in value. */ function areSetsEqual(a, b, state) { if (a.size !== b.size) { return false; } var matchedIndices = {}; var aIterable = a.values(); var aResult; var bResult; while ((aResult = aIterable.next())) { if (aResult.done) { break; } var bIterable = b.values(); var hasMatch = false; var matchIndex = 0; while ((bResult = bIterable.next())) { if (bResult.done) { break; } if (!hasMatch && !matchedIndices[matchIndex] && (hasMatch = state.equals(aResult.value, bResult.value, aResult.value, bResult.value, a, b, state))) { matchedIndices[matchIndex] = true; } matchIndex++; } if (!hasMatch) { return false; } } return true; } /** * Whether the TypedArray instances are equal in value. */ function areTypedArraysEqual(a, b) { var index = a.length; if (b.length !== index) { return false; } while (index-- > 0) { if (a[index] !== b[index]) { return false; } } return true; } var ARGUMENTS_TAG = '[object Arguments]'; var BOOLEAN_TAG = '[object Boolean]'; var DATE_TAG = '[object Date]'; var MAP_TAG = '[object Map]'; var NUMBER_TAG = '[object Number]'; var OBJECT_TAG = '[object Object]'; var REG_EXP_TAG = '[object RegExp]'; var SET_TAG = '[object Set]'; var STRING_TAG = '[object String]'; var isArray = Array.isArray; var isTypedArray = typeof ArrayBuffer === 'function' && ArrayBuffer.isView ? ArrayBuffer.isView : null; var assign = Object.assign; var getTag = Object.prototype.toString.call.bind(Object.prototype.toString); /** * Create a comparator method based on the type-specific equality comparators passed. */ function createEqualityComparator(_a) { var areArraysEqual = _a.areArraysEqual, areDatesEqual = _a.areDatesEqual, areMapsEqual = _a.areMapsEqual, areObjectsEqual = _a.areObjectsEqual, arePrimitiveWrappersEqual = _a.arePrimitiveWrappersEqual, areRegExpsEqual = _a.areRegExpsEqual, areSetsEqual = _a.areSetsEqual, areTypedArraysEqual = _a.areTypedArraysEqual; /** * compare the value of the two objects and return true if they are equivalent in values */ return function comparator(a, b, state) { // If the items are strictly equal, no need to do a value comparison. if (a === b) { return true; } // If the items are not non-nullish objects, then the only possibility // of them being equal but not strictly is if they are both `NaN`. Since // `NaN` is uniquely not equal to itself, we can use self-comparison of // both objects, which is faster than `isNaN()`. if (a == null || b == null || typeof a !== 'object' || typeof b !== 'object') { return a !== a && b !== b; } var constructor = a.constructor; // Checks are listed in order of commonality of use-case: // 1. Common complex object types (plain object, array) // 2. Common data values (date, regexp) // 3. Less-common complex object types (map, set) // 4. Less-common data values (promise, primitive wrappers) // Inherently this is both subjective and assumptive, however // when reviewing comparable libraries in the wild this order // appears to be generally consistent. // Constructors should match, otherwise there is potential for false positives // between class and subclass or custom object and POJO. if (constructor !== b.constructor) { return false; } // `isPlainObject` only checks against the object's own realm. Cross-realm // comparisons are rare, and will be handled in the ultimate fallback, so // we can avoid capturing the string tag. if (constructor === Object) { return areObjectsEqual(a, b, state); } // `isArray()` works on subclasses and is cross-realm, so we can avoid capturing // the string tag or doing an `instanceof` check. if (isArray(a)) { return areArraysEqual(a, b, state); } // `isTypedArray()` works on all possible TypedArray classes, so we can avoid // capturing the string tag or comparing against all possible constructors. if (isTypedArray != null && isTypedArray(a)) { return areTypedArraysEqual(a, b, state); } // Try to fast-path equality checks for other complex object types in the // same realm to avoid capturing the string tag. Strict equality is used // instead of `instanceof` because it is more performant for the common // use-case. If someone is subclassing a native class, it will be handled // with the string tag comparison. if (constructor === Date) { return areDatesEqual(a, b, state); } if (constructor === RegExp) { return areRegExpsEqual(a, b, state); } if (constructor === Map) { return areMapsEqual(a, b, state); } if (constructor === Set) { return areSetsEqual(a, b, state); } // Since this is a custom object, capture the string tag to determing its type. // This is reasonably performant in modern environments like v8 and SpiderMonkey. var tag = getTag(a); if (tag === DATE_TAG) { return areDatesEqual(a, b, state); } if (tag === REG_EXP_TAG) { return areRegExpsEqual(a, b, state); } if (tag === MAP_TAG) { return areMapsEqual(a, b, state); } if (tag === SET_TAG) { return areSetsEqual(a, b, state); } if (tag === OBJECT_TAG) { // The exception for value comparison is custom `Promise`-like class instances. These should // be treated the same as standard `Promise` objects, which means strict equality, and if // it reaches this point then that strict equality comparison has already failed. return (typeof a.then !== 'function' && typeof b.then !== 'function' && areObjectsEqual(a, b, state)); } // If an arguments tag, it should be treated as a standard object. if (tag === ARGUMENTS_TAG) { return areObjectsEqual(a, b, state); } // As the penultimate fallback, check if the values passed are primitive wrappers. This // is very rare in modern JS, which is why it is deprioritized compared to all other object // types. if (tag === BOOLEAN_TAG || tag === NUMBER_TAG || tag === STRING_TAG) { return arePrimitiveWrappersEqual(a, b, state); } // If not matching any tags that require a specific type of comparison, then we hard-code false because // the only thing remaining is strict equality, which has already been compared. This is for a few reasons: // - Certain types that cannot be introspected (e.g., `WeakMap`). For these types, this is the only // comparison that can be made. // - For types that can be introspected, but rarely have requirements to be compared // (`ArrayBuffer`, `DataView`, etc.), the cost is avoided to prioritize the common // use-cases (may be included in a future release, if requested enough). // - For types that can be introspected but do not have an objective definition of what // equality is (`Error`, etc.), the subjective decision is to be conservative and strictly compare. // In all cases, these decisions should be reevaluated based on changes to the language and // common development practices. return false; }; } /** * Create the configuration object used for building comparators. */ function createEqualityComparatorConfig(_a) { var circular = _a.circular, createCustomConfig = _a.createCustomConfig, strict = _a.strict; var config = { areArraysEqual: strict ? areObjectsEqualStrict : areArraysEqual, areDatesEqual: areDatesEqual, areMapsEqual: strict ? combineComparators(areMapsEqual, areObjectsEqualStrict) : areMapsEqual, areObjectsEqual: strict ? areObjectsEqualStrict : areObjectsEqual, arePrimitiveWrappersEqual: arePrimitiveWrappersEqual, areRegExpsEqual: areRegExpsEqual, areSetsEqual: strict ? combineComparators(areSetsEqual, areObjectsEqualStrict) : areSetsEqual, areTypedArraysEqual: strict ? areObjectsEqualStrict : areTypedArraysEqual, }; if (createCustomConfig) { config = assign({}, config, createCustomConfig(config)); } if (circular) { var areArraysEqual$1 = createIsCircular(config.areArraysEqual); var areMapsEqual$1 = createIsCircular(config.areMapsEqual); var areObjectsEqual$1 = createIsCircular(config.areObjectsEqual); var areSetsEqual$1 = createIsCircular(config.areSetsEqual); config = assign({}, config, { areArraysEqual: areArraysEqual$1, areMapsEqual: areMapsEqual$1, areObjectsEqual: areObjectsEqual$1, areSetsEqual: areSetsEqual$1, }); } return config; } /** * Default equality comparator pass-through, used as the standard `isEqual` creator for * use inside the built comparator. */ function createInternalEqualityComparator(compare) { return function (a, b, _indexOrKeyA, _indexOrKeyB, _parentA, _parentB, state) { return compare(a, b, state); }; } /** * Create the `isEqual` function used by the consuming application. */ function createIsEqual(_a) { var circular = _a.circular, comparator = _a.comparator, createState = _a.createState, equals = _a.equals, strict = _a.strict; if (createState) { return function isEqual(a, b) { var _a = createState(), _b = _a.cache, cache = _b === void 0 ? circular ? new WeakMap() : undefined : _b, meta = _a.meta; return comparator(a, b, { cache: cache, equals: equals, meta: meta, strict: strict, }); }; } if (circular) { return function isEqual(a, b) { return comparator(a, b, { cache: new WeakMap(), equals: equals, meta: undefined, strict: strict, }); }; } var state = { cache: undefined, equals: equals, meta: undefined, strict: strict, }; return function isEqual(a, b) { return comparator(a, b, state); }; } /** * Whether the items passed are deeply-equal in value. */ var deepEqual = createCustomEqual(); /** * Whether the items passed are deeply-equal in value based on strict comparison. */ var strictDeepEqual = createCustomEqual({ strict: true }); /** * Whether the items passed are deeply-equal in value, including circular references. */ var circularDeepEqual = createCustomEqual({ circular: true }); /** * Whether the items passed are deeply-equal in value, including circular references, * based on strict comparison. */ var strictCircularDeepEqual = createCustomEqual({ circular: true, strict: true, }); /** * Whether the items passed are shallowly-equal in value. */ var shallowEqual = createCustomEqual({ createInternalComparator: function () { return sameValueZeroEqual; }, }); /** * Whether the items passed are shallowly-equal in value based on strict comparison */ var strictShallowEqual = createCustomEqual({ strict: true, createInternalComparator: function () { return sameValueZeroEqual; }, }); /** * Whether the items passed are shallowly-equal in value, including circular references. */ var circularShallowEqual = createCustomEqual({ circular: true, createInternalComparator: function () { return sameValueZeroEqual; }, }); /** * Whether the items passed are shallowly-equal in value, including circular references, * based on strict comparison. */ var strictCircularShallowEqual = createCustomEqual({ circular: true, createInternalComparator: function () { return sameValueZeroEqual; }, strict: true, }); /** * Create a custom equality comparison method. * * This can be done to create very targeted comparisons in extreme hot-path scenarios * where the standard methods are not performant enough, but can also be used to provide * support for legacy environments that do not support expected features like * `RegExp.prototype.flags` out of the box. */ function createCustomEqual(options) { if (options === void 0) { options = {}; } var _a = options.circular, circular = _a === void 0 ? false : _a, createCustomInternalComparator = options.createInternalComparator, createState = options.createState, _b = options.strict, strict = _b === void 0 ? false : _b; var config = createEqualityComparatorConfig(options); var comparator = createEqualityComparator(config); var equals = createCustomInternalComparator ? createCustomInternalComparator(comparator) : createInternalEqualityComparator(comparator); return createIsEqual({ circular: circular, comparator: comparator, createState: createState, equals: equals, strict: strict }); } export { circularDeepEqual, circularShallowEqual, createCustomEqual, deepEqual, sameValueZeroEqual, shallowEqual, strictCircularDeepEqual, strictCircularShallowEqual, strictDeepEqual, strictShallowEqual }; //# sourceMappingURL=index.mjs.map