jsonl-diff 0.1.1__tar.gz → 0.1.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jsonl-diff
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: A lightweight, pure-Python tool for comparing large JSONL/NDJSON datasets without loading them entirely into memory.
5
5
  Author-email: rmoralespp <rmoralespp@gmail.com>
6
6
  License: MIT License
@@ -67,11 +67,8 @@ Compare large JSONL/NDJSON datasets **by record identity instead of line
67
67
  position**, without loading the complete inputs into memory.
68
68
 
69
69
  `jsonl-diff` matches records using one or more top-level fields and reports:
70
-
71
- * `equal`
72
- * `added`
73
- * `deleted`
74
- * `modified`
70
+ `equal`, `added`, `deleted`, `modified`, and tolerated duplicates in each
71
+ source.
75
72
 
76
73
  It is useful for snapshots, ETL validation, migrations, exports, and CI checks.
77
74
 
@@ -79,10 +76,8 @@ It is useful for snapshots, ETL validation, migrations, exports, and CI checks.
79
76
 
80
77
  ## Installation
81
78
 
82
- Python 3.8–3.14.
83
-
84
79
  ```bash
85
- python -m pip install jsonl-diff
80
+ pip install jsonl-diff
86
81
  ```
87
82
 
88
83
  ## Quick start
@@ -114,6 +109,8 @@ Records:
114
109
  added: 1
115
110
  deleted: 0
116
111
  modified: 1
112
+ OLD duplicates: 0
113
+ NEW duplicates: 0
117
114
  ```
118
115
 
119
116
  Record order does not matter.
@@ -121,14 +118,15 @@ Record order does not matter.
121
118
  ## Features
122
119
 
123
120
  * Single or composite top-level identities.
124
- * Disk-backed comparison for large datasets ([architecture](docs/architecture.md)).
125
- * Optional JMESPath filtering with `--where`.
126
- * Configurable duplicate handling: `error`, `first`, or `last`.
127
- * Exact RFC 6901 JSON Pointer ignores.
121
+ * Disk-backed comparison for large datasets ([architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md)).
122
+ * Optional **JMESPath** filtering with `--where`.
123
+ * Configurable duplicate handling with counts and diagnostics: `error`, `first`, or `last`.
124
+ * Exact **RFC 6901** JSON Pointer ignores.
125
+ * Optional disk-backed observed-schema diff for fields, types, nullability, and requiredness.
128
126
  * Semantic number comparison using `Decimal`.
129
127
  * Deterministic summaries and change iteration.
130
128
  * Original OLD/NEW physical line numbers.
131
- * Machine-readable JSONL change log with `--details` ([format](docs/details-format.md)).
129
+ * Machine-readable JSONL change log with `--details` ([format](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/details-format.md)).
132
130
  * Local, HTTP/HTTPS, file-like, and supported compressed sources.
133
131
  * CLI and Python API using the same comparison engine.
134
132
 
@@ -137,7 +135,7 @@ Record order does not matter.
137
135
  ```text
138
136
  jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
139
137
  [--duplicates {error,first,last}] [--details FILE] [--quiet]
140
- [--max-temp MAX_TEMP]
138
+ [--schema-diff] [--schema-ignore POINTER] [--max-temp MAX_TEMP]
141
139
  old new
142
140
  ```
143
141
 
@@ -147,10 +145,12 @@ jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
147
145
  | `--key KEY` | Required top-level identity field; repeat or comma-separate for composite keys |
148
146
  | `--ignore POINTER` | RFC 6901 pointer to exclude from content comparison |
149
147
  | `--where EXPRESSION` | JMESPath filter applied to each record |
150
- | `--duplicates POLICY` | `error` (default), `first`, or `last` |
148
+ | `--duplicates POLICY` | `error` (default), or select and report duplicates with `first`/`last` |
151
149
  | `--details FILE` | Write deterministic machine-readable JSONL changes |
150
+ | `--schema-diff` | Compare observed fields, types, nullability, and requiredness |
151
+ | `--schema-ignore` | RFC 6901 pointer to exclude from observed-schema profiling |
152
152
  | `--quiet` | Suppress the normal summary |
153
- | `--max-temp BYTES` | Limit `jsonl-diff` temporary storage |
153
+ | `--max-temp BYTES` | Best-effort budget for `jsonl-diff` workspace temporary storage |
154
154
 
155
155
  Examples:
156
156
 
@@ -169,18 +169,24 @@ jsonl-diff old.jsonl new.jsonl \
169
169
  --key id \
170
170
  --where 'deleted_at == `null`'
171
171
 
172
+ # Detect observed schema drift
173
+ jsonl-diff old.jsonl new.jsonl \
174
+ --key id \
175
+ --schema-diff \
176
+ --schema-ignore /metadata
177
+
172
178
  # Compressed input
173
179
  jsonl-diff old.jsonl.xz new.jsonl.gz --key id
174
180
  ```
