audit fixes, repo restructure, and documentation

Soundness and performance audit (17 fixes):
- See AUDIT.md for full details and @claude comments in code

Repo restructure:
- Inline json-ptr and json-patch submodules as regular directories
- Remove cbor submodule, replace serde_cbor with ciborium
- Rename patch-db/ -> core/, patch-db-macro/ -> macro/,
  patch-db-macro-internals/ -> macro-internals/, patch-db-util/ -> util/
- Purge upstream CI/CD, bench, and release cruft from json-patch
- Remove .gitmodules

Test fixes:
- Fix proptest doesnt_crash (unique file paths, proper close/cleanup)
- Add PatchDb::close() for clean teardown

Documentation:
- Add README.md, ARCHITECTURE.md, CONTRIBUTING.md, CLAUDE.md, AUDIT.md
- Add TSDocs to TypeScript client exports

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Hill
2026-02-23 19:06:42 -07:00
parent 05c93290c7
commit 86b0768bbb
46 changed files with 5744 additions and 95 deletions

View File

@@ -0,0 +1,92 @@
[
{
"comment": "1. introduction",
"doc": {
"a": "b",
"c": {
"d": "e",
"f": "g"
}
},
"patch": {
"a": "z",
"c": {
"f": null
}
},
"expected": {
"a": "z",
"c": {
"d": "e"
}
},
"merge": true
},
{
"comment": "3. example",
"doc": {
"title": "Goodbye!",
"author": {
"givenName": "John",
"familyName": "Doe"
},
"tags": [
"example",
"sample"
],
"content": "This will be unchanged"
},
"patch": {
"title": "Hello!",
"phoneNumber": "+01-123-456-7890",
"author": {
"familyName": null
},
"tags": [
"example"
]
},
"expected": {
"title": "Hello!",
"author": {
"givenName": "John"
},
"tags": [
"example"
],
"content": "This will be unchanged",
"phoneNumber": "+01-123-456-7890"
},
"merge": true
},
{
"comment": "replacing non-object",
"doc": {
"title": "Goodbye!",
"author": {
"givenName": "John"
},
"tags": [
"example",
"sample"
],
"content": "This will be unchanged"
},
"patch": {
"tags": {
"kind": "example"
}
},
"expected": {
"title": "Goodbye!",
"author": {
"givenName": "John"
},
"tags": {
"kind": "example"
},
"content": "This will be unchanged"
},
"merge": true
}
]

View File

