SkillAgentSearch skills...

Superdiff

Superdiff provides a rich and readable diff for arrays, objects, texts and coordinates. It supports stream and file inputs for handling large datasets efficiently, is battle-tested, has zero dependencies, and offers a top-tier performance.

Install / Use

/learn @DoneDeal0/Superdiff

README

<img width="1166" height="388" alt="superdiff logo" src="https://raw.githubusercontent.com/DoneDeal0/superdiff/main/assets/superdiff-banner.png" />

CI CD NPM Downloads GitHub Tag Sponsor

<hr/>

WHAT IS IT?

Superdiff provides a rich and readable diff for arrays, objects, texts and coordinates. It supports stream and file inputs for handling large datasets efficiently, is battle-tested, has zero dependencies, and offers a top-tier performance.

ℹ️ The documentation is also available on our website!

<hr/>

FEATURES

Superdiff exports 5 functions:

<hr/>

superdiff-stream-gif

<p align="center"> <sub>Superdiff smoothly comparing 1.8 million fictional votes in real time using <code>streamListDiff</code> in the browser</sub> </p> <hr/>

⚔ COMPETITORS

| Feature | superdiff | deep-object-diff | deep-diff | diff | microdiff | | ------------------------------ | --------- | ---------------- | --------- | --------- | --------- | | Object diff | ✅ | ✅ | ✅ | ❌ | ✅ | | List diff | ✅ | ❌ | ⚠️ | ❌ | ⚠️ | | Text diff | ✅ | ❌ | ✅ | ✅ | ❌ | | Coordinates diff | ✅ | ❌ | ❌ | ❌ | ❌ | | Streaming for huge datasets | ✅ | ❌ | ❌ | ❌ | ❌ | | Move detection | ✅ | ❌ | ❌ | ❌ | ❌ | | Output refinement | ✅ | ❌ | ❌ | ❌ | ❌ | | Zero dependencies | ✅ | ✅ | ❌ | ✅ | ✅ |

<sub>Some libraries perform array diffing using index-by-index comparison. This approach cannot reliably detect insertions, deletions, or moves correctly. In those cases, support is marked as partial.</sub>

📊 BENCHMARK

Environment: Node.js 24.12.0 (LTS) • MacBook Pro M2 (2023, Sequoia 15.1) • 16GB RAM.

Method: Warm up runs, then each script is executed 20 times, and we keep the median time. To minimize garbage collection and cross‑benchmark interference, all scenarios are run individually. All benchmark scripts are included so you can reproduce the results locally.

List diff

| Scenario | superdiff | arr-diff | deep-diff | | ------------------------- | ------------- | ---------- | --------- | | 10k items array | 1.84 ms | 32.95 ms | 4.74 ms | | 100k items array | 17.43 ms | 3363.15 ms | 50.36 ms |

Object diff

| Scenario | superdiff | deep-object-diff | deep-diff | microdiff | | ------------------------------ | --------- | ---------------- | --------- | ---------- | | 10k flat object keys | 2.27 ms | 2.44 ms | 39.37 ms | 2.24 ms| | 100k flat object keys | 29.23 ms | 31.86 ms | 3784.50 ms| 29.51 ms | | 100k nested nodes | 4.25 ms | 9.67 ms | 16.51 ms | 7.26 ms |

Text diff

| Scenario | superdiff | diff | | ----------------------- | ------------ | ---------- | | 10k words | 1.38 ms | 3.86 ms | | 100k words | 21.68 ms | 45.93 ms | | 10k sentences | 2.30 ms | 5.61 ms | | 100k sentences | 21.95 ms | 62.03 ms |

<sub>(Superdiff uses its normal accuracy settings to match diff's behavior)</sub>

👉 Despite providing a full structural diff with a richer output, Superdiff consistently outperforms or matches the fastest diff libraries. It also scales linearly, even with deeply nested data.

<hr/>

🤝 DONORS

I am grateful to the generous donors of Superdiff!

<div style="display: flex;">

<a href="https://github.com/AlexisAnzieu" target="_blank"><img alt="AlexisAnzieu" src="https://raw.githubusercontent.com/DoneDeal0/superdiff/main/assets/donor-anzieu.png" width="72px" height="72px"/></a> <a href="https://github.com/omonk" target="_blank"><img alt="omonk" src="https://raw.githubusercontent.com/DoneDeal0/superdiff/main/assets/donor-monk.png" width="72px" height="72px"/></a> <a href="https://github.com/sneko" target="_blank"><img alt="sneko" src="https://raw.githubusercontent.com/DoneDeal0/superdiff/main/assets/donor-sneko.png" width="72px" height="72px"/></a>

</div>

If you or your company uses this library, please show your support by becoming a sponsor! Your name and company logo will be displayed on the README.md. Premium support is also available.

<hr/>

getObjectDiff

import { getObjectDiff } from "@donedeal0/superdiff";

Compares two objects and returns a diff for each value and its possible subvalues. Supports deeply nested objects of any value type.

FORMAT

Input

prevData: Record<string, unknown>;
nextData: Record<string, unknown>;
options?: {
  ignoreArrayOrder?: boolean, // false by default,
  showOnly?: {
    statuses: ("added" | "deleted" | "updated" | "equal")[], // [] by default
    granularity?: "basic" | "deep" // "basic" by default
  }
}
  • prevData: the original object.
  • nextData: the new object.
  • options
    • ignoreArrayOrder: if true, ["hello", "world"] and ["world", "hello"] are considered equal, because the two arrays contain the same values, just in a different order.

    • showOnly: returns only the values whose status you are interested in. It takes two parameters:

      • statuses: status you want to see in the output (e.g. ["added", "equal"])
        • granularity:
          • basic returns only the main keys whose status matches your query.
          • deep can return main keys if some of their nested keys' status match your request. The nested keys are filtered accordingly.

Output

type ObjectDiff = {
  type: "object";
  status: "added" | "deleted" | "equal" | "updated";
  diff: Diff[];
};

type Diff = {
  key: string;
  value: unknown;
  previousValue: unknown;
  status: "added" | "deleted" | "equal" | "updated";
  // recursive diff in case of nested keys
  diff?: Diff[];
};

USAGE

Input

getObjectDiff(
  {
    id: 54,
    user: {
      name: "joe",
-     member: true,
-     hobbies: ["golf", "football"],
      age: 66,
    },
  },
  {
    id: 54,
    user: {
      name: "joe",
+     member: false,
+     hobbies: ["golf", "chess"],
      age: 66,
    },
  }
);

Output

{
      type: "object",
+     status: "updated",
      diff: [
        {
          key: "id",
          value: 54,
          previousValue: 54,
          status: "equal",
        },
        {
          key: "user",
          value: {
            name: "joe",
            member: false,
            hobbies: ["golf", "chess"],
            age: 66,
          },
          previousValue: {
            name: "joe",
            member: true,
            hobbies: ["golf", "football"],
            age: 66,
          },
+         status: "updated",
          diff: [
            {
              key: "name",
              value: "joe",
              previousValue: "joe",
              status: "equal",
            },
+           {
+             key: "member",
+             value: false,
+             previousValue: true,
+             status: "updated",
+           },
+           {
+             key: "hobbies",
+             value: ["golf", "chess"],
+             previousValue: ["golf", "football"],
+             status: "updated",
+           },
            {
              key: "age",
              value: 66,
              previousValue: 66,
              status: "equal",
            },
          ],
        },
      ],
    }
<hr/>

getListDiff

import { getListDiff } from "@donedeal0/superdiff";

Compares two arrays and returns a diff for each entry. Supports duplicate values, primitive values and objects.

FORMAT

Input

  prevList: T[];
  nextList: T[];
  options?: {
    showOnly?: ("added" | "deleted" | "moved" | "updated" | "equal")[], // [] by default
    referenceKey?: string, // "" by default
    ignoreArrayOrder?: boolean, // false by default,
    considerMoveAsUpdate?: boolean // false by default
  }
  • prevList: the original list.
  • nextList: the new list.
  • options
    • showOnly gives you the option to return only the values whose status you are interested in (e.g. ["added", "equal"]).
    • referenceKey will consider an object to be updated rather than added or deleted if one of its keys remains stable, such as its id. This option has no effect on other datatypes.
    • ignoreArrayOrder: if true, ["hello", "world"] and ["world", "hello"] are considered equal, because the two arrays contain the same values, just in a different order.
    • considerMoveAsUpdate: if true, a moved value is considered updated.

Output

type ListDiff = {
  type: "list";
  status: "added" | "
View on GitHub
GitHub Stars1.1k
CategoryCustomer
Updated7d ago
Forks8

Languages

TypeScript

Security Score

85/100

Audited on Mar 20, 2026

No findings