175
181
 
176
182
  ### Exit codes
177
183
 
178
- | Code | Meaning |
179
- | ---: | ----------------------------------------- |
180
- | `0` | Inputs are equal |
181
- | `1` | Added, deleted, or modified records found |
182
- | `2` | Input, resource, output, or runtime error |
183
- | `3` | Invalid CLI configuration or usage |
184
+ | Code | Meaning |
185
+ | ---: | ---------------------------------------------------------- |
186
+ | `0` | No record, duplicate, or requested observed-schema issues |
187
+ | `1` | Record differences, tolerated duplicates, or schema changes |
188
+ | `2` | Input, resource, output, or runtime error |
189
+ | `3` | Invalid CLI configuration or usage |
184
190
 
185
191
  ## Python API
186
192
 
@@ -193,11 +199,15 @@ with diff(
193
199
  key=("country", "customerId"),
194
200
  ignore=("/updated_at",),
195
201
  where='country == `"ES"`',
202
+ schema_diff=True,
196
203
  ) as result:
197
204
  print(result.summary)
198
205
 
199
206
  for change in result.changes(ChangeOperation.MODIFIED):
200
207
  print(change.key, change.old_line, change.new_line)
208
+
209
+ for change in result.schema_changes():
210
+ print(change.operation, change.path)
201
211
  ```
202
212
 
203
213
  `diff()` returns a disk-backed `DiffResult`, used as a context manager.
@@ -220,7 +230,7 @@ The public exception hierarchy is rooted at `JsonlDiffError`:
220
230
  * `DuplicateKeyError`
221
231
  * `ResourceError`
222
232
 
223
- See the [Python API reference](docs/python-api.md) for the full callable
233
+ See the [Python API reference](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/python-api.md) for the full callable
224
234
  signature, result models, `Decimal` key semantics, and error hierarchy.
225
235
 
226
236
  ## Comparison semantics
@@ -231,15 +241,18 @@ signature, result models, `Decimal` key semantics, and error hierarchy.
231
241
  * Blank lines, malformed JSON, duplicate properties, `NaN`, and infinities are rejected.
232
242
  * Identity fields must be top-level scalar values (`null` allowed only as one component of a composite key).
233
243
  * Identity types remain significant: `"1"` ≠ `1`, `true` ≠ `1`.
234
- * Duplicate identities fail by default; `--duplicates first`/`last` tolerate them.
244
+ * Duplicate identities fail by default; `--duplicates first`/`last` select one
245
+ occurrence, report every discarded occurrence, and return exit code `1`.
235
246
  * Object property order is ignored; array order is significant.
236
247
  * Numbers are compared by mathematical value: `1`, `1.0`, and `1e0` are equal.
237
248
  * Unicode strings are compared without normalization.
238
249
  * Changes are reported in deterministic identity order.
239
250
 
240
251
  `--where` selects which records participate; `--key` defines identity;
241
- `--ignore` removes fields from content comparison. See
242
- [Comparison semantics](docs/comparison-semantics.md) for the full rules,
252
+ `--ignore` removes fields from content comparison. With `--schema-diff`,
253
+ `--schema-ignore` independently removes fields from observed-schema profiling.
254
+ See
255
+ [Comparison semantics](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/comparison-semantics.md) for the full rules,
243
256
  including duplicate handling, ignore-pointer edge cases, `--where` evaluation
244
257
  order, and canonical number formatting.
245
258
 
@@ -255,7 +268,7 @@ Supported compression:
255
268
  * xz
256
269
  * Zstandard on Python 3.14
257
270
 
258
- See [architecture](docs/architecture.md) for the full source/compression
271
+ See [architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md) for the full source/compression
259
272
  support matrix and how sources are delegated to `py-jsonl`.
260
273
 
261
274
  ## Limitations
@@ -268,7 +281,7 @@ support matrix and how sources are delegated to `py-jsonl`.
268
281
  * nested identities or automatic key detection
269
282
  * fuzzy matching or numeric tolerances
270
283
  * unordered-array comparison
271
- * schema validation or input repair
284
+ * validation against a declared schema or input repair
272
285
  * ZIP, database, or cloud-provider inputs
273
286
  * GUI or HTML reports
274
287
 
@@ -276,10 +289,10 @@ It is a **dataset reconciliation tool**, not a general-purpose visual JSON diff.
276
289
 
277
290
  ## Further reading
278
291
 
279
- * [Comparison semantics](docs/comparison-semantics.md) — identity, duplicates, ignores, `--where`, canonical numbers, determinism.
280
- * [Disk-backed architecture](docs/architecture.md) — SQLite index, `max_temp`, cleanup, sources and compression.
281
- * [Details JSONL format](docs/details-format.md) — machine-readable `--details` output schema.
282
- * [Python API reference](docs/python-api.md) — full signature, result models, and error hierarchy.
292
+ * [Comparison semantics](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/comparison-semantics.md) — identity, duplicates, ignores, `--where`, canonical numbers, determinism.
293
+ * [Disk-backed architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md) — SQLite index, `max_temp`, cleanup, sources and compression.
294
+ * [Details JSONL format](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/details-format.md) — machine-readable `--details` output schema.
295
+ * [Python API reference](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/python-api.md) — full signature, result models, and error hierarchy.
283
296
 
284
297
  ## Development
285
298
 
@@ -12,11 +12,8 @@ Compare large JSONL/NDJSON datasets **by record identity instead of line
12
12
  position**, without loading the complete inputs into memory.
13
13
 
14
14
  `jsonl-diff` matches records using one or more top-level fields and reports:
15
-
16
- * `equal`
17
- * `added`
18
- * `deleted`
19
- * `modified`
15
+ `equal`, `added`, `deleted`, `modified`, and tolerated duplicates in each
16
+ source.
20
17
 
21
18
  It is useful for snapshots, ETL validation, migrations, exports, and CI checks.
22
19
 
@@ -24,10 +21,8 @@ It is useful for snapshots, ETL validation, migrations, exports, and CI checks.
24
21
 
25
22
  ## Installation
26
23
 
27
- Python 3.8–3.14.
28
-
29
24
  ```bash
30
- python -m pip install jsonl-diff
25
+ pip install jsonl-diff
31
26
  ```
32
27
 
33
28
  ## Quick start
@@ -59,6 +54,8 @@ Records:
59
54
  added: 1
60
55
  deleted: 0
61
56
  modified: 1
57
+ OLD duplicates: 0
58
+ NEW duplicates: 0
62
59
  ```
63
60
 
64
61
  Record order does not matter.
@@ -66,14 +63,15 @@ Record order does not matter.
66
63
  ## Features
67
64
 
68
65
  * Single or composite top-level identities.
69
- * Disk-backed comparison for large datasets ([architecture](docs/architecture.md)).
70
- * Optional JMESPath filtering with `--where`.
71
- * Configurable duplicate handling: `error`, `first`, or `last`.
72
- * Exact RFC 6901 JSON Pointer ignores.
66
+ * Disk-backed comparison for large datasets ([architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md)).
67
+ * Optional **JMESPath** filtering with `--where`.
68
+ * Configurable duplicate handling with counts and diagnostics: `error`, `first`, or `last`.
69
+ * Exact **RFC 6901** JSON Pointer ignores.
70
+ * Optional disk-backed observed-schema diff for fields, types, nullability, and requiredness.
73
71
  * Semantic number comparison using `Decimal`.
74
72
  * Deterministic summaries and change iteration.
75
73
  * Original OLD/NEW physical line numbers.
76
- * Machine-readable JSONL change log with `--details` ([format](docs/details-format.md)).
74
+ * Machine-readable JSONL change log with `--details` ([format](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/details-format.md)).
77
75
  * Local, HTTP/HTTPS, file-like, and supported compressed sources.
78
76
  * CLI and Python API using the same comparison engine.
79
77
 
