Skip to content

AdametherzLab/json-diff-ts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

json-diff-ts

CI TypeScript License: MIT

🔍 json-diff-ts

Compare, track, and patch JSON like a boss — TypeScript-first and zero dependencies.

One-liner

A blazing-fast TypeScript utility that diffs any two JSON values, gives you a clean list of changes (added, removed, modified), and lets you apply those changes back — with full support for nested objects and arrays.

Features

  • Deep diffing — recursively compare nested objects and arrays
  • Type-safe — full TypeScript support with strict mode
  • Patch application — apply changes back to transform JSON
  • Array support — diff arrays by index or identity keys
  • Zero deps — no external packages, just pure TypeScript
  • ESM-first — modern module system with CJS compatibility

Installation

npm install @adametherzlab/json-diff-ts

Or with Bun:

bun add @adametherzlab/json-diff-ts

Quick Start

// REMOVED external import: import { diff, patch } from "@adametherzlab/json-diff-ts";

const before = { name: "Alice", age: 30, city: "NYC" };
const after = { name: "Alice", age: 31, city: "LA", country: "USA" };

const result = diff(before, after);
console.log(result.changes);
// [
//   { path: ["age"], type: "changed", oldValue: 30, newValue: 31 },
//   { path: ["city"], type: "changed", oldValue: "NYC", newValue: "LA" },
//   { path: ["country"], type: "added", newValue: "USA" }
// ]

// Apply changes to get the new state
const patched = patch(before, result.patches);
console.log(patched);
// { name: "Alice", age: 31, city: "LA", country: "USA" }

API Reference

Types

ChangeType (enum)

enum ChangeType {
  Added = "added",
  Removed = "removed",
  Changed = "changed"
}

Change (type)

type Change = AddedChange | RemovedChange | ChangedChange;

AddedChange

interface AddedChange {
  readonly type: "added";
  readonly path: readonly string[];
  readonly newValue: JsonValue;
}

RemovedChange

interface RemovedChange {
  readonly type: "removed";
  readonly path: readonly string[];
  readonly oldValue: JsonValue;
}

ChangedChange

interface ChangedChange {
  readonly type: "changed";
  readonly path: readonly string[];
  readonly oldValue: JsonValue;
  readonly newValue: JsonValue;
}

JsonValue (type)

type JsonValue = JsonPrimitive | JsonArray | JsonObject;

JsonPrimitive (type)

JSON primitive types.

type JsonPrimitive = string | number | boolean | null;

JsonArray (interface)

JSON array type — array of JsonValue.

interface JsonArray extends ReadonlyArray<JsonValue> {}

JsonObject (interface)

JSON object type — record with string keys and JsonValue values.

interface JsonObject extends Readonly<Record<string, JsonValue>> {}

DiffOptions (interface)

interface DiffOptions {
  /** Compare arrays by identity key instead of index. Default: false */
  readonly arrayIdentityKey?: string;
  /** Treat arrays as ordered (index-based). Default: true */
  readonly orderedArrays?: boolean;
  /** Max depth for comparison. Default: unlimited */
  readonly maxDepth?: number;
}

PatchOptions (interface)

interface PatchOptions {
  /** Mutate the original object instead of returning a copy. Default: false */
  readonly mutate?: boolean;
}

DiffResult (interface)

interface DiffResult {
  /** List of all changes detected */
  readonly changes: readonly Change[];
  /** List of patch operations derived from changes */
  readonly patches: readonly Patch[];
  /** Number of additions */
  readonly added: number;
  /** Number of removals */
  readonly removed: number;
  /** Number of modifications */
  readonly changed: number;
  /** Total number of changes */
  readonly total: number;
}

Patch (type)

A patch operation that can be applied to transform JSON.

type Patch = AddPatch | RemovePatch | ReplacePatch;

AddPatch

interface AddPatch {
  readonly op: "add";
  readonly path: readonly string[];
  readonly value: JsonValue;
}

RemovePatch

interface RemovePatch {
  readonly op: "remove";
  readonly path: readonly string[];
}

ReplacePatch

interface ReplacePatch {
  readonly op: "replace";
  readonly path: readonly string[];
  readonly value: JsonValue;
}

