jsonl-diff 0.1.5__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jsonl_diff-0.2.2/PKG-INFO +414 -0
- jsonl_diff-0.2.2/README.md +358 -0
- jsonl_diff-0.2.2/jsonl_diff.egg-info/PKG-INFO +414 -0
- {jsonl_diff-0.1.5 → jsonl_diff-0.2.2}/jsonl_diff.egg-info/SOURCES.txt +1 -0
- jsonl_diff-0.2.2/jsonl_diff.egg-info/requires.txt +4 -0
- {jsonl_diff-0.1.5 → jsonl_diff-0.2.2}/jsonl_diff.py +429 -396
- {jsonl_diff-0.1.5 → jsonl_diff-0.2.2}/pyproject.toml +11 -14
- {jsonl_diff-0.1.5 → jsonl_diff-0.2.2}/tests/test_cli.py +39 -3
- {jsonl_diff-0.1.5 → jsonl_diff-0.2.2}/tests/test_diff.py +134 -8
- jsonl_diff-0.2.2/tests/test_result.py +413 -0
- {jsonl_diff-0.1.5 → jsonl_diff-0.2.2}/tests/test_sources.py +266 -293
- jsonl_diff-0.1.5/PKG-INFO +0 -345
- jsonl_diff-0.1.5/README.md +0 -287
- jsonl_diff-0.1.5/jsonl_diff.egg-info/PKG-INFO +0 -345
- jsonl_diff-0.1.5/jsonl_diff.egg-info/requires.txt +0 -8
- {jsonl_diff-0.1.5 → jsonl_diff-0.2.2}/LICENSE +0 -0
- {jsonl_diff-0.1.5 → jsonl_diff-0.2.2}/jsonl_diff.egg-info/dependency_links.txt +0 -0
- {jsonl_diff-0.1.5 → jsonl_diff-0.2.2}/jsonl_diff.egg-info/entry_points.txt +0 -0
- {jsonl_diff-0.1.5 → jsonl_diff-0.2.2}/jsonl_diff.egg-info/top_level.txt +0 -0
- {jsonl_diff-0.1.5 → jsonl_diff-0.2.2}/setup.cfg +0 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jsonl-diff
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: A lightweight tool for comparing large JSONL/NDJSON datasets without loading them entirely into memory.
|
|
5
|
+
Author-email: rmoralespp <rmoralespp@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Rolando Morales Perez
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Source, https://github.com/rmoralespp/jsonl-diff
|
|
29
|
+
Project-URL: Homepage, https://github.com/rmoralespp/jsonl-diff
|
|
30
|
+
Project-URL: Changelog, https://github.com/rmoralespp/jsonl-diff/blob/main/CHANGELOG.md
|
|
31
|
+
Project-URL: Issues, https://github.com/rmoralespp/jsonl-diff/issues
|
|
32
|
+
Project-URL: Documentation, https://rmoralespp.github.io/jsonl-diff/
|
|
33
|
+
Keywords: jsonlines,ndjson,jsonl,json,gzip,bzip2,xz,zst,utilities,serialization,deserialization,diff,json-diff,cli
|
|
34
|
+
Classifier: Programming Language :: Python
|
|
35
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
42
|
+
Classifier: Intended Audience :: Developers
|
|
43
|
+
Classifier: Intended Audience :: Information Technology
|
|
44
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
45
|
+
Classifier: Topic :: Internet
|
|
46
|
+
Classifier: Topic :: Utilities
|
|
47
|
+
Classifier: Topic :: File Formats :: JSON
|
|
48
|
+
Requires-Python: >=3.10
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
License-File: LICENSE
|
|
51
|
+
Requires-Dist: ijson>=3.6.0
|
|
52
|
+
Requires-Dist: py-jsonl==1.5.4
|
|
53
|
+
Requires-Dist: jmespath>=1.1.0
|
|
54
|
+
Requires-Dist: msgspec>=0.22.0
|
|
55
|
+
Dynamic: license-file
|
|
56
|
+
|
|
57
|
+
<div align="center">
|
|
58
|
+
|
|
59
|
+
# jsonl-diff
|
|
60
|
+
|
|
61
|
+
Compare large JSONL, NDJSON, and top-level JSON arrays by record identity—not
|
|
62
|
+
by line position—without loading either complete dataset into memory. Strict,
|
|
63
|
+
deterministic, and disk-backed.
|
|
64
|
+
|
|
65
|
+
[](https://pypi.org/project/jsonl-diff/)
|
|
66
|
+
[](https://pypi.org/project/jsonl-diff/)
|
|
67
|
+
[](https://github.com/rmoralespp/jsonl-diff/actions/workflows/ci.yml)
|
|
68
|
+
[](https://codecov.io/gh/rmoralespp/jsonl-diff)
|
|
69
|
+
[](https://github.com/rmoralespp/jsonl-diff/blob/main/LICENSE)
|
|
70
|
+
|
|
71
|
+
[Install](#install) · [Quick start](#quick-start) · [CLI](#cli) · [Python API](#python-api) · [Semantics](#comparison-semantics) · [Documentation](#documentation)
|
|
72
|
+
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
`jsonl-diff` reconciles two datasets using one or more top-level identity
|
|
78
|
+
fields. Record order does not matter. It reports what is equal, added, deleted,
|
|
79
|
+
modified, or duplicated while preserving the original OLD and NEW locations.
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
## Why jsonl-diff
|
|
83
|
+
|
|
84
|
+
| | |
|
|
85
|
+
|-----------------------------|--------------------------------------------------------------------------------------------------|
|
|
86
|
+
| **Scales beyond memory** | Streams both inputs and keeps the comparison index in SQLite. |
|
|
87
|
+
| **Matches real datasets** | Compares records by single or composite identity instead of line number. |
|
|
88
|
+
| **Stays deterministic** | Produces stable summaries, change order, and machine-readable details. |
|
|
89
|
+
| **Treats data strictly** | Rejects malformed input, invalid identities, and duplicates by default. |
|
|
90
|
+
| **Uses all available work** | Indexes OLD and NEW concurrently in isolated worker processes. |
|
|
91
|
+
| **Fits pipelines** | Supports filtering, ignored fields, compressed sources, schema drift, and meaningful exit codes. |
|
|
92
|
+
|
|
93
|
+
## Install
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pip install jsonl-diff
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Requires **Python 3.10+**.
|
|
100
|
+
|
|
101
|
+
## Quick start
|
|
102
|
+
|
|
103
|
+
Given `old.jsonl`:
|
|
104
|
+
|
|
105
|
+
```jsonl
|
|
106
|
+
{"id":1,"name":"Ada"}
|
|
107
|
+
{"id":2,"name":"Grace"}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
and `new.jsonl`:
|
|
111
|
+
|
|
112
|
+
```jsonl
|
|
113
|
+
{"id":2,"name":"Grace Hopper"}
|
|
114
|
+
{"name":"Ada","id":1}
|
|
115
|
+
{"id":3,"name":"Linus"}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
compare by `id`:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
jsonl-diff old.jsonl new.jsonl --key id
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
Records:
|
|
126
|
+
equal: 1
|
|
127
|
+
added: 1
|
|
128
|
+
deleted: 0
|
|
129
|
+
modified: 1
|
|
130
|
+
OLD duplicates: 0
|
|
131
|
+
NEW duplicates: 0
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Property order and record order do not affect the result.
|
|
135
|
+
|
|
136
|
+
## Core capabilities
|
|
137
|
+
|
|
138
|
+
- Single or composite top-level identities.
|
|
139
|
+
- Incremental JSONL/NDJSON and top-level JSON-array parsing.
|
|
140
|
+
- Disk-backed, parallel indexing for large datasets.
|
|
141
|
+
- Exact RFC 6901 ignore pointers.
|
|
142
|
+
- JMESPath record filtering.
|
|
143
|
+
- Configurable duplicate handling: `error`, `first`, or `last`.
|
|
144
|
+
- Representation-sensitive numeric comparison.
|
|
145
|
+
- Observed-schema drift for fields, types, nullability, and requiredness.
|
|
146
|
+
- Deterministic JSONL details output.
|
|
147
|
+
- Local, HTTP/HTTPS, file-like, and compressed sources.
|
|
148
|
+
- CLI and Python API backed by the same comparison engine.
|
|
149
|
+
|
|
150
|
+
## CLI
|
|
151
|
+
|
|
152
|
+
```text
|
|
153
|
+
jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
|
|
154
|
+
[--duplicates {error,first,last}] [--missing-key {error,null}]
|
|
155
|
+
[--details FILE] [--quiet]
|
|
156
|
+
[--schema-diff] [--schema-ignore POINTER] [--format {jsonl,json}]
|
|
157
|
+
old new
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
| Option | Purpose |
|
|
161
|
+
|---------------------------|-----------------------------------------------------------------------|
|
|
162
|
+
| `old`, `new` | Local path, HTTP/HTTPS source, or `-` for stdin |
|
|
163
|
+
| `--key KEY` | Top-level identity field; repeat or comma-separate for composite keys |
|
|
164
|
+
| `--ignore POINTER` | RFC 6901 field pointer excluded from content comparison |
|
|
165
|
+
| `--where EXPRESSION` | JMESPath filter evaluated against each record |
|
|
166
|
+
| `--duplicates POLICY` | Reject duplicates or retain the `first`/`last` occurrence |
|
|
167
|
+
| `--missing-key POLICY` | Reject absent identity fields or treat them as `null` |
|
|
168
|
+
| `--details FILE` | Write the JSONL change report; must not be the OLD/NEW file |
|
|
169
|
+
| `--schema-diff` | Compare observed fields, types, nullability, and requiredness |
|
|
170
|
+
| `--schema-ignore POINTER` | Exclude a pointer from observed-schema profiling |
|
|
171
|
+
| `--format FORMAT` | Read `jsonl` (default) or one top-level JSON array with `json` |
|
|
172
|
+
| `--quiet` | Suppress the human-readable summary |
|
|
173
|
+
|
|
174
|
+
### Common workflows
|
|
175
|
+
|
|
176
|
+
**Composite identity**
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
jsonl-diff old.jsonl new.jsonl --key country,customerId,type
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Ignore volatile fields**
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
jsonl-diff old.jsonl new.jsonl \
|
|
186
|
+
--key id \
|
|
187
|
+
--ignore /updated_at \
|
|
188
|
+
--ignore /metadata/request_id
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Filter participating records**
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
jsonl-diff old.jsonl new.jsonl \
|
|
195
|
+
--key id \
|
|
196
|
+
--where 'deleted_at == `null`'
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Allow a missing composite-key component**
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
jsonl-diff old.jsonl new.jsonl \
|
|
203
|
+
--key country,customerId \
|
|
204
|
+
--missing-key null
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Detect observed-schema drift**
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
jsonl-diff old.jsonl new.jsonl \
|
|
211
|
+
--key id \
|
|
212
|
+
--schema-diff \
|
|
213
|
+
--schema-ignore /metadata
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Write a machine-readable change log**
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
jsonl-diff old.jsonl new.jsonl \
|
|
220
|
+
--key id \
|
|
221
|
+
--details changes.jsonl \
|
|
222
|
+
--quiet
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Using the files from the quick start, `changes.jsonl` contains:
|
|
226
|
+
|
|
227
|
+
```jsonl
|
|
228
|
+
{"duplicates":"error","ignore":[],"key":["id"],"missing_key":"error","type":"meta","where":null}
|
|
229
|
+
{"key":[2],"new_line":1,"old_line":2,"op":"modified","type":"change"}
|
|
230
|
+
{"key":[3],"new_line":3,"op":"added","type":"change"}
|
|
231
|
+
{"added":1,"deleted":0,"equal":1,"modified":1,"new_duplicates":0,"old_duplicates":0,"type":"summary"}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The stream starts with comparison metadata, emits only changed identities in
|
|
235
|
+
deterministic order, and finishes with the complete summary. See the
|
|
236
|
+
[details format](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/details-format.md) for duplicate and schema events.
|
|
237
|
+
|
|
238
|
+
**Get every report from a single indexing pass**
|
|
239
|
+
|
|
240
|
+
Indexing dominates the cost of a comparison, and each invocation indexes both
|
|
241
|
+
inputs again. Instead of running separate summary, schema, and details
|
|
242
|
+
commands, request them together and query the details file afterwards:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
jsonl-diff old.jsonl new.jsonl \
|
|
246
|
+
--key id \
|
|
247
|
+
--schema-diff \
|
|
248
|
+
--details report.jsonl
|
|
249
|
+
|
|
250
|
+
# Later queries read report.jsonl instead of re-indexing the inputs.
|
|
251
|
+
jq -c 'select(.type == "schema_change")' report.jsonl
|
|
252
|
+
jq -c 'select(.type == "change" and .op == "modified")' report.jsonl
|
|
253
|
+
jq -c 'select(.type == "summary")' report.jsonl
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
See [Details format → Querying a report](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/details-format.md#querying-a-report).
|
|
257
|
+
|
|
258
|
+
**Read compressed inputs**
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
jsonl-diff old.jsonl.xz new.jsonl.gz --key id
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Read top-level JSON arrays**
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
jsonl-diff old.json new.json --key id --format json
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Exit codes
|
|
271
|
+
|
|
272
|
+
| Code | Meaning |
|
|
273
|
+
|-----:|------------------------------------------------------------------|
|
|
274
|
+
| `0` | No record, duplicate, or requested schema differences |
|
|
275
|
+
| `1` | Record differences, tolerated duplicates, or schema changes |
|
|
276
|
+
| `2` | Input, resource, output, or runtime failure |
|
|
277
|
+
| `3` | Invalid configuration or CLI usage (e.g. `--details` is OLD/NEW) |
|
|
278
|
+
|
|
279
|
+
## Python API
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
from jsonl_diff import ChangeOperation, diff
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def main():
|
|
286
|
+
with diff(
|
|
287
|
+
"old.jsonl.gz",
|
|
288
|
+
"new.jsonl.gz",
|
|
289
|
+
key=("country", "customerId"),
|
|
290
|
+
ignore=("/updated_at",),
|
|
291
|
+
where='country == `"ES"`',
|
|
292
|
+
schema_diff=True,
|
|
293
|
+
) as result:
|
|
294
|
+
print(result.summary)
|
|
295
|
+
|
|
296
|
+
for change in result.changes(ChangeOperation.MODIFIED):
|
|
297
|
+
print(change.key, change.old_line, change.new_line)
|
|
298
|
+
|
|
299
|
+
for change in result.schema_changes():
|
|
300
|
+
print(change.operation, change.path)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
if __name__ == "__main__":
|
|
304
|
+
main()
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
`diff()` returns a context-managed, disk-backed `DiffResult`. Entering the
|
|
308
|
+
context completely validates and indexes both sources. Changes, duplicates,
|
|
309
|
+
and schema changes are then iterated lazily instead of being materialized in
|
|
310
|
+
memory.
|
|
311
|
+
|
|
312
|
+
Applications must protect the entry point because indexing uses worker
|
|
313
|
+
processes. See [Parallel indexing](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/parallel-indexing.md#application-entry-points)
|
|
314
|
+
for the multiprocessing requirements.
|
|
315
|
+
|
|
316
|
+
The immutable result models include:
|
|
317
|
+
|
|
318
|
+
- `Summary` and `SchemaSummary`
|
|
319
|
+
- `Change` and `SourceLocation`
|
|
320
|
+
- `Duplicate`
|
|
321
|
+
- `SchemaChange` and `SchemaFieldProfile`
|
|
322
|
+
|
|
323
|
+
The public exception hierarchy is rooted at `JsonlDiffError`:
|
|
324
|
+
|
|
325
|
+
- `ConfigurationError`
|
|
326
|
+
- `InputError`
|
|
327
|
+
- `DuplicateKeyError`
|
|
328
|
+
- `ResourceError`
|
|
329
|
+
|
|
330
|
+
See the [Python API reference](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/python-api.md) for the complete signature,
|
|
331
|
+
models, iterator behavior, numeric key semantics, and error hierarchy.
|
|
332
|
+
|
|
333
|
+
## Comparison semantics
|
|
334
|
+
|
|
335
|
+
The comparison is intentionally strict:
|
|
336
|
+
|
|
337
|
+
- Each JSONL line must contain exactly one valid top-level object.
|
|
338
|
+
- `--format json` requires one top-level array whose elements are records.
|
|
339
|
+
- Blank lines, malformed JSON, `NaN`, and infinities are rejected.
|
|
340
|
+
- Repeated object properties use last-wins semantics.
|
|
341
|
+
- Identity fields are top-level JSON values; nested identity paths are not supported.
|
|
342
|
+
- Object member order is ignored recursively; array order remains significant.
|
|
343
|
+
- JSON types remain distinct: `"1"` is not `1`, and `true` is not `1`.
|
|
344
|
+
- Parsed numeric representation remains significant: `1` is not `1.0`, and
|
|
345
|
+
`1.0` is not `1.00`.
|
|
346
|
+
- Unicode strings are compared without normalization.
|
|
347
|
+
- Duplicate identities fail unless `first` or `last` is explicitly selected.
|
|
348
|
+
- Changes are emitted in deterministic canonical-identity order.
|
|
349
|
+
|
|
350
|
+
`--where` decides which records participate. `--key` defines their identity.
|
|
351
|
+
`--ignore` excludes fields from content comparison. `--schema-ignore`
|
|
352
|
+
independently excludes fields from observed-schema profiling.
|
|
353
|
+
|
|
354
|
+
Read [Comparison semantics](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/comparison-semantics.md) for duplicate rules,
|
|
355
|
+
ignore-pointer edge cases, filter order, schema inference, and numeric behavior.
|
|
356
|
+
|
|
357
|
+
## Sources and compression
|
|
358
|
+
|
|
359
|
+
| Source | CLI | Python API |
|
|
360
|
+
|--------------------|:------------:|:------------:|
|
|
361
|
+
| Local path | Yes | Yes |
|
|
362
|
+
| HTTP/HTTPS URL | Yes | Yes |
|
|
363
|
+
| File-like object | No | Yes |
|
|
364
|
+
| gzip (`.gz`) | Yes | Yes |
|
|
365
|
+
| bzip2 (`.bz2`) | Yes | Yes |
|
|
366
|
+
| xz (`.xz`) | Yes | Yes |
|
|
367
|
+
| Zstandard (`.zst`) | Python 3.14+ | Python 3.14+ |
|
|
368
|
+
| ZIP archive | No | No |
|
|
369
|
+
|
|
370
|
+
Source opening and decompression are delegated to `py-jsonl`. File-like inputs
|
|
371
|
+
and stdin are staged in the private temporary workspace so both indexing
|
|
372
|
+
workers can reopen them.
|
|
373
|
+
|
|
374
|
+
## Scope
|
|
375
|
+
|
|
376
|
+
`jsonl-diff` is a dataset reconciliation tool—not a visual or structural JSON
|
|
377
|
+
editor. It intentionally does not provide:
|
|
378
|
+
|
|
379
|
+
- line-position or field-level diffs;
|
|
380
|
+
- JSON Patch output;
|
|
381
|
+
- move or rename detection;
|
|
382
|
+
- nested identities or automatic key detection;
|
|
383
|
+
- fuzzy matching or numeric tolerances;
|
|
384
|
+
- unordered-array comparison;
|
|
385
|
+
- declared-schema validation or input repair;
|
|
386
|
+
- ZIP, database, or cloud-provider inputs;
|
|
387
|
+
- GUI or HTML reports.
|
|
388
|
+
|
|
389
|
+
## Documentation
|
|
390
|
+
|
|
391
|
+
| Guide | Covers |
|
|
392
|
+
|---------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|
|
|
393
|
+
| [Comparison semantics](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/comparison-semantics.md) | Identity, duplicates, ignores, filtering, numbers, and determinism |
|
|
394
|
+
| [Disk-backed architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md) | SQLite indexes, temporary storage, cleanup, and source handling |
|
|
395
|
+
| [Parallel indexing](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/parallel-indexing.md) | Worker lifecycle, database merge, recovery, and entry points |
|
|
396
|
+
| [Details format](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/details-format.md) | Machine-readable JSONL event contract |
|
|
397
|
+
| [Python API](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/python-api.md) | Complete signature, models, iterators, and errors |
|
|
398
|
+
|
|
399
|
+
## Development
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
uv sync --group test --group lint
|
|
403
|
+
# Lint
|
|
404
|
+
uv run ruff check --quiet --output-format=concise .
|
|
405
|
+
# Tests
|
|
406
|
+
uv run python -Wd -m pytest tests/ --cov
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
The suite covers CLI behavior, identity and canonicalization, validation,
|
|
410
|
+
details output, source handling, compression, parallel indexing, and cleanup.
|
|
411
|
+
|
|
412
|
+
## License
|
|
413
|
+
|
|
414
|
+
Licensed under the terms in [LICENSE](https://github.com/rmoralespp/jsonl-diff/blob/main/LICENSE).
|