@@ -82,7 +80,7 @@ Record order does not matter.
82
80
  ```text
83
81
  jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
84
82
  [--duplicates {error,first,last}] [--details FILE] [--quiet]
85
- [--max-temp MAX_TEMP]
83
+ [--schema-diff] [--schema-ignore POINTER] [--max-temp MAX_TEMP]
86
84
  old new
87
85
  ```
88
86
 
@@ -92,10 +90,12 @@ jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
92
90
  | `--key KEY` | Required top-level identity field; repeat or comma-separate for composite keys |
93
91
  | `--ignore POINTER` | RFC 6901 pointer to exclude from content comparison |
94
92
  | `--where EXPRESSION` | JMESPath filter applied to each record |
95
- | `--duplicates POLICY` | `error` (default), `first`, or `last` |
93
+ | `--duplicates POLICY` | `error` (default), or select and report duplicates with `first`/`last` |
96
94
  | `--details FILE` | Write deterministic machine-readable JSONL changes |
95
+ | `--schema-diff` | Compare observed fields, types, nullability, and requiredness |
96
+ | `--schema-ignore` | RFC 6901 pointer to exclude from observed-schema profiling |
97
97
  | `--quiet` | Suppress the normal summary |
98
- | `--max-temp BYTES` | Limit `jsonl-diff` temporary storage |
98
+ | `--max-temp BYTES` | Best-effort budget for `jsonl-diff` workspace temporary storage |
99
99
 
100
100
  Examples:
101
101
 
@@ -114,18 +114,24 @@ jsonl-diff old.jsonl new.jsonl \
114
114
  --key id \
115
115
  --where 'deleted_at == `null`'
116
116
 
117
+ # Detect observed schema drift
118
+ jsonl-diff old.jsonl new.jsonl \
119
+ --key id \
120
+ --schema-diff \
121
+ --schema-ignore /metadata
122
+
117
123
  # Compressed input
118
124
  jsonl-diff old.jsonl.xz new.jsonl.gz --key id
119
125
  ```
120
126
 
121
127
  ### Exit codes
122
128
 
123
- | Code | Meaning |
124
- | ---: | ----------------------------------------- |
125
- | `0` | Inputs are equal |
126
- | `1` | Added, deleted, or modified records found |
127
- | `2` | Input, resource, output, or runtime error |
128
- | `3` | Invalid CLI configuration or usage |
129
+ | Code | Meaning |
130
+ | ---: | ---------------------------------------------------------- |
131
+ | `0` | No record, duplicate, or requested observed-schema issues |
132
+ | `1` | Record differences, tolerated duplicates, or schema changes |
133
+ | `2` | Input, resource, output, or runtime error |
134
+ | `3` | Invalid CLI configuration or usage |
129
135
 
130
136
  ## Python API
131
137
 
@@ -138,11 +144,15 @@ with diff(
138
144
  key=("country", "customerId"),
139
145
  ignore=("/updated_at",),
140
146
  where='country == `"ES"`',
147
+ schema_diff=True,
141
148
  ) as result:
142
149
  print(result.summary)
143
150
 
144
151
  for change in result.changes(ChangeOperation.MODIFIED):
145
152
  print(change.key, change.old_line, change.new_line)
153
+
154
+ for change in result.schema_changes():
155
+ print(change.operation, change.path)
146
156
  ```
147
157
 
148
158
  `diff()` returns a disk-backed `DiffResult`, used as a context manager.
@@ -165,7 +175,7 @@ The public exception hierarchy is rooted at `JsonlDiffError`:
165
175
  * `DuplicateKeyError`
166
176
  * `ResourceError`
167
177
 
168
- See the [Python API reference](docs/python-api.md) for the full callable
178
+ See the [Python API reference](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/python-api.md) for the full callable
169
179
  signature, result models, `Decimal` key semantics, and error hierarchy.
170
180
 
171
181
  ## Comparison semantics
@@ -176,15 +186,18 @@ signature, result models, `Decimal` key semantics, and error hierarchy.
176
186
  * Blank lines, malformed JSON, duplicate properties, `NaN`, and infinities are rejected.
177
187
  * Identity fields must be top-level scalar values (`null` allowed only as one component of a composite key).
178
188
  * Identity types remain significant: `"1"` ≠ `1`, `true` ≠ `1`.
179
- * Duplicate identities fail by default; `--duplicates first`/`last` tolerate them.
189
+ * Duplicate identities fail by default; `--duplicates first`/`last` select one
190
+ occurrence, report every discarded occurrence, and return exit code `1`.
180
191
  * Object property order is ignored; array order is significant.
