UNPKG

kewlr

Version:

Compare objects and return whether they are equal according to a recursive equality algorithm. This module is around 6 - 9% faster then similiar modules. Works for both NodejS and the browser.

985 lines (945 loc) 31.1 kB
/* global Buffer, Symbol, Uint8Array, DataView, ArrayBuffer, ArrayBufferView, Map, Set, Uint8Array, Uint16Array, Uint32Array, Float32Array, Float64Array, Uint8ClampedArray, Int8Array */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); /** * Checks if `value` is the * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @typedef {true | false} isObject * @property {[any]} [value] */ function isObject(value) { var type = typeof value; return value != null && (type == 'object' || type == 'function'); } /** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @typedef {true | false} isObjectLike * @property {[any]} [value] */ function isObjectLike(value) { return value != null && typeof value == 'object'; } /** Built-in value references. */ /** Built-in value references. */ var isArray = Array.isArray; var getPrototype = Object.getPrototypeOf; var hasOwn = Object.prototype.hasOwnProperty; var objectToString = Object.prototype.toString; // Accept core-js's almost-correct polyfill var REAL_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator !== undefined; var FAUX_ITERATOR_SYMBOL = '@@iterator'; var ITERATOR_SYMBOL = REAL_ITERATOR_SYMBOL ? Symbol.iterator : FAUX_ITERATOR_SYMBOL; var supportsMap = typeof Map === 'function'; var supportsSet = typeof Set === 'function'; var supportsUnicode = hasOwn.call(RegExp.prototype, 'unicode'); var supportsSticky = hasOwn.call(RegExp.prototype, 'sticky'); // core-js' symbols are objects, and some old versions of V8 erroneously had // `typeof Symbol() === "object"`. var symbolsAreObjects = typeof Symbol === 'function' && typeof Symbol() === 'object'; // IE feature detection - workaround for IE coz IE11 doesn't support Set#entries or Set#@@iterator var isIE = typeof window !== 'undefined' && window.navigator.userAgent.indexOf('Trident/') > 0 ? true : false; /** * Returns iterator function if it exist, otherwise return undefined * * @typedef {true | false | undefined} getIteratorFn * @property {[any]} [iterable] */ function getIteratorFn(iterable) { // Accept the naïve "@@iterator" protocol, too, but only if `Symbol.iterator` // doesn't exist. return (REAL_ITERATOR_SYMBOL && iterable[ITERATOR_SYMBOL]) || iterable[FAUX_ITERATOR_SYMBOL]; } /** * Compare two Map() values * * @typedef {true | false} compareValues * @property {[Map]} [actual] * @property {Map} [expected] * @property {any[]} [actualKeys] * @property {any[]} [expectedKeys] * @property {number} [end] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {Map[]} [left] * @property {Map[]} [right] */ function compareValues(actual, expected, actualKeys, expectedKeys, end, isEqual, context, left, right) { for (var i = end - 1; i >= 0; i--) { if (isEqual(actual.get(actualKeys[i]), expected.get(expectedKeys[i]), isEqual, context, left, right) === false) { return false; } } return true; } /** * Check if an array contains equal keys * * @typedef {true | false} equalKeys * @property {[any[]]} [actualKeys] * @property {any[]} [expectedKeys] * @property {number} [start] * @property {number} [end] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any[]} [left] * @property {any[]} [right] */ function equalKeys(actualKeys, expectedKeys, start, end, isEqual, context, left, right) { for (var i = start + 1; i < end; i++) { var key = expectedKeys[i]; if (isEqual(actualKeys, key, isEqual, context, left, right)) { while (i > start) { expectedKeys[i] = expectedKeys[--i]; } expectedKeys[i] = key; return true; } } return false; } /** * Converts `map`to array for faster handling. * * @typedef {Map} convertMapToArray * @property {[ Map[] | Set[]]} [obj] */ function convertMapToArray(obj) { if (isIE) { var index = -1; var result = Array(obj.size); obj.forEach(function (value, key) { result[++index] = [key, value]; }); return result; } else { var list = new Array(obj.size); var i = 0; var iter = obj.keys(); for (var next = iter.next(); !next.done; next = iter.next()) { list[i++] = next.value; } return list; } } /** * Compare equality between two Map(). * * @typedef {true | false} equalMap * @property {[Map<any, any>]} [actual] * @property {Map<any, any>} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {Map<any, any>[]} [left] * @property {Map<any, any>[]} [right] */ function equalMap(actual, expected, isEqual, context, left, right) { var end = actual.size; var actualKeys = convertMapToArray(actual); var expectedKeys = convertMapToArray(expected); var index = 0; // cheap O(n) - assumes everything is in order while (index !== end && isEqual(actualKeys[index], expectedKeys[index], isEqual, context, left, right)) { index++; } if (index === end) { return compareValues(actual, expected, actualKeys, expectedKeys, end, isEqual, context, left, right); } // Don't compare the same key twice if (equalKeys(expectedKeys[index], actualKeys, index, end, isEqual, context, left, right) === false) { return false; } // expensive O(n^2) linear check while (++index < end) { var key = expectedKeys[index]; if ((isEqual(key, actualKeys[index], isEqual, context, left, right) === false) && equalKeys(key, actualKeys, index, end, isEqual, context, left, right) === false) { return false; } } return compareValues(actual, expected, actualKeys, expectedKeys, end, isEqual, context, left, right); } /** * Check if two arrays has equal keys * * @typedef {true | false} equalArrays * @property {[any]} [actual] * @property {any[]} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any} [left] * @property {any} [right] */ function equalArrays(actual, expected, isEqual, context, left, right) { var count = actual.length; // Same number of own properties if (count !== expected.length) { return false; } // Equal if both are empty if (count === 0) { return true; } // deep compare the contents, ignoring non-numeric properties. while (count--) { if (isEqual(actual[count], expected[count], isEqual, context, left, right) === false) { return false; } } return true; } function convertSetToArray(obj) { if (isIE) { var index = -1; var result = Array(obj.size); obj.forEach(function (value) { result[++index] = value; }); return result; } else { var list = new Array(obj.size); var i = 0; var iter = obj.keys(); for (var next = iter.next(); !next.done; next = iter.next()) { list[i++] = next.value; } return list; } } /** * Compare equality between two Set() * * @typedef {true | false} equalSet * @property {[Set]} [actual] * @property {Set} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {Set[]} [left] * @property {Set[]} [right] */ function equalSet(actual, expected, isEqual, context, left, right) { return equalArrays(convertSetToArray(actual), convertSetToArray(expected), isEqual, context, left, right); } function isEqualIterators(actual, expected, isEqual, context, left, right) { var anext = actual.next(); var bnext = expected.next(); while (!anext.done && !bnext.done) { if (isEqual(anext.value, bnext.value, isEqual, context, left, right) === false) { return false; } anext = actual.next(); bnext = expected.next(); } return anext.done && bnext.done; } /** * Compare interators * * @typedef {true | false | undefined } compareIterators * @property {[any]} [actual] * @property {any} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any} [left] * @property {any} [right] */ function compareIterators(actual, expected, isEqual, context, left, right) { var thrown = false; var err; // Note! The try-finally clauses are to prevent a memory leak in case an error occurs // (which would also corrupt the contents, leaving junk for the next call). try { return isEqualIterators(actual, expected, isEqual, context, left, right); } catch (e) { thrown = true; err = e; } finally { try { if (typeof actual.return === 'function') { actual.return(); } } finally { try { if (typeof expected.return === 'function') { expected.return(); } } finally { if (thrown) { throw err; } } } } } /** * Compare circular references for iterators * * @typedef {true | false} iteratorReferences * @property {[any]} [actual] * @property {any} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any} [left] * @property {any} [right] */ function iteratorReferences(actual, expected, isEqual, context, left, right) { var leftIndex = left.indexOf(actual); var rightIndex = right.indexOf(expected); if (leftIndex === rightIndex) { if (leftIndex >= 0) { return true; } left.push(actual); right.push(expected); var result = compareIterators(actual[ITERATOR_SYMBOL](), expected[ITERATOR_SYMBOL](), isEqual, context, left, right); left.pop(); right.pop(); return result; } return false; } /** * Compare possible function objects * * @typedef {any} compareFunctionObjects * @property {[any]} [actual] * @property {any} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any} [left] * @property {any} [right] */ function compareFunctionObjects(actual, expected, tag, isEqual, context, left, right) { return getIteratorFn(actual) && (iteratorReferences(actual, expected, isEqual, context, [left], [right])); } /** * Compare inner values * * @typedef {true | false} compareInnerValues * @property {[any]} [actual] * @property {any} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any} [left] * @property {any} [right] */ function compareInnerValues(actual, expected, isEqual, context, left, right) { // Fixes circular reference issues with Arrays, Map() and Set() in strict mode if (context & 131072 /* EQUAL_PROTO */) { // 'strict mode' *only* if (context & 32768 /* STRICT_MODE */) { if (isArray(actual)) { return equalArrays(actual, expected, isEqual, context, left, right); } if (supportsMap && actual instanceof Map) { return equalMap(actual, expected, isEqual, context, left, right); } if (supportsSet && actual instanceof Set) { return equalSet(actual, expected, isEqual, context, left, right); } if (getIteratorFn(actual)) { return compareFunctionObjects(actual, expected, 'dd', isEqual, context, left, right); } } } var actualKeys = Object.keys(actual); var expectedKeys = Object.keys(expected); // if they don't have the same length, then not equivalent if (actualKeys.length !== expectedKeys.length) { return false; } // Shortcut if there's nothing to match if (actualKeys.length === 0) { return true; } var index = actualKeys.length; while (index--) { var key = actualKeys[index]; if (!(hasOwn.call(expected, key))) { return false; } } // equivalent values for every corresponding key, and possibly expensive deep test while (++index < actualKeys.length) { var key$1 = actualKeys[index]; var actualValue = actual[key$1]; var expectedValue = expected[key$1]; if (!(actualValue === expectedValue || isEqual(actualValue, expectedValue, isEqual, context, left, right))) { return false; } } return true; } /** * Gets the index at which the first occurrence of `value` is found in `array`. * If `fromIndex` is negative, it's used as the offset from the end of `array`. * * @typedef {any} indexOf * @property {[any[]]} [array] * @property {any} [value] * @property {number} [fromIndex] */ function indexOf(array, value) { var length = array ? array.length : 0; var index = -1; var isReflexive = value === value; while (++index < length) { var other = array[index]; if ((isReflexive ? other === value : other !== other)) { return index; } } return -1; } /** * Compare circular references * * @typedef {true | false} compareReferences * @property {[any]} [actual] * @property {any} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any} [left] * @property {any} [right] */ function compareReferences(actual, expected, isEqual, context, left, right) { // strict mode *only* if (!(context & 65536 /* SHALLOW_MODE */)) { var leftIndex = indexOf(left, actual); var rightIndex = indexOf(right, expected); if (leftIndex === rightIndex) { if (leftIndex >= 0) { return true; } left.push(actual); right.push(expected); var result = compareInnerValues(actual, expected, isEqual, context, left, right); left.pop(); right.pop(); return result; } return false; } // compare only innerValues for 'loose mode' return compareInnerValues(actual, expected, isEqual, context & ~65536 /* SHALLOW_MODE */, [actual], [expected]); } var isFunction$1 = (function () { function SlowIsFunction(value) { if (value == null) { return false; } var tag = objectToString.call(value); return tag === '[object Function]' || tag === '[object GeneratorFunction]' || tag === '[object AsyncFunction]' || tag === '[object Proxy]'; } function isPoisoned(object) { return object != null && typeof object !== 'function'; } // In Safari 10, `typeof Proxy === 'object'` if (isPoisoned(global.Proxy)) { return SlowIsFunction; } // In Safari 8, several typed array constructors are `typeof C === 'object'` if (isPoisoned(global.Int8Array)) { return SlowIsFunction; } // In old V8, RegExps are callable return function isFunction(value) { return typeof value === 'function'; }; })(); /** * Check if Buffer are supported * * @typedef {any} bufferSupport */ var bufferSupport = (function () { var FakeBuffer = function FakeBuffer () {}; FakeBuffer.prototype.isBuffer = function isBuffer () { return true; }; if (!isFunction$1(Buffer)) { return 8 /* BUFFER_POLYFILL */; } if (!isFunction$1(Buffer.isBuffer) || Buffer.isBuffer(new FakeBuffer())) { return 8 /* BUFFER_POLYFILL */; } return 4 /* BUFFER_NATIVE */; })(); /** * Check if isPolyfilledFastBuffer are used * * @typedef {true | false} isPolyfilledFastBuffer * @property {[any]} [Object] */ function isPolyfilledFastBuffer(object) { var Buffer = object.constructor; if (!isFunction$1(Buffer) || !isFunction$1(Buffer.isBuffer)) { return false; } return Buffer.isBuffer(object); } var globalIsBuffer = bufferSupport === 4 /* BUFFER_NATIVE */ ? global.Buffer.isBuffer : undefined; /** * isBuffer * * @typedef {true | false} isBuffer * @property {[any]} [Object] */ function isBuffer(object) { if (bufferSupport === 4 /* BUFFER_NATIVE */ && globalIsBuffer(object) || isPolyfilledFastBuffer(object)) { return true; } if (!isFunction$1(object.slice)) { return false; } var slice = object.slice(0, 0); return slice != null && isPolyfilledFastBuffer(slice); } /** * Performs a [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * comparison between two values to determine if they are equivalent. * * @typedef {true | false} isStrictEqual * @property {[any]} [actual] * @property {any} [expected] */ function isStrictEqual(actual, expected) { return actual === expected || (actual !== actual && expected !== expected); } /** * Check if arrayBuffer are supported * * @typedef {any} arrayBufferSupport */ var arrayBufferSupport = (function () { if (!isFunction$1(Uint8Array) || !isFunction$1(DataView) || !isFunction$1(ArrayBuffer)) { return 1 /* BUFFER_NONE */; } // ES6 typed arrays if (isFunction$1(ArrayBuffer.isView)) { return 2 /* BUFFER_CURRENT */; } return 1 /* BUFFER_NONE */; })(); /** * Determine if 'isView' is supported or not * * @typedef {any} isView */ var isView = (function () { switch (arrayBufferSupport) { case 2 /* BUFFER_CURRENT */: return ArrayBuffer.isView; case 1 /* BUFFER_NONE */: return undefined; default: return undefined; } })(); var argsTag = '[object Arguments]'; var numberTag = '[object Number]'; var weakMapTag = '[object WeakMap]'; var promiseTag = '[object Promise]'; var weakSetTag = '[object WeakSet]'; var errorTag = '[object Error]'; var boolTag = '[object Boolean]'; var stringTag = '[object String]'; /** * Compare two regExp values and check if they are equivalent. * * @typedef {true | false} compareRegEx * @property {[RegExp]} [actual] * @property {RegExp} [expected] */ function compareRegEx(actual, expected) { return actual.source === expected.source && actual.global === expected.global && actual.ignoreCase === expected.ignoreCase && actual.multiline === expected.multiline && actual.lastIndex === expected.lastIndex && (!supportsUnicode || actual.unicode === expected.unicode) && // Only supported by Firefox (!supportsSticky || actual.sticky === expected.sticky); } /** * Compare two Buffer.isView() values * * @typedef {true | false} equalView * @property {[Uint8Array]} [actual] * @property {Uint8Array} [expected] */ function equalView(actual, expected) { var count = actual.length; if (count !== expected.length) { return false; } while (count) { count--; if (actual[count] !== expected[count]) { return false; } } return true; } /** * Compare objects with identical prototypes * * @typedef {true | false} equalProtos * @property {[any]} [actual] * @property {any} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any} [left] * @property {any} [right] */ function equalProtos(actual, expected, isEqual, context, left, right) { if (isArray(actual)) { if (actual.length !== expected.length) { return false; } if (actual.length === 0) { return true; } } // RegExp if (actual instanceof RegExp) { return compareRegEx(actual, expected); } // Date if (actual instanceof Date) { return actual.getTime() === expected.getTime(); } // Map() if (supportsMap && actual instanceof Map) { // check for different primitive keys if (actual.size !== expected.size) { return false; } if (actual.size === 0) { return true; } } // Map() if (supportsSet && actual instanceof Set) { // check for different primitive keys if (actual.size !== expected.size) { return false; } if (actual.size === 0) { return true; } } // DataView, ArrayBuffer and Buffer if ((arrayBufferSupport & 1 /* BUFFER_NONE */) === 0) { if (actual instanceof DataView) { return equalView(new Uint8Array(actual.buffer, actual.byteOffset, actual.byteLength), new Uint8Array(expected.buffer, expected.byteOffset, expected.byteLength)); } if (actual instanceof ArrayBuffer) { if ((actual.byteLength !== expected.byteLength)) { return false; } return equalView(new Uint8Array(actual), new Uint8Array(expected)); } if (isBuffer(actual) || isView(actual)) { if (actual.length !== expected.length) { return false; } if (actual.length === 0) { return true; } return equalView(actual, expected); } } // There is a known bug with the 'typeof' operator in in Safari 9 which returns 'object' for // typed array and other constructors. And there is also an issue with Safari 10 for window.Proxy. // This will not affect 'kewlr' coz we are only returning false after checking for iterabels. if (typeof actual === 'function') { if (getIteratorFn(actual) && (compareReferences(actual, expected, isEqual, context, left, right))) { return true; } return false; } // Numbers, Booleans, WeakMap, WeakSet, Promise, Error and String switch (objectToString.call(actual)) { // booleans and number primitives and their corresponding object wrappers case boolTag: return +actual === +expected; case numberTag: return isStrictEqual(+actual, +expected); case stringTag: return actual == (expected + ''); // None of this has enumerable keys, so we are only returning false case weakMapTag: case weakSetTag: case promiseTag: case errorTag: return false; case argsTag: if (objectToString.call(expected) !== argsTag || actual.length !== expected.length) { return false; } if (actual.length === 0) { return true; } default: return compareReferences(actual, expected, isEqual, context, left, right); } } /** * Performs a [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * comparison between two values to determine if they are equivalent. * * @typedef {true | false} isLooseEqual * @property {[any]} [actual] * @property {any} [expected] */ function isLooseEqual(actual, expected) { return actual == expected || (actual !== actual && expected !== expected); } /** * Compare objects with different prototypes. This is only done for the 'loose' mode. * Only return false for the 'strict' mode. * * @typedef {true | false} differentProtos * @property {[any]} [actual] * @property {any} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any} [left] * @property {any} [right] */ function differentProtos(actual, expected, isEqual, context, left, right) { // core.js and older V8 compat if (symbolsAreObjects) { if (actual instanceof Symbol || expected instanceof Symbol) { return false; } } // Only return 'false' for strict mode if (context & 32768 /* STRICT_MODE */) { return false; } // RegExp if (actual instanceof RegExp && expected instanceof RegExp) { return compareRegEx(actual, expected); } // isArray. Note! this will return true for' loose([], {})' if (isArray(actual) && isArray(expected)) { if (actual.length !== expected.length) { return false; } if (actual.length === 0) { return true; } } // DataView, ArrayBuffer and Buffer if ((arrayBufferSupport & 1 /* BUFFER_NONE */) === 0) { if (actual instanceof DataView) { if ((actual.byteLength != expected.byteLength) || (actual.byteOffset != expected.byteOffset)) { return false; } return equalView(new Uint8Array(actual.buffer, actual.byteOffset, actual.byteLength), new Uint8Array(expected.buffer, expected.byteOffset, expected.byteLength)); } if (actual instanceof ArrayBuffer) { if ((actual.byteLength != expected.byteLength) || !equalView(new Uint8Array(actual), new Uint8Array(expected))) { return false; } return true; } if (isBuffer(actual) || isView(actual)) { if (actual.length !== expected.length) { return false; } return equalView(actual, expected); } } // Date if (actual instanceof Date && expected instanceof Date) { return actual.getTime() === expected.getTime(); } // Map() && Set() if ((supportsMap && actual instanceof Map) || (supportsSet && actual instanceof Set)) { // check for different primitive keys if (actual.size === expected.size) { return true; } // return false by default return false; } var actualTag = objectToString.call(actual); // There is a known bug with the 'typeof' operator in in Safari 9 which returns 'object' for // typed array and other constructors. And there is also an issue with Safari 10 for window.Proxy. // This will not affect 'kewlr' coz we are only returning false after checking for iterabels. if (typeof actual === 'function') { if (getIteratorFn(actual) && (compareReferences(actual, expected, isEqual, context, left, right))) { return true; } return false; } switch (actualTag) { case boolTag: return +actual === +expected; case numberTag: return isLooseEqual(+actual, +expected); case stringTag: return actual == (expected + ''); // None of this has enumerable keys, so we are only returning false case weakMapTag: case weakSetTag: case promiseTag: return false; case errorTag: return actual.name == actual.name && actual.message == actual.message; default: if (actualTag === argsTag) { if (objectToString.call(expected) !== argsTag || actual.length !== expected.length) { return false; } if (actual.length === 0) { return true; } } else if (objectToString.call(expected) === argsTag) { return false; } return compareReferences(actual, expected, isEqual, context, left, right); } } /** * Deep equal main function * * @typedef {true | false} deepEqual * @property {[any]} [actual] * @property {any} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any} [left] * @property {any} [right] */ function deepEqual(actual, expected, isEqual, context, left, right) { return context & 32768 /* STRICT_MODE */ && getPrototype(actual) === getPrototype(expected) ? equalProtos(actual, expected, isEqual, context | 131072 /* EQUAL_PROTO */, left, right) : differentProtos(actual, expected, isEqual, context, left, right); } /** * Loose equal * * @typedef {true | false} shallowEqual * @property {[any]} [actual] * @property {any} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any} [left] * @property {any} [right] */ function shallowEqual(actual, expected, isEqual, context, left, right) { // if they reference the same object in memory, then they are the same if (actual == expected) { return true; } if (actual == null || expected == null || (!isObject(actual) && !isObjectLike(expected))) { return actual != actual && expected != expected; } return deepEqual(actual, expected, isEqual, context, left, right); } /** * Strict equal * * @typedef {true | false} strictEqual * @property {[any]} [actual] * @property {any} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any} [left] * @property {any} [right] */ function strictEqual(actual, expected, isEqual, context, left, right) { // if they reference the same object in memory, then they are the same if (actual === expected) { return true; } // NaNs are equal if (actual !== actual) { return expected !== expected; } if (actual === null || actual === undefined // eslint-disable-line no-undefined ) { return actual === expected; } if (!isObject(actual) && !isObjectLike(expected)) { return actual === expected; } return deepEqual(actual, expected, isEqual, context, left, right); } /** * Chai equal * * @typedef {true | false} chaiEqual * @property {[any]} [actual] * @property {any} [expected] * @property {EqualFunc} [isEqual] * @property {number} [context] * @property {any} [left] * @property {any} [right] */ function chaiEqual(actual, expected, isEqual, context, left, right) { // if they reference the same object in memory, then they are the same if (actual === expected) { // Handle +-0 cases return actual !== 0 || 1 / actual === 1 / expected; } if (actual == null || expected == null || (!isObject(actual) && !isObjectLike(expected))) { return actual !== actual && expected !== expected; } return deepEqual(actual, expected, isEqual, context, left, right); } /** * Shallow mode * * @typedef {true | false} shallow * @property {[any]} [actual] * @property {any} [expected] */ function shallow(actual, expected) { return shallowEqual(actual, expected, shallowEqual, 65536 /* SHALLOW_MODE */); } /** * Chai mode * * @typedef {true | false} match * @property {[any]} [actual] * @property {any} [expected] */ function chai(actual, expected) { return chaiEqual(actual, expected, strictEqual, 32768 /* STRICT_MODE */ | 65536 /* SHALLOW_MODE */); } /** * Strict mode * * @typedef {true | false} strict * @property {[any]} [actual] * @property {any} [expected] */ function strict(actual, expected) { return strictEqual(actual, expected, strictEqual, 32768 /* STRICT_MODE */ | 65536 /* SHALLOW_MODE */); } exports.shallow = shallow; exports.chai = chai; exports.strict = strict;