Skip to content

Package overview

Stop guessing what changed. Get paths, patches, and review-ready output.

Typed deep comparison, change records, JSON Patch, DiffView, and optional engines for merge, query, and stats.

DiffResult is plain data. DiffView is DX. Merge and other engines are opt-in subpaths — not a CRDT / live sync runtime.

When to use

  • Dirty checks, audit trails, optimistic UI sync, RFC 6902 patches between clients
  • Collaborative drafts that need conflict-aware merge (/merge)
  • Filtering or summarizing an existing DiffResult (/query, /stats)

When not to use

  • Shallow equality on primitives or a single known key (=== / lodash isEqual may be enough)
  • Full CRDT / OT collaboration runtimes — this is snapshot merge + patch, not a live sync protocol
  • Binary blob / stream diffs — comparison walks structured values (objects, arrays, Map, Set, Date, RegExp, typed arrays), not arbitrary byte streams or file diffs

Features

  • diff / hasChanges / compare with typed change records and paths
  • RFC 6902 patch / applyPatch (root or /patch)
  • serialize to JSON, Markdown, table, HTML, console, human
  • DiffView (/view): fluent filter → explain → serialize → patch → stats
  • Opt-in engines: /merge, /query, /stats, /view, /plugins, /formatter

Install

bash
npm install @jayoncode/object-diff
bash
pnpm add @jayoncode/object-diff
bash
yarn add @jayoncode/object-diff

Example: detect changes and apply a patch

ts
import { diff, hasChanges, patch, applyPatch } from "@jayoncode/object-diff";

const before = { user: { name: "John", role: "viewer" }, active: true };
const after = { user: { name: "Jane", role: "admin" }, active: true };

if (hasChanges(before, after)) {
  const result = diff(before, after);
  const operations = patch(result);
  const synced = applyPatch(before, operations);
  // synced matches `after` for tracked paths
}

Use diff() for audit trails and inspectors; hasChanges() for dirty checks; patch() / applyPatch() to propagate updates between stores or clients.

Review with DiffView

ts
import { diff } from "@jayoncode/object-diff";
import { createDiffView } from "@jayoncode/object-diff/view";

const view = createDiffView(diff(before, after, { detectMoves: true }));
view.explain({ format: "human" }); // review text
view.serialize("markdown");
view.patch();

Slim entry: @jayoncode/object-diff/core (compare/diff only). Patch domain: @jayoncode/object-diff/patch. Fluent toolbox: @jayoncode/object-diff/view.

Inspect changes interactively →

Problem → approach

Typical painObject Diff
JSON.stringify(a) !== JSON.stringify(b) — no paths, no types, order-sensitivediff() returns typed change records with paths (user.name, items[2])
Hand-rolled deep equality and patch logic for optimistic UIpatch() / applyPatch() emit and apply RFC 6902 operations
Explaining what changed in PRs or logs requires custom formattingcreateDiffView(result).explain() / serialize()
Collaborative drafts need conflict-aware combine@jayoncode/object-diff/merge

Overview

Object Diff compares structured values — plain objects, arrays, Date, RegExp, Map, Set, and typed arrays, not just JSON-safe primitives — emits structured change records, and can serialize results or produce RFC 6902-style patch operations (add / remove / replace / move / copy / test).

APIPurpose
diff(a, b)Full change list with paths and types
hasChanges(a, b)Boolean dirty check without full diff cost
createDiffView(result)Fluent explain / filter / serialize / patch (/view)
patch(diffResult)Generate patch operations
applyPatch(target, ops)Immutable apply
serialize(diff, format)JSON, Markdown, table, and more

Optional engines: /merge, /query, /stats, /formatter, /plugins, /view — see Engines.

Root import of merge/query/stats/plugins/view is not supported — use the subpath.

Documentation path

Foundation

#GuideTopicsPlayground
1TutorialInstall, first diffLab
2Core conceptsSnapshots, changes, patchesLab / Diff

Core APIs

#GuideTopicsPlayground
3DiffingOptions, moves, filtersLab Moves tab / Diff
4PatchingRFC ops, validate, optimizeLab / Patch
5SerializationFormats + custom serializersLab / JSON

Engines

#GuideTopicsPlayground
6DX / DiffViewexplain, fluent toolboxLab Explain tab
7EnginesSubpath map
8MergeTwo-/three-way strategiesLab Merge tab
9QueryFilter existing DiffResults
10StatisticsMetrics, hot prefixes
11PluginsMatchers, formatters, hooks
12IntegrationsForms, session, auditExamples
13PerformanceComplexity, budgetsBenchmarks

Mental model (compare → changes → patch)

NeedPrefer
Boolean dirty checkhasChanges(a, b)
Structured change listdiff(a, b).changes
Equality of two valuescompare(a, b)
RFC 6902 operationspatch(diffResult) then applyPatch(target, ops)

Form Intelligence can call these via plugins / form.diffFrom* without coupling cores — FI composition recipe.

Package fit

RequirementAPI
Form/state dirty detectionhasChanges(a, b)
Structured audit logdiff() change records
Partial sync between clientspatch() + applyPatch()
Human-readable changelogscreateDiffView(...).explain() / serialize
Collaborative mergemerge from /merge
Slim compare-only bundle@jayoncode/object-diff/core

Reference

An ecosystem of independent, headless TypeScript libraries engineered for modern web development. Every package includes interactive playgrounds and documentation that evolves alongside the code.