Functions

diff(oldVal, newVal, options?)

function diff(oldVal: JsonValue, newVal: JsonValue, options?: DiffOptions): DiffResult

Parameters:

  • oldVal — The original JSON value
  • newVal — The new JSON value to compare against
  • options — Optional configuration for diff behavior

Returns: DiffResult containing all changes and statistics

Example:

const result = diff({ a: 1 }, { a: 2 });
console.log(result.changes); // [{ path: ["a"], type: "changed", oldValue: 1, newValue: 2 }]

diffObjects(oldObj, newObj, options?)

Diff two objects, detecting added, removed, and changed properties.

function diffObjects(oldObj: JsonObject, newObj: JsonObject, options?: DiffOptions): DiffResult

Example:

const result = diffObjects({ x: 1 }, { x: 2, y: 3 });

diffArrays(oldArr, newArr, options?)

function diffArrays(oldArr: JsonArray, newArr: JsonArray, options?: DiffOptions): DiffResult

Example:

const result = diffArrays([1, 2], [1, 3]);

parsePath(pathString)

function parsePath(pathString: string): readonly string[]

Example:

parsePath("a.b.c") // ["a", "b", "c"]
parsePath("a[0].b") // ["a", "0", "b"]
parsePath('a["0"].b') // ["a", "0", "b"]

buildPath(segments)

function buildPath(segments: readonly string[]): string

Example:

buildPath(["a", "b", "c"]) // "a.b.c"
buildPath(["a", "0", "b"]) // "a[0].b"

deepEqual(a, b)

function deepEqual(a: JsonValue, b: JsonValue): boolean

Example:

deepEqual({ a: 1 }, { a: 1 }) // true
deepEqual([1, 2], [1, 2]) // true
deepEqual({ a: 1 }, { a: 2 }) // false

patch(value, patches, options?)

function patch(value: JsonValue, patches: readonly Patch[], options?: PatchOptions): JsonValue

Parameters:

  • value — The JsonValue to patch
  • patches — Array of patches to apply
  • options — Patch options

Returns: Patched JsonValue (new copy if mutate is false, same reference if true)

Example:

const original = { a: 1 };
const patched = patch(original, [{ path: ["a"], op: "replace", value: 2 }]);
// original is unchanged, patched is { a: 2 }

applyChanges(value, changes, options?)

function applyChanges(value: JsonValue, changes: readonly Change[], options?: PatchOptions): JsonValue

Example:

const original = { a: 1 };
const changes = diff(original, { a: 2 });
const patched = applyChanges(original, changes);

Advanced Usage

Array Diffing by Identity

// REMOVED external import: import { diff } from "@adametherzlab/json-diff-ts";

const usersBefore = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" }
];

const usersAfter = [
  { id: 1, name: "Alice Updated" },
  { id: 3, name: "Charlie" },
  { id: 2, name: "Bob" }
];

const result = diff(usersBefore, usersAfter, { arrayIdentityKey: "id" });
console.log(result.changes);
// Detects: name change for id=1, addition of id=3, keeps id=2 in same position

Path Manipulation

// REMOVED external import: import { parsePath, buildPath, patch } from "@adametherzlab/json-diff-ts";

const pathStr = "users[0].profile.settings.notifications";
const segments = parsePath(pathStr);
// ["users", "0", "profile", "settings", "notifications"]

const restored = buildPath(segments);
// "users[0].profile.settings.notifications"

// Use segments directly in patches
const data = { users: [{ profile: { settings: { notifications: true } } }] };
const patched = patch(data, [
  { op: "replace", path: segments, value: false }
]);

Mutate In-Place

For performance-critical scenarios where memory allocation matters:

// REMOVED external import: import { diff, patch } from "@adametherzlab/json-diff-ts";

const largeObject = { /* big data */ };
const updates = { /* changes */ };

const result = diff(largeObject, updates);
patch(largeObject, result.patches, { mutate: true });
// Modifies largeObject directly, no copy allocation

Contributing

See CONTRIBUTING.md

License

MIT (c) AdametherzLab

About

JSON diff — compare objects, list changes, apply patches

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors