| 'use strict'; | 
|   | 
| const Types = require('./types'); | 
|   | 
|   | 
| const internals = { | 
|     mismatched: null | 
| }; | 
|   | 
|   | 
| module.exports = function (obj, ref, options) { | 
|   | 
|     options = Object.assign({ prototype: true }, options); | 
|   | 
|     return !!internals.isDeepEqual(obj, ref, options, []); | 
| }; | 
|   | 
|   | 
| internals.isDeepEqual = function (obj, ref, options, seen) { | 
|   | 
|     if (obj === ref) {                                                      // Copied from Deep-eql, copyright(c) 2013 Jake Luer, jake@alogicalparadox.com, MIT Licensed, https://github.com/chaijs/deep-eql | 
|         return obj !== 0 || 1 / obj === 1 / ref; | 
|     } | 
|   | 
|     const type = typeof obj; | 
|   | 
|     if (type !== typeof ref) { | 
|         return false; | 
|     } | 
|   | 
|     if (obj === null || | 
|         ref === null) { | 
|   | 
|         return false; | 
|     } | 
|   | 
|     if (type === 'function') { | 
|         if (!options.deepFunction || | 
|             obj.toString() !== ref.toString()) { | 
|   | 
|             return false; | 
|         } | 
|   | 
|         // Continue as object | 
|     } | 
|     else if (type !== 'object') { | 
|         return obj !== obj && ref !== ref;                                  // NaN | 
|     } | 
|   | 
|     const instanceType = internals.getSharedType(obj, ref, !!options.prototype); | 
|     switch (instanceType) { | 
|         case Types.buffer: | 
|             return Buffer && Buffer.prototype.equals.call(obj, ref);        // $lab:coverage:ignore$ | 
|         case Types.promise: | 
|             return obj === ref; | 
|         case Types.regex: | 
|             return obj.toString() === ref.toString(); | 
|         case internals.mismatched: | 
|             return false; | 
|     } | 
|   | 
|     for (let i = seen.length - 1; i >= 0; --i) { | 
|         if (seen[i].isSame(obj, ref)) { | 
|             return true;                                                    // If previous comparison failed, it would have stopped execution | 
|         } | 
|     } | 
|   | 
|     seen.push(new internals.SeenEntry(obj, ref)); | 
|   | 
|     try { | 
|         return !!internals.isDeepEqualObj(instanceType, obj, ref, options, seen); | 
|     } | 
|     finally { | 
|         seen.pop(); | 
|     } | 
| }; | 
|   | 
|   | 
| internals.getSharedType = function (obj, ref, checkPrototype) { | 
|   | 
|     if (checkPrototype) { | 
|         if (Object.getPrototypeOf(obj) !== Object.getPrototypeOf(ref)) { | 
|             return internals.mismatched; | 
|         } | 
|   | 
|         return Types.getInternalProto(obj); | 
|     } | 
|   | 
|     const type = Types.getInternalProto(obj); | 
|     if (type !== Types.getInternalProto(ref)) { | 
|         return internals.mismatched; | 
|     } | 
|   | 
|     return type; | 
| }; | 
|   | 
|   | 
| internals.valueOf = function (obj) { | 
|   | 
|     const objValueOf = obj.valueOf; | 
|     if (objValueOf === undefined) { | 
|         return obj; | 
|     } | 
|   | 
|     try { | 
|         return objValueOf.call(obj); | 
|     } | 
|     catch (err) { | 
|         return err; | 
|     } | 
| }; | 
|   | 
|   | 
| internals.hasOwnEnumerableProperty = function (obj, key) { | 
|   | 
|     return Object.prototype.propertyIsEnumerable.call(obj, key); | 
| }; | 
|   | 
|   | 
| internals.isSetSimpleEqual = function (obj, ref) { | 
|   | 
|     for (const entry of obj) { | 
|         if (!ref.has(entry)) { | 
|             return false; | 
|         } | 
|     } | 
|   | 
|     return true; | 
| }; | 
|   | 
|   | 
| internals.isDeepEqualObj = function (instanceType, obj, ref, options, seen) { | 
|   | 
|     const { isDeepEqual, valueOf, hasOwnEnumerableProperty } = internals; | 
|     const { keys, getOwnPropertySymbols } = Object; | 
|   | 
|     if (instanceType === Types.array) { | 
|         if (options.part) { | 
|   | 
|             // Check if any index match any other index | 
|   | 
|             for (const objValue of obj) { | 
|                 for (const refValue of ref) { | 
|                     if (isDeepEqual(objValue, refValue, options, seen)) { | 
|                         return true; | 
|                     } | 
|                 } | 
|             } | 
|         } | 
|         else { | 
|             if (obj.length !== ref.length) { | 
|                 return false; | 
|             } | 
|   | 
|             for (let i = 0; i < obj.length; ++i) { | 
|                 if (!isDeepEqual(obj[i], ref[i], options, seen)) { | 
|                     return false; | 
|                 } | 
|             } | 
|   | 
|             return true; | 
|         } | 
|     } | 
|     else if (instanceType === Types.set) { | 
|         if (obj.size !== ref.size) { | 
|             return false; | 
|         } | 
|   | 
|         if (!internals.isSetSimpleEqual(obj, ref)) { | 
|   | 
|             // Check for deep equality | 
|   | 
|             const ref2 = new Set(ref); | 
|             for (const objEntry of obj) { | 
|                 if (ref2.delete(objEntry)) { | 
|                     continue; | 
|                 } | 
|   | 
|                 let found = false; | 
|                 for (const refEntry of ref2) { | 
|                     if (isDeepEqual(objEntry, refEntry, options, seen)) { | 
|                         ref2.delete(refEntry); | 
|                         found = true; | 
|                         break; | 
|                     } | 
|                 } | 
|   | 
|                 if (!found) { | 
|                     return false; | 
|                 } | 
|             } | 
|         } | 
|     } | 
|     else if (instanceType === Types.map) { | 
|         if (obj.size !== ref.size) { | 
|             return false; | 
|         } | 
|   | 
|         for (const [key, value] of obj) { | 
|             if (value === undefined && !ref.has(key)) { | 
|                 return false; | 
|             } | 
|   | 
|             if (!isDeepEqual(value, ref.get(key), options, seen)) { | 
|                 return false; | 
|             } | 
|         } | 
|     } | 
|     else if (instanceType === Types.error) { | 
|   | 
|         // Always check name and message | 
|   | 
|         if (obj.name !== ref.name || | 
|             obj.message !== ref.message) { | 
|   | 
|             return false; | 
|         } | 
|     } | 
|   | 
|     // Check .valueOf() | 
|   | 
|     const valueOfObj = valueOf(obj); | 
|     const valueOfRef = valueOf(ref); | 
|     if ((obj !== valueOfObj || ref !== valueOfRef) && | 
|         !isDeepEqual(valueOfObj, valueOfRef, options, seen)) { | 
|   | 
|         return false; | 
|     } | 
|   | 
|     // Check properties | 
|   | 
|     const objKeys = keys(obj); | 
|     if (!options.part && | 
|         objKeys.length !== keys(ref).length && | 
|         !options.skip) { | 
|   | 
|         return false; | 
|     } | 
|   | 
|     let skipped = 0; | 
|     for (const key of objKeys) { | 
|         if (options.skip && | 
|             options.skip.includes(key)) { | 
|   | 
|             if (ref[key] === undefined) { | 
|                 ++skipped; | 
|             } | 
|   | 
|             continue; | 
|         } | 
|   | 
|         if (!hasOwnEnumerableProperty(ref, key)) { | 
|             return false; | 
|         } | 
|   | 
|         if (!isDeepEqual(obj[key], ref[key], options, seen)) { | 
|             return false; | 
|         } | 
|     } | 
|   | 
|     if (!options.part && | 
|         objKeys.length - skipped !== keys(ref).length) { | 
|   | 
|         return false; | 
|     } | 
|   | 
|     // Check symbols | 
|   | 
|     if (options.symbols !== false) {                                // Defaults to true | 
|         const objSymbols = getOwnPropertySymbols(obj); | 
|         const refSymbols = new Set(getOwnPropertySymbols(ref)); | 
|   | 
|         for (const key of objSymbols) { | 
|             if (!options.skip || | 
|                 !options.skip.includes(key)) { | 
|   | 
|                 if (hasOwnEnumerableProperty(obj, key)) { | 
|                     if (!hasOwnEnumerableProperty(ref, key)) { | 
|                         return false; | 
|                     } | 
|   | 
|                     if (!isDeepEqual(obj[key], ref[key], options, seen)) { | 
|                         return false; | 
|                     } | 
|                 } | 
|                 else if (hasOwnEnumerableProperty(ref, key)) { | 
|                     return false; | 
|                 } | 
|             } | 
|   | 
|             refSymbols.delete(key); | 
|         } | 
|   | 
|         for (const key of refSymbols) { | 
|             if (hasOwnEnumerableProperty(ref, key)) { | 
|                 return false; | 
|             } | 
|         } | 
|     } | 
|   | 
|     return true; | 
| }; | 
|   | 
|   | 
| internals.SeenEntry = class { | 
|   | 
|     constructor(obj, ref) { | 
|   | 
|         this.obj = obj; | 
|         this.ref = ref; | 
|     } | 
|   | 
|     isSame(obj, ref) { | 
|   | 
|         return this.obj === obj && this.ref === ref; | 
|     } | 
| }; |