{"id":40433474,"url":"https://github.com/ver0-project/deep-equal","last_synced_at":"2026-02-28T18:05:57.041Z","repository":{"id":36987019,"uuid":"446793474","full_name":"ver0-project/deep-equal","owner":"ver0-project","description":"🔎 Deep values comparator for JS ","archived":false,"fork":false,"pushed_at":"2026-02-25T12:46:20.000Z","size":1357,"stargazers_count":17,"open_issues_count":2,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2026-02-25T13:47:38.215Z","etag":null,"topics":["assert","deep-equal","javascript","value"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ver0-project.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-01-11T11:28:10.000Z","updated_at":"2026-02-24T22:51:16.000Z","dependencies_parsed_at":"2026-02-25T11:12:19.948Z","dependency_job_id":null,"html_url":"https://github.com/ver0-project/deep-equal","commit_stats":{"total_commits":179,"total_committers":5,"mean_commits":35.8,"dds":"0.11731843575418999","last_synced_commit":"d60f0eef811715bacae1cb1b215925ca90ca92f6"},"previous_names":["ver0-project/deep-equal"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/ver0-project/deep-equal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ver0-project%2Fdeep-equal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ver0-project%2Fdeep-equal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ver0-project%2Fdeep-equal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ver0-project%2Fdeep-equal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ver0-project","download_url":"https://codeload.github.com/ver0-project/deep-equal/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ver0-project%2Fdeep-equal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29943631,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-28T13:49:17.081Z","status":"ssl_error","status_checked_at":"2026-02-28T13:48:50.396Z","response_time":90,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assert","deep-equal","javascript","value"],"created_at":"2026-01-20T16:23:23.690Z","updated_at":"2026-02-28T18:05:57.030Z","avatar_url":"https://github.com/ver0-project.png","language":"TypeScript","readme":"\u003cdiv align=\"center\"\u003e\n\u003ch1\u003e@ver0/deep-equal\u003c/h1\u003e\n\n[![NPM Version](https://img.shields.io/npm/v/%40ver0%2Fdeep-equal?style=flat-square)](https://www.npmjs.com/package/@ver0/deep-equal)\n[![NPM Downloads](https://img.shields.io/npm/dm/%40ver0%2Fdeep-equal?style=flat-square)](https://www.npmjs.com/package/@ver0/deep-equal)\n[![Dependents (via libraries.io), scoped npm package](https://img.shields.io/librariesio/dependents/npm/%40ver0/deep-equal?style=flat-square)](https://www.npmjs.com/package/@ver0/deep-equal)\n[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ver0-project/deep-equal/ci.yml?style=flat-square)](https://github.com/ver0-project/deep-equal/actions)\n[![Codecov](https://img.shields.io/codecov/c/github/ver0-project/deep-equal?token=Y2K96S71RH\u0026style=flat-square)](https://app.codecov.io/gh/ver0-project/deep-equal)\n[![NPM Type Definitions](https://img.shields.io/npm/types/%40ver0%2Fdeep-equal?style=flat-square)](https://www.npmjs.com/package/@ver0/deep-equal)\n\n\u003cp\u003e\u003cbr/\u003e🔍 Deep values comparator for JavaScript\u003c/p\u003e\n\u003c/div\u003e\n\n### Features\n\n- ✅ Handles circular references correctly\n- ✅ Supports all modern JS data types\n- ✅ Correctly handles special cases like NaN comparisons\n- ✅ Lightweight with zero dependencies\n- ✅ Fast, while handling all cases\n\n### Why this package exists?\n\nWhen comparing objects in JavaScript, the built-in equality operators (`==` and `===`) only check for reference\nequality, not structural equality. This means that two objects with the same properties and values will be considered\ndifferent if they're not the same instance.\n\nMany deep equality solutions exist, but they often have limitations:\n\n- Some don't handle circular references\n- Some have inconsistent behavior with special values like NaN\n- Some don't support newer JavaScript features or types\n\nThis package aims to provide a comprehensive **single** solution that addresses all these concerns while maintaining\nexcellent performance.\n\n### Installation\n\n```bash\n# Using npm\nnpm install @ver0/deep-equal\n\n# Using yarn\nyarn add @ver0/deep-equal\n\n# Using pnpm\npnpm add @ver0/deep-equal\n```\n\n### How to use\n\nThe API is extremely simple - just import the `isEqual` function and use it to compare any two values:\n\n```javascript\nimport {isEqual} from '@ver0/deep-equal';\n\n// Comparing objects\nisEqual({a: 1, b: 2}, {a: 1, b: 2}); // true\nisEqual({a: 1, b: 2}, {a: 1, b: 3}); // false\n\n// Handling circular references\nconst obj1 = {a: 1};\nconst obj2 = {a: 1};\nobj1.self = obj1;\nobj2.self = obj2;\nisEqual(obj1, obj2); // true\n\n// Works with various data types and containers\nisEqual(new Date('2023-01-01'), new Date('2023-01-01')); // true\nisEqual(new Set([1, 2]), new Set([1, 2])); // true\nisEqual(new Map([['a', 1]]), new Map([['a', 1]])); // true\nisEqual(/abc/g, /abc/g); // true\n\n// Correctly handles special cases\nisEqual(NaN, NaN); // true\n```\n\n### Comparison Behavior\n\n#### Primitives and Special Values\n\nPrimitive values (numbers, strings, booleans, `undefined`, `null`) are compared using strict equality (`===`).\n\n`NaN` is a special case — JavaScript's `===` operator considers `NaN !== NaN`, but `isEqual` treats two `NaN` values\nas equal. This also applies to boxed NaN values: `isEqual(Object(NaN), Object(NaN))` returns `true`.\n\nBoxed primitives (`new Number()`, `new String()`, `new Boolean()`) are compared by their underlying value using\n`valueOf()`.\n\n#### Objects\n\nTwo objects are first checked for matching prototypes — if their prototypes differ, they are not equal.\n\nThen, both objects must have the same number of own enumerable string-keyed properties. Each property value is compared\nrecursively.\n\n**Symbol-keyed properties are not compared.** Symbols are designed to be non-enumerable identifiers — they serve as\nhidden metadata rather than data-carrying properties. Well-known symbols like `Symbol.iterator` define behavior, and\nframework-specific symbols like `$$typeof` are internal markers. Comparing them as data would contradict their intended\nrole in the language.\n\n#### Arrays\n\nArrays must have the same `length`. Each element is compared recursively by index.\n\n#### Sets\n\nA `Set` is an implementation of a mathematical set — an unordered collection of unique values. The fact that JavaScript\npreserves insertion order during iteration is a convenience of the specification, not a defining characteristic. A Set\nis not an array, and comparing it like one would be incorrect.\n\nSets must have the same `size`. Membership is checked using the Set's built-in `has()` method, which uses the\n[SameValueZero](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#same-value-zero_equality)\nalgorithm. Order is irrelevant.\n\n```javascript\n// Order does not matter\nisEqual(new Set([1, 2, 3]), new Set([3, 1, 2])); // true\n\n// Object references — compared by identity, not structure\nisEqual(new Set([{a: 1}]), new Set([{a: 1}])); // false\n```\n\n**Object elements are compared by reference, not by deep equality.** This follows from how the Set itself defines\nuniqueness. A Set uses SameValueZero to determine whether a value already exists — you can add 100 structurally\nidentical objects and the Set will hold all 100 as distinct elements. Deep-comparing elements would impose an identity\nmodel that contradicts the container's own semantics. `isEqual` respects the data structure's definition of membership\nrather than overriding it.\n\n#### Maps\n\nA `Map` is a key-value collection where keys are identified by SameValueZero — the same identity model as Sets. Just\nlike with Sets, this means object keys are distinct references, not interchangeable structures. Two structurally\nidentical objects used as Map keys are two different keys as far as the Map is concerned.\n\nMaps must have the same `size`. **Keys are compared by reference** using the Map's built-in `has()` method, while\n**values are deep-compared** recursively.\n\n```javascript\n// Primitive keys — works as expected\nisEqual(new Map([['a', {x: 1}]]), new Map([['a', {x: 1}]])); // true\n\n// Object keys — compared by identity, not structure\nconst k1 = {id: 1};\nconst k2 = {id: 1};\nisEqual(new Map([[k1, 'v']]), new Map([[k2, 'v']])); // false\n```\n\nThe reasoning is the same as for Sets — `isEqual` respects the Map's own definition of key identity rather than\noverriding it.\n\n#### Dates and Regular Expressions\n\nDates are compared by their timestamp value (`getTime()`). Regular expressions are compared by their `source` and\n`flags` properties.\n\n#### TypedArrays, DataViews, and ArrayBuffers\n\nAll binary data types — `ArrayBuffer`, `SharedArrayBuffer`, `DataView`, and all TypedArray variants — are compared at\nthe byte level using `Uint8Array`.\n\nFor TypedArrays and DataViews, only the viewed slice is compared. The `byteOffset` and `byteLength` of the view are\nrespected, so two views into the same underlying buffer that cover different regions are correctly identified as\ndifferent.\n\nByte-level comparison has a correctness advantage: it preserves NaN bit patterns. A `Float64Array([NaN])` compared\nelement-by-element would fail because `NaN !== NaN`, but comparing the underlying bytes works correctly.\n\n#### Custom Classes\n\nWhen two objects share the same prototype but don't match any of the built-in types above, `isEqual` checks whether\nthe class defines a custom `valueOf()` or `toString()` method.\n\nIf `valueOf()` is present, differs from `Object.prototype.valueOf`, and **both instances share the same function\nreference** (which is naturally true for prototype methods), the comparison is performed by calling `valueOf()` on each\ninstance and comparing the results. The same logic applies as a fallback to `toString()`.\n\nThis is a **terminal comparison** — if `valueOf()` or `toString()` is used, own properties are not checked afterward.\nThe rationale is that a class defining `valueOf()` is declaring \"this is my primitive representation,\" and that\nrepresentation is the basis for equality.\n\nClasses that don't define custom `valueOf()` or `toString()` fall through to property-by-property comparison, just like\nplain objects.\n\n#### Circular References\n\nCircular and cross-references between objects are handled correctly. Visited object pairs are tracked during recursion\nto detect cycles, so self-referential structures are compared without infinite loops.\n\n### Performance\n\nCheck out the benchmarks by running `yarn benchmark` in the project directory.\n\n\u003e Unlike most deep equality libraries, this package supports circular reference detection. This requires tracking\n\u003e visited object pairs during recursion, which adds overhead on recursive structures (objects, arrays, maps).\n\u003e\n\u003e To minimize this cost, the tracking mechanism is lazily allocated — comparisons of leaf types like dates, regular\n\u003e expressions, sets, and typed arrays have zero tracking overhead. The cost is only paid when recursion actually occurs.\n\u003e\n\u003e Rather than splitting into separate functions (one with cycle detection, one without), this package provides a single\n\u003e function that handles all cases. Simplicity in both the API and the implementation.\n\n### Supported Types\n\n- Primitive values (numbers, strings, booleans, undefined, null)\n- NaN (correctly compared to be equal to itself)\n- Boxed primitives (Number objects, String objects, Boolean objects)\n- Plain objects\n- Arrays\n- Sets\n- Maps\n- Regular Expressions\n- Date objects\n- ArrayBuffers\n- SharedArrayBuffers\n- DataViews\n- TypedArrays (Int8Array, Uint8Array, Float64Array, etc.)\n- Objects with null prototypes\n- Objects with custom `valueOf()` / `toString()`\n- Any objects with circular references\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fver0-project%2Fdeep-equal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fver0-project%2Fdeep-equal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fver0-project%2Fdeep-equal/lists"}