{"version":3,"file":"isDeepEqual.cjs","names":["purry"],"sources":["../src/isDeepEqual.ts"],"sourcesContent":["import { purry } from \"./purry\";\n\n/**\n * Performs a *deep structural* comparison between two values to determine if\n * they are equivalent. For primitive values this is equivalent to `===`, for\n * arrays the check would be performed on every item recursively, in order, and\n * for objects all props will be compared recursively.\n *\n * The built-in Date and RegExp are special-cased and will be compared by their\n * values. Objects with a `null` prototype are comparable to plain objects by\n * their enumerable properties.\n *\n * !IMPORTANT: TypedArrays and symbol properties of objects are not supported\n * right now and might result in unexpected behavior. Please open an issue in\n * the Remeda github project if you need support for these types.\n *\n * The result would be narrowed to the second value so that the function can be\n * used as a type guard.\n *\n * See:\n * - `isStrictEqual` if you don't need a deep comparison and just want to\n * check for simple (`===`, `Object.is`) equality.\n * - `isShallowEqual` if you need to compare arrays and objects \"by-value\" but\n * don't want to recurse into their values.\n *\n * @param data - The first value to compare.\n * @param other - The second value to compare.\n * @signature\n *    isDeepEqual(data, other)\n * @example\n *    isDeepEqual(1, 1) //=> true\n *    isDeepEqual(1, '1') //=> false\n *    isDeepEqual([1, 2, 3], [1, 2, 3]) //=> true\n * @dataFirst\n * @category Guard\n */\nexport function isDeepEqual<T, S extends T>(\n  data: T,\n  other: T extends Exclude<T, S> ? S : never,\n): data is S;\nexport function isDeepEqual<T>(data: T, other: T): boolean;\n\n/**\n * Performs a *deep structural* comparison between two values to determine if\n * they are equivalent. For primitive values this is equivalent to `===`, for\n * arrays the check would be performed on every item recursively, in order, and\n * for objects all props will be compared recursively.\n *\n * The built-in Date and RegExp are special-cased and will be compared by their\n * values. Objects with a `null` prototype are comparable to plain objects by\n * their enumerable properties.\n *\n * !IMPORTANT: TypedArrays and symbol properties of objects are not supported\n * right now and might result in unexpected behavior. Please open an issue in\n * the Remeda github project if you need support for these types.\n *\n * The result would be narrowed to the second value so that the function can be\n * used as a type guard.\n *\n * See:\n * - `isStrictEqual` if you don't need a deep comparison and just want to\n * check for simple (`===`, `Object.is`) equality.\n * - `isShallowEqual` if you need to compare arrays and objects \"by-value\" but\n * don't want to recurse into their values.\n *\n * @param other - The second value to compare.\n * @signature\n *    isDeepEqual(other)(data)\n * @example\n *    pipe(1, isDeepEqual(1)); //=> true\n *    pipe(1, isDeepEqual('1')); //=> false\n *    pipe([1, 2, 3], isDeepEqual([1, 2, 3])); //=> true\n * @dataLast\n * @category Guard\n */\nexport function isDeepEqual<T, S extends T>(\n  other: T extends Exclude<T, S> ? S : never,\n): (data: T) => data is S;\nexport function isDeepEqual<T>(other: T): (data: T) => boolean;\n\nexport function isDeepEqual(...args: readonly unknown[]): unknown {\n  return purry(isDeepEqualImplementation, args);\n}\n\nfunction isDeepEqualImplementation<T>(data: unknown, other: T): data is T {\n  if (data === other) {\n    return true;\n  }\n\n  if (Object.is(data, other)) {\n    // We want to ignore the slight differences between `===` and `Object.is` as\n    // both of them largely define equality from a semantic point-of-view.\n    return true;\n  }\n\n  if (typeof data !== \"object\" || typeof other !== \"object\") {\n    return false;\n  }\n\n  if (data === null || other === null) {\n    return false;\n  }\n\n  if (!isComparablePrototype(data, other)) {\n    // If the objects don't share a prototype it's unlikely that they are\n    // semantically equal. It is technically possible to build 2 prototypes that\n    // act the same but are not equal (at the reference level, checked via\n    // `===`) and then create 2 objects that are equal although we would fail on\n    // them. Because this is so unlikely, the optimization we gain here for the\n    // rest of the function by assuming that `other` is of the same type as\n    // `data` is more than worth it.\n    return false;\n  }\n\n  if (Array.isArray(data)) {\n    return isDeepEqualArrays(data, other as unknown as readonly unknown[]);\n  }\n\n  if (data instanceof Map) {\n    return isDeepEqualMaps(data, other as unknown as Map<unknown, unknown>);\n  }\n\n  if (data instanceof Set) {\n    return isDeepEqualSets(data, other as unknown as Set<unknown>);\n  }\n\n  if (data instanceof Date) {\n    return data.getTime() === (other as unknown as Date).getTime();\n  }\n\n  if (data instanceof RegExp) {\n    return data.toString() === (other as unknown as RegExp).toString();\n  }\n\n  // At this point we only know that the 2 objects have a comparable prototype and are not\n  // any of the previous types. They could be plain objects (Object.prototype),\n  // they could be classes, they could be other built-ins, or they could be\n  // something weird. We assume that comparing values by keys is enough to judge\n  // their equality.\n\n  if (Object.keys(data).length !== Object.keys(other).length) {\n    return false;\n  }\n\n  for (const [key, value] of Object.entries(data)) {\n    if (!(key in other)) {\n      return false;\n    }\n\n    if (\n      !isDeepEqualImplementation(\n        value,\n        // @ts-expect-error [ts7053] - We already checked that `other` has `key`\n        other[key],\n      )\n    ) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nfunction isComparablePrototype(data: object, other: object): boolean {\n  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- This is a low-level check, we can't avoid it being typed as `any`.\n  const dataPrototype = Object.getPrototypeOf(data);\n  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- This is a low-level check, we can't avoid it being typed as `any`.\n  const otherPrototype = Object.getPrototypeOf(other);\n\n  if (dataPrototype === otherPrototype) {\n    return true;\n  }\n\n  if (dataPrototype === null) {\n    return otherPrototype === Object.prototype;\n  }\n\n  return dataPrototype === Object.prototype && otherPrototype === null;\n}\n\nfunction isDeepEqualArrays(\n  data: readonly unknown[],\n  other: readonly unknown[],\n): boolean {\n  if (data.length !== other.length) {\n    return false;\n  }\n\n  for (const [index, item] of data.entries()) {\n    if (!isDeepEqualImplementation(item, other[index])) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nfunction isDeepEqualMaps(\n  data: ReadonlyMap<unknown, unknown>,\n  other: ReadonlyMap<unknown, unknown>,\n): boolean {\n  if (data.size !== other.size) {\n    return false;\n  }\n\n  for (const [key, value] of data.entries()) {\n    if (!other.has(key)) {\n      return false;\n    }\n\n    if (!isDeepEqualImplementation(value, other.get(key))) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nfunction isDeepEqualSets(\n  data: ReadonlySet<unknown>,\n  other: ReadonlySet<unknown>,\n): boolean {\n  if (data.size !== other.size) {\n    return false;\n  }\n\n  // To ensure we only count each item once we need to \"remember\" which items of\n  // the other set we've already matched against. We do this by creating a copy\n  // of the other set and removing items from it as we find them in the data\n  // set.\n  const otherCopy = [...other];\n\n  for (const dataItem of data) {\n    let isFound = false;\n\n    for (const [index, otherItem] of otherCopy.entries()) {\n      if (isDeepEqualImplementation(dataItem, otherItem)) {\n        isFound = true;\n        otherCopy.splice(index, 1);\n        break;\n      }\n    }\n\n    if (!isFound) {\n      return false;\n    }\n  }\n\n  return true;\n}\n"],"mappings":"kGAgFA,SAAgB,EAAY,GAAG,EAAmC,CAChE,OAAOA,EAAAA,MAAM,EAA2B,CAAI,CAC9C,CAEA,SAAS,EAA6B,EAAe,EAAqB,CAKxE,GAJI,IAAS,GAIT,OAAO,GAAG,EAAM,CAAK,EAGvB,MAAO,GAWT,GARI,OAAO,GAAS,UAAY,OAAO,GAAU,UAI7C,IAAS,MAAQ,IAAU,MAI3B,CAAC,EAAsB,EAAM,CAAK,EAQpC,MAAO,GAGT,GAAI,MAAM,QAAQ,CAAI,EACpB,OAAO,EAAkB,EAAM,CAAsC,EAGvE,GAAI,aAAgB,IAClB,OAAO,EAAgB,EAAM,CAAyC,EAGxE,GAAI,aAAgB,IAClB,OAAO,EAAgB,EAAM,CAAgC,EAG/D,GAAI,aAAgB,KAClB,OAAO,EAAK,QAAQ,IAAO,EAA0B,QAAQ,EAG/D,GAAI,aAAgB,OAClB,OAAO,EAAK,SAAS,IAAO,EAA4B,SAAS,EASnE,GAAI,OAAO,KAAK,CAAI,CAAC,CAAC,SAAW,OAAO,KAAK,CAAK,CAAC,CAAC,OAClD,MAAO,GAGT,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAAI,EAK5C,GAJI,EAAE,KAAO,IAKX,CAAC,EACC,EAEA,EAAM,EACR,EAEA,MAAO,GAIX,MAAO,EACT,CAEA,SAAS,EAAsB,EAAc,EAAwB,CAEnE,IAAM,EAAgB,OAAO,eAAe,CAAI,EAE1C,EAAiB,OAAO,eAAe,CAAK,EAUlD,OARI,IAAkB,EACb,GAGL,IAAkB,KACb,IAAmB,OAAO,UAG5B,IAAkB,OAAO,WAAa,IAAmB,IAClE,CAEA,SAAS,EACP,EACA,EACS,CACT,GAAI,EAAK,SAAW,EAAM,OACxB,MAAO,GAGT,IAAK,GAAM,CAAC,EAAO,KAAS,EAAK,QAAQ,EACvC,GAAI,CAAC,EAA0B,EAAM,EAAM,EAAM,EAC/C,MAAO,GAIX,MAAO,EACT,CAEA,SAAS,EACP,EACA,EACS,CACT,GAAI,EAAK,OAAS,EAAM,KACtB,MAAO,GAGT,IAAK,GAAM,CAAC,EAAK,KAAU,EAAK,QAAQ,EAKtC,GAJI,CAAC,EAAM,IAAI,CAAG,GAId,CAAC,EAA0B,EAAO,EAAM,IAAI,CAAG,CAAC,EAClD,MAAO,GAIX,MAAO,EACT,CAEA,SAAS,EACP,EACA,EACS,CACT,GAAI,EAAK,OAAS,EAAM,KACtB,MAAO,GAOT,IAAM,EAAY,CAAC,GAAG,CAAK,EAE3B,IAAK,IAAM,KAAY,EAAM,CAC3B,IAAI,EAAU,GAEd,IAAK,GAAM,CAAC,EAAO,KAAc,EAAU,QAAQ,EACjD,GAAI,EAA0B,EAAU,CAAS,EAAG,CAClD,EAAU,GACV,EAAU,OAAO,EAAO,CAAC,EACzB,KACF,CAGF,GAAI,CAAC,EACH,MAAO,EAEX,CAEA,MAAO,EACT"}