181
192
  * Numbers are compared by mathematical value: `1`, `1.0`, and `1e0` are equal.
182
193
  * Unicode strings are compared without normalization.
183
194
  * Changes are reported in deterministic identity order.
184
195
 
185
196
  `--where` selects which records participate; `--key` defines identity;
186
- `--ignore` removes fields from content comparison. See
187
- [Comparison semantics](docs/comparison-semantics.md) for the full rules,
197
+ `--ignore` removes fields from content comparison. With `--schema-diff`,
198
+ `--schema-ignore` independently removes fields from observed-schema profiling.
199
+ See
200
+ [Comparison semantics](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/comparison-semantics.md) for the full rules,
188
201
  including duplicate handling, ignore-pointer edge cases, `--where` evaluation
189
202
  order, and canonical number formatting.
190
203
 
@@ -200,7 +213,7 @@ Supported compression:
200
213
  * xz
201
214
  * Zstandard on Python 3.14
202
215
 
203
- See [architecture](docs/architecture.md) for the full source/compression
216
+ See [architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md) for the full source/compression
204
217
  support matrix and how sources are delegated to `py-jsonl`.
205
218
 
206
219
  ## Limitations
@@ -213,7 +226,7 @@ support matrix and how sources are delegated to `py-jsonl`.
213
226
  * nested identities or automatic key detection
214
227
  * fuzzy matching or numeric tolerances
215
228
  * unordered-array comparison
216
- * schema validation or input repair
229
+ * validation against a declared schema or input repair
217
230
  * ZIP, database, or cloud-provider inputs
218
231
  * GUI or HTML reports
219
232
 
@@ -221,10 +234,10 @@ It is a **dataset reconciliation tool**, not a general-purpose visual JSON diff.
221
234
 
222
235
  ## Further reading
223
236
 
224
- * [Comparison semantics](docs/comparison-semantics.md) — identity, duplicates, ignores, `--where`, canonical numbers, determinism.
225
- * [Disk-backed architecture](docs/architecture.md) — SQLite index, `max_temp`, cleanup, sources and compression.
226
- * [Details JSONL format](docs/details-format.md) — machine-readable `--details` output schema.
227
- * [Python API reference](docs/python-api.md) — full signature, result models, and error hierarchy.
237
+ * [Comparison semantics](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/comparison-semantics.md) — identity, duplicates, ignores, `--where`, canonical numbers, determinism.
238
+ * [Disk-backed architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md) — SQLite index, `max_temp`, cleanup, sources and compression.
239
+ * [Details JSONL format](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/details-format.md) — machine-readable `--details` output schema.
240
+ * [Python API reference](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/python-api.md) — full signature, result models, and error hierarchy.
228
241
 
229
242
  ## Development
230
243
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jsonl-diff
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: A lightweight, pure-Python tool for comparing large JSONL/NDJSON datasets without loading them entirely into memory.
5
5
  Author-email: rmoralespp <rmoralespp@gmail.com>
6
6
  License: MIT License
@@ -67,11 +67,8 @@ Compare large JSONL/NDJSON datasets **by record identity instead of line
67
67
  position**, without loading the complete inputs into memory.
68
68
 
69
69
  `jsonl-diff` matches records using one or more top-level fields and reports:
70
-
71
- * `equal`
72
- * `added`
73
- * `deleted`
74
- * `modified`
70
+ `equal`, `added`, `deleted`, `modified`, and tolerated duplicates in each
71
+ source.
75
72
 
76
73
  It is useful for snapshots, ETL validation, migrations, exports, and CI checks.
77
74
 
@@ -79,10 +76,8 @@ It is useful for snapshots, ETL validation, migrations, exports, and CI checks.
79
76
 
80
77
  ## Installation
81
78
 
82
- Python 3.8–3.14.
83
-
84
79
  ```bash
85
- python -m pip install jsonl-diff
80
+ pip install jsonl-diff
86
81
  ```
87
82
 
88
83
  ## Quick start
@@ -114,6 +109,8 @@ Records:
114
109
  added: 1
115
110
  deleted: 0
116
111
  modified: 1
