feltdb-client 0.4.3

Native async Rust client for a FeltDB server: records, atomic and idempotent transactions, compare-and-set, bounded queries, read-basis fences, events, and the canonical application API
Documentation
# feltdb-client

A native async Rust client for a [FeltDB](https://github.com/rkendel1/flow_db) server: the Rust
counterpart of `@feltdb/core`'s server runtime. It holds no state of its own; the server is the
authority for every read and write.

```toml
[dependencies]
feltdb-client = "0.2"
serde_json = "1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
```

```rust
use feltdb_client::{Client, Operation, Precondition, Transaction};
use serde_json::json;

#[tokio::main]
async fn main() -> feltdb_client::Result<()> {
    let client = Client::builder("http://127.0.0.1:7700")
        .token(std::env::var("FELTDB_TOKEN").unwrap_or_default())
        .build()?;

    // One atomic commit. The id makes a retry after an uncertain outcome safe.
    let tx = Transaction::new("ship-o-1")
        .operation(Operation::put("orders", "o-1", json!({ "status": "shipped" })).expect_version(1))
        .operation(Operation::create("audit", "a-1", json!({ "order": "o-1" })))
        .precondition(Precondition::absent("locks", "o-1"));
    match client.commit(&tx).await {
        Ok(receipt) => println!("committed at revision {}", receipt.commit_revision),
        Err(error) if error.is_conflict() => println!("lost a race: {:?}", error.precondition_failure()),
        Err(error) if error.is_outcome_unknown() => { /* resubmit `tx` unchanged */ }
        Err(error) => return Err(error),
    }
    Ok(())
}
```

## What it covers

| Server surface | Client |
| --- | --- |
| Records: `GET/POST /collections/…`, `PATCH`, `DELETE` | `get`, `list`, `create`, `update`, `delete` |
| Atomic, idempotent transactions with preconditions and fences: `POST /transactions` | `commit` with `Transaction`, `Operation`, `Precondition` |
| Read bases: `POST /transactions/read-basis` | `read_basis`, then `Transaction::fence` |
| Compare-and-set: `POST /collections/{c}/{id}/cas` | `compare_and_set` → `CasOutcome` |
| Bounded query: `POST /query` | `query` with `BoundedQuery` |
| Committed revision: `GET /revision` | `revision` |
| Live events: `GET /events` (SSE) | `events` → `EventStream` |
| Canonical application API under `/v1` | `client.application(id)`: `discover`, `query`, `commit` (per-operation `if_version` via `update_if_version`/`delete_if_version`), `read_basis`, `events`, `replay`, `provenance`, `sign_in`/`sign_up`/`session`/`sign_out` |
| Committed state version: `GET /v1/state/version` | `application.state_version()` |
| Control plane: `/api/tenants`, `/api/applications/…` drafts, revisions, promotion; `POST /admin/backups` | `client.control()`: `create_tenant`, `create_application`, `create_draft`, `update_draft`, `validate_draft`, `commit_draft`, `revision`, `promote`, `backup` |
| Health: `GET /health` | `health` |
| Capability discovery | `capabilities` |
| Anything else | `request(Method, path, body)` |

Errors carry the server's stable `code`, its `request_id`, and for a refused transaction the
structured `PreconditionFailure`. The canonical query and transaction documents are `feltdb`'s
own types (re-exported under `feltdb_client::wire`), so the client and server serialize the same
structs.

A server behind a private CA is trusted with
`Client::builder(url).add_root_certificate_pem(pem)`, in addition to the bundled public roots; verification is never
disabled.

The event stream does not reconnect by itself; reopen it from `EventStream::last_event_id()`.
For an embedded store in your own process, use the [`feltdb`](https://crates.io/crates/feltdb)
crate instead; to run the server, `cargo install feltdb-server`.

License: MIT.