mirror of
https://github.com/Start9Labs/patch-db.git
synced 2026-03-26 02:11:54 +00:00
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>
2.5 KiB
2.5 KiB
patch-db
A database that tracks state updates as RFC 6902 JSON Patches. Enables observable, event-driven state management with a Rust backend and TypeScript client.
Overview
patch-db stores your application state as a single JSON document. Instead of opaque writes, every mutation is recorded as a JSON Patch — a sequence of add/remove/replace operations. Subscribers receive only the patches relevant to the subtree they're watching, making it efficient for UIs that need to react to fine-grained state changes.
Key properties
- Event-sourced — patches are the source of truth, not snapshots
- Observable — subscribers watch arbitrary subtrees via JSON Pointers and receive scoped patches in real time
- Persistent — the Rust backend writes to disk with CBOR serialization, automatic compaction, and crash-safe backup files
- Type-safe — derive macros on the Rust side; generic type parameters and deep
watch$()overloads on the TypeScript side - Immutable values — the Rust side uses
imbl_value::Valuefor efficient structural sharing
Quick start
Rust
Add to your Cargo.toml:
[dependencies]
patch-db = { git = "https://github.com/Start9Labs/patch-db" }
use patch_db::PatchDb;
use json_ptr::ROOT;
#[tokio::main]
async fn main() -> Result<(), patch_db::Error> {
let db = PatchDb::open("my.db").await?;
// Write a value
db.put(&ROOT, &serde_json::json!({ "count": 0 })).await?;
// Read it back
let dump = db.dump(&ROOT).await;
println!("revision {}: {}", dump.id, dump.value);
// Subscribe to changes
let mut watch = db.watch(ROOT.to_owned()).await;
// watch implements Stream — use it with tokio, futures, etc.
Ok(())
}
TypeScript
import { PatchDB, Dump, Update } from 'patch-db'
import { Observable } from 'rxjs'
interface AppState {
users: { [id: string]: { name: string; online: boolean } }
settings: { theme: string }
}
// source$ delivers updates from the server (WebSocket, SSE, etc.)
const source$: Observable<Update<AppState>[]> = getUpdatesFromServer()
const db = new PatchDB<AppState>(source$)
db.start()
// Watch a deeply nested path — fully type-safe
db.watch$('settings', 'theme').subscribe(theme => {
console.log('Theme changed:', theme)
})
Further reading
- ARCHITECTURE.md — project structure, crate/package details, data flow, storage format
- CONTRIBUTING.md — environment setup, build commands, testing, code style
License
MIT