@@ -0,0 +1,286 @@
[
{
"comment": "Can revert add (replace key)",
"doc": {
"foo": {
"bar": {
"baz": true
}
}
},
"patch": [
{
"op": "add",
"path": "/foo",
"value": false
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
},
{
"comment": "Can revert add (insert into array)",
"doc": {
"foo": [1, 2, 3]
},
"patch": [
{
"op": "add",
"path": "/foo/1",
"value": false
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
},
{
"comment": "Can revert add (insert last element into array)",
"doc": {
"foo": [1, 2, 3]
},
"patch": [
{
"op": "add",
"path": "/foo/-",
"value": false
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
},
{
"comment": "Can revert remove (object)",
"doc": {
"foo": {
"bar": {
"baz": true
}
}
},
"patch": [
{
"op": "remove",
"path": "/foo"
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
},
{
"comment": "Can revert remove (array)",
"doc": {
"foo": [1, 2, 3]
},
"patch": [
{
"op": "remove",
"path": "/foo/1"
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
},
{
"comment": "Can revert replace (replace key)",
"doc": {
"foo": {
"bar": {
"baz": true
}
}
},
"patch": [
{
"op": "replace",
"path": "/foo",
"value": false
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
},
{
"comment": "Can revert replace (replace array element)",
"doc": {
"foo": [1, 2, 3]
},
"patch": [
{
"op": "replace",
"path": "/foo/1",
"value": false
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
},
{
"comment": "Can revert move (move into key)",
"doc": {
"foo": {
"bar": {
"baz": true
}
},
"abc": {
"def": {
"ghi": false
}
}
},
"patch": [
{
"op": "move",
"from": "/abc",
"path": "/foo",
"value": false
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
},
{
"comment": "Can revert move (move into array)",
"doc": {
"foo": [1, 2, 3],
"abc": {
"def": {
"ghi": false
}
}
},
"patch": [
{
"op": "move",
"path": "/foo/1",
"from": "/abc"
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
},
{
"comment": "Can revert move (move into last element of an array)",
"doc": {
"foo": [1, 2, 3],
"abc": {
"def": {
"ghi": false
}
}
},
"patch": [
{
"op": "move",
"path": "/foo/-",
"from": "/abc"
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
},
{
"comment": "Can revert copy (copy into key)",
"doc": {
"foo": {
"bar": {
"baz": true
}
},
"abc": {
"def": {
"ghi": false
}
}
},
"patch": [
{
"op": "copy",
"from": "/abc",
"path": "/foo",
"value": false
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
},
{
"comment": "Can revert copy (copy into array)",
"doc": {
"foo": [1, 2, 3],
"abc": {
"def": {
"ghi": false
}
}
},
"patch": [
{
"op": "copy",
"path": "/foo/1",
"from": "/abc"
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
},
{
"comment": "Can revert copy (copy into last element of an array)",
"doc": {
"foo": [1, 2, 3],
"abc": {
"def": {
"ghi": false
}
}
},
"patch": [
{
"op": "copy",
"path": "/foo/-",
"from": "/abc"
},
{
"op": "remove",
"path": "/foo/bar"
}
],
"error": "invalid pointer"
}
]

View File

@@ -0,0 +1,343 @@
[
{
"comment": "4.1. add with missing object",
"doc": {
"q": {
"bar": 2
}
},
"patch": [
{
"op": "add",
"path": "/a/b",
"value": 1
}
],
"error": "path /a does not exist -- missing objects are not created recursively"
},
{
"comment": "A.1. Adding an Object Member",
"doc": {
"foo": "bar"
},
"patch": [
{
"op": "add",
"path": "/baz",
"value": "qux"
}
],
"expected": {
"baz": "qux",
"foo": "bar"
}
},
{
"comment": "A.2. Adding an Array Element",
"doc": {
"foo": [
"bar",
"baz"
]
},
"patch": [
{
"op": "add",
"path": "/foo/1",
"value": "qux"
}
],
"expected": {
"foo": [
"bar",
"qux",
"baz"
]
}
},
{
"comment": "A.3. Removing an Object Member",
"doc": {
"baz": "qux",
"foo": "bar"
},
"patch": [
{
"op": "remove",
"path": "/baz"
}
],
"expected": {
"foo": "bar"
}
},
{
"comment": "A.4. Removing an Array Element",
"doc": {
"foo": [
"bar",
"qux",
"baz"
]
},
"patch": [
{
"op": "remove",
"path": "/foo/1"
}
],
"expected": {
"foo": [
"bar",
"baz"
]
}
},
{
"comment": "A.5. Replacing a Value",
"doc": {
"baz": "qux",
"foo": "bar"
},
"patch": [
{
"op": "replace",
"path": "/baz",
"value": "boo"
}
],
"expected": {
"baz": "boo",
"foo": "bar"
}
},
{
"comment": "A.6. Moving a Value",
"doc": {
"foo": {
"bar": "baz",
"waldo": "fred"
},
"qux": {
"corge": "grault"
}
},
"patch": [
{
"op": "move",
"from": "/foo/waldo",
"path": "/qux/thud"
}
],
"expected": {
"foo": {
"bar": "baz"
},
"qux": {
"corge": "grault",
"thud": "fred"
}
}
},
{
"comment": "A.7. Moving an Array Element",
"doc": {
"foo": [
"all",
"grass",
"cows",
"eat"
]
},
"patch": [
{
"op": "move",
"from": "/foo/1",
"path": "/foo/3"
}
],
"expected": {
"foo": [
"all",
"cows",
"eat",
"grass"
]
}
},
{
"comment": "A.8. Testing a Value: Success",
"doc": {
"baz": "qux",
"foo": [
"a",
2,
"c"
]
},
"patch": [
{
"op": "test",
"path": "/baz",
"value": "qux"
},
{
"op": "test",
"path": "/foo/1",
"value": 2
}
],
"expected": {
"baz": "qux",
"foo": [
"a",
2,
"c"
]
}
},
{
"comment": "A.9. Testing a Value: Error",
"doc": {
"baz": "qux"
},
"patch": [
{
"op": "test",
"path": "/baz",
"value": "bar"
}
],
"error": "string not equivalent"
},
{
"comment": "A.10. Adding a nested Member Object",
"doc": {
"foo": "bar"
},
"patch": [
{
"op": "add",
"path": "/child",
"value": {
"grandchild": {}
}
}
],
"expected": {
"foo": "bar",
"child": {
"grandchild": {
}
}
}
},
{
"comment": "A.11. Ignoring Unrecognized Elements",
"doc": {
"foo": "bar"
},
"patch": [
{
"op": "add",
"path": "/baz",
"value": "qux",
"xyz": 123
}
],
"expected": {
"foo": "bar",
"baz": "qux"
}
},
{
"comment": "A.12. Adding to a Non-existent Target",
"doc": {
"foo": "bar"
},
"patch": [
{
"op": "add",
"path": "/baz/bat",
"value": "qux"
}
],
"error": "add to a non-existent target"
},
{
"comment": "A.13 Invalid JSON Patch Document",
"doc": {
"foo": "bar"
},
"patch": [
{
"op": "add",
"path": "/baz",
"value": "qux",
"op": "remove"
}
],
"error": "operation has two 'op' members",
"disabled": true
},
{
"comment": "A.14. ~ Escape Ordering",
"doc": {
"/": 9,
"~1": 10
},
"patch": [
{
"op": "test",
"path": "/~01",
"value": 10
}
],
"expected": {
"/": 9,
"~1": 10
}
},
{
"comment": "A.15. Comparing Strings and Numbers",
"doc": {
"/": 9,
"~1": 10
},
"patch": [
{
"op": "test",
"path": "/~01",
"value": "10"
}
],
"error": "number is not equal to string"
},
{
"comment": "A.16. Adding an Array Value",
"doc": {
"foo": [
"bar"
]
},
"patch": [
{
"op": "add",
"path": "/foo/-",
"value": [
"abc",
"def"
]
}
],
"expected": {
"foo": [
"bar",
[
"abc",
"def"
]
]
}
}
]

1877
json-patch/specs/tests.json Normal file

File diff suppressed because it is too large Load Diff