112
+ OLD duplicates: 0
113
+ NEW duplicates: 0
117
114
  ```
118
115
 
119
116
  Record order does not matter.
@@ -121,14 +118,15 @@ Record order does not matter.
121
118
  ## Features
122
119
 
123
120
  * Single or composite top-level identities.
124
- * Disk-backed comparison for large datasets ([architecture](docs/architecture.md)).
125
- * Optional JMESPath filtering with `--where`.
126
- * Configurable duplicate handling: `error`, `first`, or `last`.
127
- * Exact RFC 6901 JSON Pointer ignores.
121
+ * Disk-backed comparison for large datasets ([architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md)).
122
+ * Optional **JMESPath** filtering with `--where`.
123
+ * Configurable duplicate handling with counts and diagnostics: `error`, `first`, or `last`.
124
+ * Exact **RFC 6901** JSON Pointer ignores.
125
+ * Optional disk-backed observed-schema diff for fields, types, nullability, and requiredness.
128
126
  * Semantic number comparison using `Decimal`.
129
127
  * Deterministic summaries and change iteration.
130
128
  * Original OLD/NEW physical line numbers.
131
- * Machine-readable JSONL change log with `--details` ([format](docs/details-format.md)).
129
+ * Machine-readable JSONL change log with `--details` ([format](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/details-format.md)).
132
130
  * Local, HTTP/HTTPS, file-like, and supported compressed sources.
133
131
  * CLI and Python API using the same comparison engine.
134
132
 
@@ -137,7 +135,7 @@ Record order does not matter.
137
135
  ```text
138
136
  jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
139
137
  [--duplicates {error,first,last}] [--details FILE] [--quiet]
140
- [--max-temp MAX_TEMP]
138
+ [--schema-diff] [--schema-ignore POINTER] [--max-temp MAX_TEMP]
141
139
  old new
142
140
  ```
143
141
 
@@ -147,10 +145,12 @@ jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
147
145
  | `--key KEY` | Required top-level identity field; repeat or comma-separate for composite keys |
148
146
  | `--ignore POINTER` | RFC 6901 pointer to exclude from content comparison |
149
147
  | `--where EXPRESSION` | JMESPath filter applied to each record |
150
- | `--duplicates POLICY` | `error` (default), `first`, or `last` |
148
+ | `--duplicates POLICY` | `error` (default), or select and report duplicates with `first`/`last` |
151
149
  | `--details FILE` | Write deterministic machine-readable JSONL changes |
150
+ | `--schema-diff` | Compare observed fields, types, nullability, and requiredness |
151
+ | `--schema-ignore` | RFC 6901 pointer to exclude from observed-schema profiling |
152
152
  | `--quiet` | Suppress the normal summary |
153
- | `--max-temp BYTES` | Limit `jsonl-diff` temporary storage |
153
+ | `--max-temp BYTES` | Best-effort budget for `jsonl-diff` workspace temporary storage |
154
154
 
155
155
  Examples:
156
156
 
@@ -169,18 +169,24 @@ jsonl-diff old.jsonl new.jsonl \
169
169
  --key id \
170
170
  --where 'deleted_at == `null`'
171
171
 
172
+ # Detect observed schema drift
173
+ jsonl-diff old.jsonl new.jsonl \
174
+ --key id \
175
+ --schema-diff \
176
+ --schema-ignore /metadata
177
+
172
178
  # Compressed input
173
179
  jsonl-diff old.jsonl.xz new.jsonl.gz --key id
174
180
  ```
175
181
 
176
182
  ### Exit codes
177
183
 
178
- | Code | Meaning |
179
- | ---: | ----------------------------------------- |
180
- | `0` | Inputs are equal |
181
- | `1` | Added, deleted, or modified records found |
182
- | `2` | Input, resource, output, or runtime error |
183
- | `3` | Invalid CLI configuration or usage |
184
+ | Code | Meaning |
185
+ | ---: | ---------------------------------------------------------- |
186
+ | `0` | No record, duplicate, or requested observed-schema issues |
187
+ | `1` | Record differences, tolerated duplicates, or schema changes |
188
+ | `2` | Input, resource, output, or runtime error |
189
+ | `3` | Invalid CLI configuration or usage |
184
190
 
185
191
  ## Python API
186
192
 
@@ -193,11 +199,15 @@ with diff(
193
199
  key=("country", "customerId"),
194
200
  ignore=("/updated_at",),
195
201
  where='country == `"ES"`',
202
+ schema_diff=True,
196
203
  ) as result:
197
204
  print(result.summary)
198
205
 
199
206
  for change in result.changes(ChangeOperation.MODIFIED):
200
207
  print(change.key, change.old_line, change.new_line)
208
+
209
+ for change in result.schema_changes():
210
+ print(change.operation, change.path)
201
211
  ```
202
212
 
203
213
  `diff()` returns a disk-backed `DiffResult`, used as a context manager.
@@ -220,7 +230,7 @@ The public exception hierarchy is rooted at `JsonlDiffError`:
220
230
  * `DuplicateKeyError`
221
231
  * `ResourceError`
222
232
 
223
- See the [Python API reference](docs/python-api.md) for the full callable
233
+ See the [Python API reference](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/python-api.md) for the full callable
224
234
  signature, result models, `Decimal` key semantics, and error hierarchy.
225
235
 
226
236
  ## Comparison semantics
@@ -231,15 +241,18 @@ signature, result models, `Decimal` key semantics, and error hierarchy.
231
241
  * Blank lines, malformed JSON, duplicate properties, `NaN`, and infinities are rejected.
232
242
  * Identity fields must be top-level scalar values (`null` allowed only as one component of a composite key).
233
243
  * Identity types remain significant: `"1"` ≠ `1`, `true` ≠ `1`.
234
- * Duplicate identities fail by default; `--duplicates first`/`last` tolerate them.
244
+ * Duplicate identities fail by default; `--duplicates first`/`last` select one
245
+ occurrence, report every discarded occurrence, and return exit code `1`.
235
246
  * Object property order is ignored; array order is significant.
236
247
  * Numbers are compared by mathematical value: `1`, `1.0`, and `1e0` are equal.
237
248
  * Unicode strings are compared without normalization.
238
249
  * Changes are reported in deterministic identity order.
239
250
 
240
251
  `--where` selects which records participate; `--key` defines identity;
241
- `--ignore` removes fields from content comparison. See
242
- [Comparison semantics](docs/comparison-semantics.md) for the full rules,
252
+ `--ignore` removes fields from content comparison. With `--schema-diff`,
253
+ `--schema-ignore` independently removes fields from observed-schema profiling.
254
+ See
255
+ [Comparison semantics](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/comparison-semantics.md) for the full rules,
243
256
  including duplicate handling, ignore-pointer edge cases, `--where` evaluation
244
257
  order, and canonical number formatting.
245
258
 
@@ -255,7 +268,7 @@ Supported compression:
255
268
  * xz
256
269
  * Zstandard on Python 3.14
257
270
 
258
- See [architecture](docs/architecture.md) for the full source/compression
271
+ See [architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md) for the full source/compression
259
272
  support matrix and how sources are delegated to `py-jsonl`.
260
273
 
261
274
  ## Limitations
@@ -268,7 +281,7 @@ support matrix and how sources are delegated to `py-jsonl`.
268
281
  * nested identities or automatic key detection
269
282
  * fuzzy matching or numeric tolerances
270
283
  * unordered-array comparison
271
- * schema validation or input repair
284
+ * validation against a declared schema or input repair
272
285
  * ZIP, database, or cloud-provider inputs
273
286
  * GUI or HTML reports
274
287
 
@@ -276,10 +289,10 @@ It is a **dataset reconciliation tool**, not a general-purpose visual JSON diff.
276
289
 
277
290
  ## Further reading
278
291
 
279
- * [Comparison semantics](docs/comparison-semantics.md) — identity, duplicates, ignores, `--where`, canonical numbers, determinism.
280
- * [Disk-backed architecture](docs/architecture.md) — SQLite index, `max_temp`, cleanup, sources and compression.
281
- * [Details JSONL format](docs/details-format.md) — machine-readable `--details` output schema.
282
- * [Python API reference](docs/python-api.md) — full signature, result models, and error hierarchy.
292
+ * [Comparison semantics](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/comparison-semantics.md) — identity, duplicates, ignores, `--where`, canonical numbers, determinism.
293
+ * [Disk-backed architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md) — SQLite index, `max_temp`, cleanup, sources and compression.
294
+ * [Details JSONL format](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/details-format.md) — machine-readable `--details` output schema.
295
+ * [Python API reference](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/python-api.md) — full signature, result models, and error hierarchy.
283
296
 
284
297
  ## Development
285
298