Expand description
The replica’s JSON wire surface: the shared write-plane codec re-exported wholesale,
plus the two renderings that read the ENGINE’s own types and therefore cannot live in
the write plane — the flat-change / wire-schema dialect every native host ships to
its client (flat_changes_to_json, wire_schema_to_json) and the SSR assembled
snapshot (assembled_snapshot_to_json).
Both read the engine’s change model (FlatChange/WireSchema, CaughtChange/
CaughtNode) — the IVM types a Graph produces. rindle-writeplane is engine-free
by construction (it depends on rindle-value, not rindle), so this is the lowest
crate that can host them. Everything else in the wire codec still lives in the write
plane and is re-exported below, so every downstream rindle_replica::wire_json::…
import site is unchanged.
§One dialect, every native home
The flat-change rendering is byte-for-byte the wasm Db’s (rindle/src/wasm/ marshal.rs): bare cells (number | string | boolean | null), camelCase keys,
{ tag }-tagged ops — so the SAME @rindle/client ArrayView folds either backend’s
stream. It used to live in the napi addon (rindle-replica-node/src/marshal.rs); it
was lifted here (design 410 PR 0) when a SECOND native home — rindle-mobile’s Swift
binding — needed the same bytes. Two copies of a wire dialect drift; one function
cannot. The addon’s marshal now re-exports these, and rindle-mobile’s
tests/json_shape.rs pins the output against literal strings.
Constants§
- ABSENT_
WIRE_ KEY - The wire marker for an absent cell —
{"$absent": true}.
Functions§
- absent_
marker - assembled_
snapshot_ to_ json - Render a query’s assembled view snapshot — the
Addhydration changes (Graph::hydrate_change_sink) read for the SSR one-shot — to the flat, already-joined wire shape (SSR-DESIGN.md§3.3). Unlike the normalized per-table ops, the view is handed back constructed: each result node is{ cols, <rel>: [..] }, with relationships nested inline by name, so a stateless API server (which runs no IVM engine) can use it directly. Cells are keyed by column name and honor the query’s projection;.one()levels surface as a single object (ornull); scalar-projected relationship aggregates (countAs) surface as a single scalar field. - flat_
changes_ to_ json - A batch of flat changes → the JS/Swift-shaped array (the hydrate snapshot, or one commit’s events).
- is_
absent_ marker - json_
to_ owned - A bare JS cell →
OwnedValue, inferring from the runtime JSON type (matches the wasmjs_to_owned_value: number→Float always, string→Str, bool→Bool, null→Null). A json column already crossed as its stringified form, so it lands here as aString. - mutation_
from_ json - Parse one change-source ingest row change (see
mutation_to_json) into aMutation.Errcarries a human reason for the malformed cell/op (the caller maps it to the wire error). Onlyadd/remove/editexist today; the partialupdateop is deferred with the Postgres connector (CHANGE-SOURCE-DESIGN.md §3, §8.4). - mutation_
to_ json - The change-source ingest shape of one row change (the contract the daemon’s
/apply-row-change-txnendpoint, the master’s change-log chunks, and the replicator fan-out all speak):add → { row },remove → { old },edit → { old, row }(rowis the NEW row, and only PK cells ofoldare read on apply). Distinct fromnormalized_op_to_json, which is the OUTBOUND batch wire (editcarriesnew) and deliberately collapsesIntto a JS number; this codec is integer-exact (design 226 Stage D — seeingest_cell_to_json). One codec so the master payload, the fan-out frames, and the HTTP ingest never drift. CAVEAT: the room’s write-behind flush emits this same ingest SHAPE through a different encoder (rindle-wire::wire_json::row_change_to_json), which still rendersIntin float form — decode-side consumers pattern-matchingOwnedValue::Inton flushed rows (the lmid outcome-retention prune) therefore never seeIntfrom that emitter today; unifying it onto this codec would activate them. - nbatch_
frame - Assemble one full
nbatchframe forclient_qid, splicing the already-encodedops_fragmentinto the scalar envelope. Byte-identical to thejson!frame inRouterSink::batch:{"batch":{"cv":…,"epoch":…,"normalizedFp":"…","ops":…,"seq":…}, "queryId":…,"t":"nbatch"}(all keys alphabetical). - nbatch_
ops_ fragment - Encode
batch.opsas its[…]JSON array — the fragmentnbatch_framesplices into each subscriber’s envelope.RouterSink::batchencodes it ONCE per batch, outside the router lock, then assembles a frame per subscriber (only the envelope’squeryId/seq/epochdiffer across a fan-out). - normalized_
batch_ from_ json - Parse an
nbatchframe’sbatchobject into aNormalizedBatch. - normalized_
batch_ to_ json - A
NormalizedBatch→{ epoch, seq, cv, normalizedFp, ops }. The fingerprint is a hex string (a u64 would lose precision as a JS number);cvis the commit version the optimistic client buffers by (OPTIMISTIC-WRITES-DESIGN.md §8.6). - normalized_
hello_ from_ json - Parse an
nhelloframe’shelloobject into aNormalizedHello(shape only — see the module docs for where semantic validation lives). - normalized_
hello_ to_ json - A
NormalizedHello→{ epoch, comparatorVersion, tables: [{ name, columns, primaryKey }], normalizedFp }— the slim per-table-schema handshake (§3). - normalized_
op_ from_ json - Parse one
{ table, op, … }wire object into aNormalizedOp. Note the shape is the OUTBOUND batch wire —removecarriesrowandeditcarriesold/new— distinct from the change-source ingest shape (rindle-replica’smutation_from_json), whereremovecarriesoldandedit’s new row rides asrow. - normalized_
op_ to_ json - A
NormalizedOp→ its camelCase JS object:{ table, op, row | old/new }(bare cells), the path-free twin of a flat change (NORMALIZED-CHANGES-DESIGN.md §3). - owned_
to_ json OwnedValue→ a bare JS value.Int/Floatcollapse to a JSnumber(the engine’s single numeric model);Str/Jsonto a string (a json column is parsed at read time by the view’s typed schema, exactly as on the local path).- wire_
row_ from_ json - One JSON array of bare cells → a positional wire row.
- wire_
row_ to_ json - One positional wire row → its JSON array of bare cells.
- wire_
schema_ to_ json - A
WireSchema→ the camelCase object theArrayViewbuilds from (handed once inhello):{ columns, primaryKey, sort: [col, asc][], singular, relationships }.