Metadata-Version: 2.4
Name: jdelta
Version: 0.1.0
Summary: Structural diff for two JSON files — see which values changed, by path, ignoring key order and whitespace. Zero dependencies.
Author: yyfjj
License: MIT
Project-URL: Homepage, https://github.com/jjdoor/jdelta-py
Project-URL: Repository, https://github.com/jjdoor/jdelta-py
Project-URL: Issues, https://github.com/jjdoor/jdelta-py/issues
Keywords: json,diff,delta,compare,structural,jsonpath,cli,ci,config,snapshot
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# 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.**

```bash
pip install jdelta

$ jdelta before.json after.json

Added (2)
  + email         "alice@corp.com"
  + 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  ~3
```

> This is the Python build. A behavior-equivalent Node build is on npm:
> `npx jdelta` (<https://github.com/jjdoor/jdelta>).

## Why

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

```bash
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 to `xs[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 (`1` and `1.0` are 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) or `1.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-code` won't
    fire). The **Python build keeps integer precision exactly** — prefer it for
    large-integer data.
- **Long values are abbreviated** in the human view (truncated with `…` past ~72
  characters). `--json` output is never truncated.

## `--json` shape

```json
{
  "added":   [{ "path": "email", "value": "alice@corp.com" }],
  "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
