jdelta
v0.1.0
Published
Structural diff for two JSON files — see which values changed, by path, ignoring key order and whitespace. Zero dependencies.
Downloads
122
Maintainers
Readme
jdelta
A structural diff for two JSON files. git diff and diff work on lines —
so reformatting, reordered keys, or a changed indent drown the one value you
actually care about in red-and-green noise. jdelta compares the data, not
the text, and tells you exactly which values changed, addressed by path. Zero
dependencies, no network.
$ npx jdelta before.json after.json
Added (2)
+ email "[email protected]"
+ user.tags[2] "d"
Removed (1)
- legacyId 1001
Changed (3)
~ user.age 30 → 31
~ user.role "viewer" → "admin"
~ user.tags[1] "b" → "c"
+2 -1 ~3Why
You're reviewing a config change, an API-response snapshot, a tsconfig, a
locale file — and the diff is unreadable because someone ran a formatter or the
serializer reordered keys. You don't care that line 40 moved to line 12; you
care that auth.required flipped to false. jdelta ignores key order and
whitespace entirely and reports the actual value-level delta by path.
Usage
jdelta old.json new.json # human-readable, grouped by added/removed/changed
jdelta old.json new.json --json # machine-readable
jdelta old.json new.json --quiet # just the +a -r ~c summary line
jdelta old.json new.json --exit-code # exit 1 if they differ (CI gate)Options
| Flag | Effect |
|------|--------|
| --json | Emit { added, removed, changed, summary } as JSON |
| --quiet | Print only a one-line summary (+a -r ~c, or no differences) |
| --exit-code | Exit 1 when the files differ (for CI gates) |
| -v, --version | Print version |
| -h, --help | Show help |
How it reads the diff
- Paths. Object keys use dot notation (
user.profile.age); array elements use index notation (items[2].price). Keys that aren't plain identifiers — containing dots, spaces or hyphens, or starting with a digit — fall back to quoted brackets:["order-id"],["123"]. - Added / Removed / Changed. A key present on only one side is added or
removed; a key on both with a different value is changed. A type change
(
number→string,object→array) is reported as a single changed entry tagged with the kinds — not as an add + remove. - Arrays are compared by index.
xs[2]is compared toxs[2]; a length change shows up as added/removed trailing elements. (Inserting at the front of an array therefore reads as "everything shifted" — index diffing is simple and predictable rather than guessing at moves.) - Numbers are compared by value (
1and1.0are equal). Two caveats follow from how each runtime parses JSON numbers:- Float display. Floats are rendered by each runtime's native serializer, so
an integral float can show as
1(Node) or1.0(Python), and exotic floats (e.g.1e-7) may differ in formatting. The detected change is the same. - Large integers. JavaScript has no bigint in JSON, so the Node build parses
every number as an IEEE-754 double: integers beyond ±2^53 (snowflake IDs,
int64 database keys) lose precision and can compare equal when they aren't —
so the Node build may miss a change to such a value (and
--exit-codewon't fire). The Python build keeps integer precision exactly — prefer it for large-integer data.
- Float display. Floats are rendered by each runtime's native serializer, so
an integral float can show as
- Long values are abbreviated in the human view (truncated with
…past ~72 characters).--jsonoutput is never truncated.
--json shape
{
"added": [{ "path": "email", "value": "[email protected]" }],
"removed": [{ "path": "legacyId", "value": 1001 }],
"changed": [{ "path": "user.age", "from": 30, "to": 31 }],
"summary": { "added": 1, "removed": 1, "changed": 1, "total": 3 }
}Exit codes
| Code | Meaning |
|------|---------|
| 0 | success (default — even when the files differ) |
| 1 | files differ and --exit-code was passed |
| 2 | error (bad args, unreadable file, invalid JSON) |
By default jdelta is a viewer and exits 0; add --exit-code to make it a
gate (the git diff --exit-code convention).
License
MIT
