shapediff 0.1.0a4__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Angus Uelsmann
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.4
2
+ Name: shapediff
3
+ Version: 0.1.0a4
4
+ Summary: Evidence-aware JSON API response diffing without a schema
5
+ Author: Angus Uelsmann
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/IamAngusU/shapediff
8
+ Project-URL: Issues, https://github.com/IamAngusU/shapediff/issues
9
+ Keywords: json,api,regression,contract,diff,cli,schema-drift
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Software Development :: Testing
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Dynamic: license-file
26
+
27
+ # ShapeDiff
28
+
29
+ Compare observed JSON API response structures without maintaining a schema.
30
+ For developers checking undocumented endpoints, legacy APIs and response
31
+ regressions in CI. Python 3.10+, no runtime dependencies, network calls or telemetry.
32
+
33
+ ```sh
34
+ shapediff production.json staging.json
35
+ ```
36
+
37
+ Types, missing fields and nested object/array changes are checked; ordinary scalar
38
+ values such as IDs and timestamps can change without producing findings.
39
+ Exit code 1 means a breaking finding was observed. A clean diff describes the
40
+ sampled responses and does not prove a complete API contract.
41
+
42
+ The CLI also supports JSONL/directories, value-free saved profiles, endpoint
43
+ suites, ignore/map rules and JSON/Markdown output.
44
+
45
+ [Quick start, examples and screenshots](https://github.com/IamAngusU/shapediff#readme) ·
46
+ [Compatibility rules](https://github.com/IamAngusU/shapediff/blob/main/docs/compatibility.md) ·
47
+ [Security and profile privacy](https://github.com/IamAngusU/shapediff/blob/main/docs/security.md) ·
48
+ [MIT license](https://github.com/IamAngusU/shapediff/blob/main/LICENSE)
@@ -0,0 +1,251 @@
1
+ <p align="center">
2
+ <img src="docs/assets/shapediff-wordmark.svg" width="360" alt="ShapeDiff">
3
+ </p>
4
+
5
+ <h1 align="center">Compare responses. Not the noise.</h1>
6
+ <p align="center">Find API shape regressions without maintaining a schema.</p>
7
+
8
+ <p align="center"><a href="README.de.md"><img src="docs/assets/readme-language-de.svg" height="40" alt="Diese README auf Deutsch lesen"></a></p>
9
+
10
+ <p align="center">
11
+ <a href="https://github.com/IamAngusU/shapediff/releases/tag/v0.1.0a4"><img src="docs/assets/readme/repo-release.svg" height="40" alt="Preview 0.1.0a4"></a>
12
+ <a href="pyproject.toml"><img src="docs/assets/readme/repo-runtime.svg" height="40" alt="Python 3.10 or newer"></a>
13
+ <a href="LICENSE"><img src="docs/assets/readme/repo-license.svg" height="40" alt="MIT license"></a>
14
+ </p>
15
+
16
+
17
+ <p align="center">
18
+ <a href="README.de.md">Deutsch</a> ·
19
+ <a href="#get-running">Get running</a> ·
20
+ <a href="#more-than-two-samples">Multiple samples</a> ·
21
+ <a href="#compare-multiple-endpoints">Endpoint suites</a> ·
22
+ <a href="#what-the-results-mean">Compatibility rules</a> ·
23
+ <a href="docs/compatibility.md">Exact semantics</a> ·
24
+ <a href="docs/security.md">Security</a> ·
25
+ <a href="docs/metrics.md">Measured results</a>
26
+ </p>
27
+
28
+ ShapeDiff compares **observed JSON API response structures**. IDs, timestamps, names and every other ordinary value can change without producing a finding. Types, missing fields and changes inside nested objects or arrays are checked.
29
+
30
+ It runs locally, reads files or stdin, and has **no runtime dependencies**, account, telemetry or network calls.
31
+
32
+ ![ShapeDiff compares the included production and staging responses](docs/assets/terminal.png)
33
+
34
+ ```text
35
+ $ python3 shapediff.py examples/production.json examples/staging.json
36
+
37
+ ERROR user.email missing
38
+ ERROR user.id number -> string
39
+ INFO user.avatar added
40
+
41
+ shapediff: 2 breaking, 0 warning, 1 info | samples 1 -> 1
42
+ ```
43
+
44
+ The summary is printed to stderr; findings are printed to stdout. Exit code `1` means a breaking finding was observed. A summary of what was observed is not a proof that an undocumented endpoint has a stable contract.
45
+
46
+ ## Get running
47
+
48
+ **Python 3.10 or newer.** The [GitHub release](https://github.com/IamAngusU/shapediff/releases/tag/v0.1.0a4) offers an installable wheel and a standalone `shapediff.py`. There are no runtime packages to resolve. `pipx` installs the command into its own environment:
49
+
50
+ ```sh
51
+ pipx install 'https://github.com/IamAngusU/shapediff/releases/download/v0.1.0a4/shapediff-0.1.0a4-py3-none-any.whl'
52
+ shapediff production.json staging.json
53
+ ```
54
+
55
+ Install [pipx](https://pipx.pypa.io/stable/installation/) first if needed. For a project virtual environment, use `python -m pip install` with the same wheel URL. The release includes `SHA256SUMS.txt` for checking downloaded artifacts. Use a release URL in automation so the installed version stays fixed.
56
+
57
+ The single-file option needs no package installation:
58
+
59
+ ### macOS / Linux
60
+
61
+ ```sh
62
+ curl -fsSL https://github.com/IamAngusU/shapediff/releases/download/v0.1.0a4/shapediff.py -o shapediff.py && python3 shapediff.py production.json staging.json
63
+ ```
64
+
65
+ ### Windows PowerShell
66
+
67
+ ```powershell
68
+ Invoke-WebRequest -ErrorAction Stop 'https://github.com/IamAngusU/shapediff/releases/download/v0.1.0a4/shapediff.py' -OutFile shapediff.py; py -3 shapediff.py production.json staging.json
69
+ ```
70
+
71
+ The download saves readable Python source; it does not pipe remote code into a shell. Review it and verify its SHA-256 before running it when your environment requires that assurance. From a clone, `python3 shapediff.py` and `python3 -m shapediff` work without installing anything.
72
+
73
+ ## Reusable baselines without keeping response values
74
+
75
+ Capture a versioned `.sdshape` profile once, then compare later responses directly against it. The profile stores observed type counts, field presence counts, and nested shapes. **No scalar response values or source byte lengths are saved.**
76
+
77
+ ```sh
78
+ # First capture: a response file, a JSONL stream, or a directory
79
+ python3 shapediff.py profile production.json -o production.sdshape
80
+
81
+ # Check a new response against the stored observations
82
+ python3 shapediff.py production.sdshape staging.json --breaking-only
83
+
84
+ # Add another observation without retaining the original response
85
+ curl -fsS https://api.example.test/users/1 | python3 shapediff.py profile - --update production.sdshape
86
+
87
+ # Review the accumulated baseline against new data
88
+ python3 shapediff.py production.sdshape current/ --format markdown
89
+ ```
90
+
91
+ Profiles can also be compared against each other: `shapediff old.sdshape new.sdshape`. Comparison automatically inherits the original `--map`, `--ignore`, `--formats` and `--strict-integers` rules from the profile. If two profiles disagree, the comparison stops instead of returning misleading findings. Updates merge presence and type counts and use an atomic file replacement. A failed update leaves the original intact.
92
+
93
+ `--output` on `profile` refuses to replace an existing baseline unless you supply `--force`. Use `--update` to add observations rather than rebuilding from raw responses.
94
+
95
+ **Privacy boundary:** literal JSON *object keys* are preserved. A key that is itself an email address, UUID or other identifier can be sensitive. Capture refuses obvious email/UUID/hex-style identifiers as keys by default and directs you to `--map` or `--ignore`. `--allow-identifier-keys` is an explicit escape hatch. This is a heuristic, not a guarantee that every potentially sensitive key is detected; inspect profiles before sharing them.
96
+
97
+ ```sh
98
+ # User IDs in object keys become one value-shape, without recording the IDs
99
+ python3 shapediff.py profile users.jsonl -o users.sdshape --map /usersById
100
+ ```
101
+
102
+ A profile is **not** a JSON Schema, a contract certificate, or an authorization-safe data export. Its counters only describe the responses you sampled. See [profile safety and limitations](docs/security.md).
103
+
104
+ ## More than two samples
105
+
106
+ One response cannot establish optionality. Multiple responses give a better **observed frequency**, but still cannot prove an API field is required or optional. ShapeDiff keeps these distinctions visible.
107
+
108
+ ```sh
109
+ shapediff examples/baseline/ examples/current/
110
+ ```
111
+
112
+ ```text
113
+ ERROR tags[*].count number -> string
114
+ WARN avatar not seen in current samples (baseline 1/3)
115
+ ```
116
+
117
+ Here, `avatar` appeared in one of three baseline object observations. It is a warning, not a guaranteed breaking change and not a proven optional field. A field present in all baseline observations and absent in all current observations is reported as `ERROR missing`, together with counts when `--evidence` is enabled.
118
+
119
+ Each directory is a **collection of samples of the same response contract**. Do not put multiple unrelated endpoints in one directory: that would mix their structures. Directory scanning is non-recursive unless `--recursive` is set.
120
+
121
+ `.json`, `.jsonl`, `.ndjson` and `-` (stdin) are supported. JSONL and NDJSON use one response per non-empty line. A JSON array remains one response containing an array, not a collection of independent responses.
122
+
123
+ ```sh
124
+ shapediff baseline.jsonl current.ndjson --evidence
125
+ curl -fsS https://api.example.test/users/1 | shapediff production.json -
126
+ shapediff production.json staging.json --breaking-only
127
+ shapediff production.json staging.json --format json --output report.json
128
+ shapediff baseline/ current/ --coverage-drop 0.5 --fail-on warning
129
+ ```
130
+
131
+ The URL example uses `curl`, not built-in HTTP fetching. ShapeDiff itself never contacts the network and never handles API credentials. Use `curl -f` or equivalent so failed HTTP requests do not accidentally become valid baseline responses.
132
+
133
+ ## What the results mean
134
+
135
+ | Observed difference in a **response** | Default |
136
+ | --- | --- |
137
+ | Field seen in every baseline object but missing everywhere in current | `ERROR` |
138
+ | `number` becomes `string` | `ERROR` |
139
+ | New possible type, including `string` becoming `string\|null` | `ERROR` |
140
+ | Previously intermittent field is not seen in current samples | `WARN` |
141
+ | `null`-only baseline becomes a populated type | `WARN` |
142
+ | Array has elements on one side and only empty arrays on the other | `WARN` |
143
+ | New field appears | `INFO` |
144
+ | An observed type disappears but remaining types are still supported | `INFO` |
145
+ | Only values change | No finding |
146
+
147
+ A response consumer must be able to handle all new values; **adding a possible output type can therefore break it**. Conversely, the disappearance of one previously observed type is generally a narrowing of observed output behavior, not a new consumer burden.
148
+
149
+ These classifications are conservative regression signals, not formal backward-compatibility proofs. See [the precise evidence and grading rules](docs/compatibility.md), including what ShapeDiff cannot know from data alone.
150
+
151
+ ## Compare multiple endpoints
152
+
153
+ Use `suite` when a test run covers several response contracts. A suite file names each response, records its observed HTTP status, and points to JSON samples, a sample directory, a `.sdshape` profile, or `null` for a bodyless response. ShapeDiff matches cases by `id` and compares their bodies **independently**.
154
+
155
+ ```sh
156
+ python3 shapediff.py suite examples/baseline.sdsuite examples/current.sdsuite
157
+ python3 shapediff.py suite before.sdsuite after.sdsuite --format json --output report.json
158
+ ```
159
+
160
+ ```json
161
+ {
162
+ "suite_version": 1,
163
+ "responses": [
164
+ {
165
+ "id": "GET /users/{id}",
166
+ "status": 200,
167
+ "body": "samples/users.jsonl",
168
+ "headers": {"content-type": "application/json"}
169
+ },
170
+ {"id": "DELETE /users/{id}", "status": 204, "body": null}
171
+ ]
172
+ }
173
+ ```
174
+
175
+ Save the manifest with a `.sdsuite` suffix. Body paths are relative to its directory and must stay within it. A case can use a saved profile as its body baseline; its inference rules are inherited for that case. `--max-samples` and `--max-shape-nodes` apply **per case**. Suites are read locally and do not make HTTP requests.
176
+
177
+ Only headers listed in the manifest are compared. Header names are case-insensitive; values are compared exactly, so include stable values such as `content-type`, not request IDs or timestamps. A changed status, removed case, missing tracked header, or changed tracked header value is an `ERROR`. A new case or header is `INFO`. Header values are never copied into reports, but **the manifest itself contains them**; do not put credentials or private header values there. See [suite semantics](docs/compatibility.md) and [security guidance](docs/security.md).
178
+
179
+ ## Dynamic keys, on purpose
180
+
181
+ Dynamic object members are common in maps keyed by UUIDs, region IDs or usernames. Without a schema, there is no reliable general-purpose way to distinguish that pattern from removed fields. **ShapeDiff never guesses by default.**
182
+
183
+ ```sh
184
+ shapediff before.json after.json --map /usersById
185
+ shapediff before.json after.json --map /regions --ignore '/regions/*/traceId'
186
+ ```
187
+
188
+ `--map /usersById` treats the keys at that object path as entries, compares the merged **value shapes** at `usersById{*}`, and does not report key turnover as removals/additions. Map values still produce breaking findings for structural regressions. If one side contains no map entries, coverage is marked `WARN`, not falsely clean.
189
+
190
+ Both `--ignore` and `--map` accept JSON Pointer-style selectors. `*` matches one path segment, `**` matches zero or more. Escape literal `/` as `~1` and `~` as `~0`. For simple names, `items[*].etag` is also accepted. Prefer pointers for keys containing dots or brackets.
191
+
192
+ ## Use it in CI
193
+
194
+ ```sh
195
+ shapediff tests/contracts/production/ tests/contracts/staging/ --breaking-only --quiet
196
+ ```
197
+
198
+ Exit codes are stable and deliberate:
199
+
200
+ | Exit | Meaning |
201
+ | --- | --- |
202
+ | `0` | No findings at the selected failure level |
203
+ | `1` | A finding matched `--fail-on` |
204
+ | `2` | Invalid command-line usage |
205
+ | `3` | Invalid JSON, inaccessible input, limit exceeded or output I/O error |
206
+ | `130` | Interrupted |
207
+
208
+ `--fail-on error` is the default. Select `warning`, `any` or `never` to choose the gate; `--breaking-only` filters the **display** and does not change the exit logic. JSON and Markdown outputs are available for build artifacts and PR reviews.
209
+
210
+ | Option | Purpose |
211
+ | --- | --- |
212
+ | `--evidence` | Show observation counts beside text findings |
213
+ | `--format text\|json\|markdown` | Text, structured JSON or PR-ready Markdown |
214
+ | `--output FILE` | Atomic report write, rejects overwriting input files |
215
+ | `--ignore SELECTOR` | Skip a subtree before inference; repeatable |
216
+ | `--map SELECTOR` | Explicitly treat an object as a dynamic-key map |
217
+ | `--formats` | Opt into string-format drift warnings |
218
+ | `--strict-integers` | Distinguish integer and number observations (integer is a number subtype) |
219
+ | `--coverage-drop 0.5` | Warn on large observed drops in field presence |
220
+ | `--recursive` | Also include sample files in subdirectories |
221
+ | `--max-document-mib N` | Bound each JSON document / JSONL line; default 32 |
222
+ | `--max-samples N` | Bound observations per side; default 100,000 |
223
+ | `--max-shape-nodes N` | Bound inferred shape size; default 250,000 |
224
+ | `--max-depth N` | Bound nesting depth; default 128 |
225
+ | `--color auto\|always\|never` | ANSI colors in terminal reports |
226
+
227
+ Run `shapediff --help` or `shapediff profile --help` for the full interface. Profile capture supports `--update`, `--force` and `--allow-identifier-keys` in addition to the relevant ingestion flags.
228
+
229
+ ## Development and verification
230
+
231
+ ```sh
232
+ python3 -m unittest discover -s tests -v
233
+ python3 scripts/benchmark.py --documents 5000 --repetitions 5
234
+ python3 shapediff.py examples/production.json examples/staging.json --fail-on never
235
+ ```
236
+
237
+ Tests are standard-library-only, offline and repeatable. [Local verification](docs/verification-local.json), [comparison benchmark](docs/benchmark-local.json) and [saved-profile benchmark](docs/benchmark-profile-local.json) record what actually ran. The optional GitHub Actions workflow performs the same test suite, but a workflow file alone is not proof that CI ran. This alpha has not been represented as a published or externally audited release.
238
+
239
+ ## License
240
+
241
+ [MIT](LICENSE) · © 2026 Angus Uelsmann · [angusu.de](https://angusu.de)
242
+
243
+ ## Why not Optic, oasdiff or Pact?
244
+
245
+ [oasdiff](https://github.com/oasdiff/oasdiff) compares OpenAPI contracts. [Optic](https://github.com/opticdev/optic) provides OpenAPI linting, diffing and testing. Prefer them when OpenAPI is your source of truth. [Pact](https://docs.pact.io/) verifies explicit consumer/provider contracts through tests.
246
+
247
+ ShapeDiff starts with observed JSON responses. It fits undocumented endpoints, legacy APIs and quick regression checks in CI. It cannot infer unseen responses or your consumers' requirements, so a clean diff does not establish contract compatibility. Use it alongside contract tests when you have both.
248
+
249
+ [Build evidence](docs/metrics.md) · [Registry publication](docs/registry.md)
250
+
251
+ <p align="center"><a href="https://github.com/IamAngusU/icon-marquee"><img src="docs/assets/stack-marquee.svg" width="760" alt="Python, JSON, GitHub and Linux"></a></p>
@@ -0,0 +1,22 @@
1
+ # ShapeDiff
2
+
3
+ Compare observed JSON API response structures without maintaining a schema.
4
+ For developers checking undocumented endpoints, legacy APIs and response
5
+ regressions in CI. Python 3.10+, no runtime dependencies, network calls or telemetry.
6
+
7
+ ```sh
8
+ shapediff production.json staging.json
9
+ ```
10
+
11
+ Types, missing fields and nested object/array changes are checked; ordinary scalar
12
+ values such as IDs and timestamps can change without producing findings.
13
+ Exit code 1 means a breaking finding was observed. A clean diff describes the
14
+ sampled responses and does not prove a complete API contract.
15
+
16
+ The CLI also supports JSONL/directories, value-free saved profiles, endpoint
17
+ suites, ignore/map rules and JSON/Markdown output.
18
+
19
+ [Quick start, examples and screenshots](https://github.com/IamAngusU/shapediff#readme) ·
20
+ [Compatibility rules](https://github.com/IamAngusU/shapediff/blob/main/docs/compatibility.md) ·
21
+ [Security and profile privacy](https://github.com/IamAngusU/shapediff/blob/main/docs/security.md) ·
22
+ [MIT license](https://github.com/IamAngusU/shapediff/blob/main/LICENSE)
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "shapediff"
7
+ version = "0.1.0a4"
8
+ description = "Evidence-aware JSON API response diffing without a schema"
9
+ readme = "README.pypi.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Angus Uelsmann" }]
13
+ keywords = ["json", "api", "regression", "contract", "diff", "cli", "schema-drift"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ "Topic :: Software Development :: Testing",
27
+ ]
28
+ dependencies = []
29
+
30
+ [project.scripts]
31
+ shapediff = "shapediff:main"
32
+
33
+ [project.urls]
34
+ Repository = "https://github.com/IamAngusU/shapediff"
35
+ Issues = "https://github.com/IamAngusU/shapediff/issues"
36
+
37
+ [tool.setuptools]
38
+ py-modules = ["shapediff"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.4
2
+ Name: shapediff
3
+ Version: 0.1.0a4
4
+ Summary: Evidence-aware JSON API response diffing without a schema
5
+ Author: Angus Uelsmann
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/IamAngusU/shapediff
8
+ Project-URL: Issues, https://github.com/IamAngusU/shapediff/issues
9
+ Keywords: json,api,regression,contract,diff,cli,schema-drift
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Software Development :: Testing
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Dynamic: license-file
26
+
27
+ # ShapeDiff
28
+
29
+ Compare observed JSON API response structures without maintaining a schema.
30
+ For developers checking undocumented endpoints, legacy APIs and response
31
+ regressions in CI. Python 3.10+, no runtime dependencies, network calls or telemetry.
32
+
33
+ ```sh
34
+ shapediff production.json staging.json
35
+ ```
36
+
37
+ Types, missing fields and nested object/array changes are checked; ordinary scalar
38
+ values such as IDs and timestamps can change without producing findings.
39
+ Exit code 1 means a breaking finding was observed. A clean diff describes the
40
+ sampled responses and does not prove a complete API contract.
41
+
42
+ The CLI also supports JSONL/directories, value-free saved profiles, endpoint
43
+ suites, ignore/map rules and JSON/Markdown output.
44
+
45
+ [Quick start, examples and screenshots](https://github.com/IamAngusU/shapediff#readme) ·
46
+ [Compatibility rules](https://github.com/IamAngusU/shapediff/blob/main/docs/compatibility.md) ·
47
+ [Security and profile privacy](https://github.com/IamAngusU/shapediff/blob/main/docs/security.md) ·
48
+ [MIT license](https://github.com/IamAngusU/shapediff/blob/main/LICENSE)
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ README.md
3
+ README.pypi.md
4
+ pyproject.toml
5
+ shapediff.py
6
+ shapediff.egg-info/PKG-INFO
7
+ shapediff.egg-info/SOURCES.txt
8
+ shapediff.egg-info/dependency_links.txt
9
+ shapediff.egg-info/entry_points.txt
10
+ shapediff.egg-info/top_level.txt
11
+ tests/test_cli.py
12
+ tests/test_core.py
13
+ tests/test_profiles.py
14
+ tests/test_suite.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ shapediff = shapediff:main
@@ -0,0 +1 @@
1
+ shapediff