$ data a reactive data engine for typescript

the original sin of reactive UI

Only do the work that changed.

Almost every reactive system re-runs the whole computation when a single value changes. data is a reactive data engine that moves only the change: $() wraps a value, operators derive incrementally-maintained views, sinks deliver consolidated deltas, render binds to the DOM. Work scales with the change, not the dataset.

Like crossfilter's incremental aggregation meets Solid-style fine-grained DOM — for UIs over large, fast-changing data: dashboards, analytics, grids, live feeds.

quickstart ↓ github →

0 dependencies · type-stripped, not bundled · SCHEDULE_VERSION 4 · 330+ tests · MIT

one write, one path

Only the change moves.

Almost every reactive system re-runs the whole computation when a single value changes. data is a reactive data engine that moves only the change: $() wraps a value, operators derive incrementally-maintained views, sinks deliver consolidated deltas, render binds to the DOM. Work scales with the change, not the dataset.

Like crossfilter's incremental aggregation meets Solid-style fine-grained DOM — for UIs over large, fast-changing data: dashboards, analytics, grids, live feeds.

$(trades) object-born · keyed · rows
write trades.t3.qty.update()
keysymsideqtypx
delta → seq
quickstart ↓ github →

— dependencies · type-stripped, not bundled · SCHEDULE_VERSION — · — tests · MIT

change & derivation

A table and its change-stream are one thing.

A write is a delta — three row verbs, add · update · remove, in one batch envelope — and every operator consumes and emits exactly that. So a filter, a count, a grouping stays live for the price of the change, not the table. data keeps the table and its stream as one thing: wrap a value in $(), chain operators, bind to the DOM.

Like crossfilter's incremental aggregation meets Solid-style fine-grained DOM — for UIs over large, fast-changing data: dashboards, analytics, grids, live feeds.

quickstart ↓ github →

0 dependencies · type-stripped, not bundled · SCHEDULE_VERSION 4 · 330+ tests · MIT

one engine · two workloads · flip through nine
1 / 9
datav4 length(bucket)×2 · filter→length · avg — O(1)/tick —
priceqtycum
cpu per framebaseline · dashed line = 16ms / 60fps budget
liquid orders—
avg bid—
data baseline—
ticks / sec—
↓ same engine · interactive brushing · 231,083 flight rows — drag any chart
datav4
between → intersect → length(fn) · O(Δ)/brush
    — / 231,083
    p50—ms p95—ms n0

    The same engine you pick above, doing two jobs. Top: one order-book tick stream through the same depth-chart-and-ladder viz — every tick is a deep-path write, and data moves a couple of bucket counters per delta (length(bucket) is aggregate-decomposable: O(1) per change); every peer re-walks all 150,000 orders once per frame (O(N)), and the green baseline is data against the 16 ms budget. Bottom: the same library filtering 231,083 flight rows as you brush — a brush is one filters.get(name).update([lo, hi]), and between walks only the rows entering or leaving the window. Every implementation is the real library doing the idiomatic thing; every number is this machine's.

    one engine · two workloads · every number measured on this page
    order book 150,000 orders · length(bucket)×2 · filter→length · avg —
    priceqtycum
    cpu per frameone commit per frame · dashed line = 16ms / 60fps budget
    brushing 231,083 flight rows · between → intersect → length(fn) —
    drag any chart — — of 231,083 rows · —
    liquid orders—
    avg bid—
    ticks / sec—
    commits / sec—

    Two workloads, one engine, nothing to hide behind. Left: a 150,000-order book ticking at the rate you set — each tick is one deep-path write, all of a frame's ticks land in one consolidated commit, and length(bucket)×2, filter→length and avg move a counter apiece, so the frame cost is the flat line under the 16 ms budget. Right: 231,083 flight rows under four brushes — a brush is one filters.get(name).update([lo, hi]); between walks only the rows crossing the boundary, and intersect and length(fn) fold the deltas. The figures are this machine's, measured as you watch.

    follow one write · trades.t3.qty.update(v) · four sinks
    seq — SCHEDULE_VERSION 4 · SCHEMA_VERSION 3
    1

    Write

    render(list(trades))

    drag the number — every move is trades.t3.qty.update(v)

    const trades = $({ t3: { sym: 'NVDA', qty: 500, … }, … })
    batch(() => samples.forEach(v => trades.t3.qty.update(v)))
    2

    Delta

    trades.sink({ init, apply })

    —

    —

    
    
    trades.sink({
      init:  snapshot => show(snapshot),
      apply: b => show(b),  // { seq, origin, rows, order, scalar }
    })
    3

    Flow

    runtime().onCommit(c => …)
    h0 · source trades 4 rows h1 · filter buys 3 rows h2 · za('qty', 2) largest h2 · median('px') typical 187.5 h2 · sum('qty') volume

    —

    const buys = trades.filter(t => t.side === 'buy')
    const largest = buys.za('qty', 2), typical = buys.median('px')
    const volume = buys.sum('qty');    runtime().onCommit(trace)
    4

    Sink

    two sinks · one batch
    dom · render(list(largest))
      —
      wire · WireBatch → ingest(replica)
      —
      
      
      render(ol, list(largest, t => li(b(t.sym), t.qty)))
      wireSink(largest, wb => ingest(replica, wb.records))

      You dragged one number and watched it travel. The write became one delta carrying its path and its previous value; it flowed through exactly the views that read it — typical never lit, because a median over px learns nothing from a qty — and two sinks received the same consolidated batch: one rendered a list with a single DOM instruction, one replayed it into a replica that stayed identical. That is the whole engine — three row verbs, three order verbs, one envelope, and work proportional to the change.

      one engine · two workloads · flip through nine
      1 / 9
      order book · — orders · deep-path writes — datav4 length(bucket)×2 · filter→length · avg×2 — O(1)/tick
      priceqtycum
      last deltas
      cpu per framebaseline · dashed line = 16 ms / 60 fps budget
      brushing · loading the flights · drag any chart — datav4 between → intersect → length(fn) · O(Δ)/brush
      — of — rows · — ms p50 —p95 —n —
      liquid orders—
      avg bid—
      data baseline—
      ticks / sec—

      The same engine you pick above, doing two jobs. Left: a -order book ticking at the rate you set — every tick is one deep-path write, a frame's ticks land in one commit, and data moves a bucket counter apiece (length(bucket) is aggregate-decomposable: O(1) per delta) while every peer re-walks the whole book once per frame (O(N)); green is data against the 16 ms budget. Right: the flight rows under four brushes — a brush is one filters.get(name).update([lo, hi]); between walks only the rows crossing the boundary, intersect and length(fn) fold the deltas, and the ms/brush is that write, settle included, timed on every pointer move as you drag — under a peer, the same moves through that library's filters and histograms, in the same window. Every implementation is the real library doing the idiomatic thing; crossfilter's index is incremental too, and its walk is a tighter loop over the same crossing rows.

      01

      The argument

      A change is small. You set one field on one row. Yet most reactive stacks respond by re-running everything downstream — re-filtering the whole list, re-summing every number, diffing an entire tree — and then trusting a memoiser to paper over the waste.

      data takes the change literally. A write carries its exact path, and each operator in the chain does work scoped to that path alone:

      trades.t3.qty.update(380)   // one delta · path ['qty'] · prev 500
      • filter re-tests the predicate for one row
      • za moves one rank
      • sum nudges a running total in O(1)
      • render rewrites only the single bound cell

      No diff pass. No list re-render. No selector re-run over 231,083 rows. The cost of an update is the length of the path it travels — which is why the race above holds at any rate, and why it scales to datasets that make recompute-everything systems stall.

      01

      The argument

      A change is small. You set one field on one row. Yet most reactive stacks respond by re-running everything downstream — re-filtering the whole list, re-summing every number, diffing an entire tree — and then trusting a memoiser to paper over the waste.

      data takes the change literally. A write carries its exact path, and each operator in the chain does work scoped to that path alone:

      write trades.t3.qty.update() sourcetrades · rows row t3 · qty one call · one deep path
      delta batchseq · rows one row verb · carries prev
      views
      buys largest volume
      dom
      boundqty → text node t3 · qty text · added · removed
      render · the one bound cell
      seq · origin · nodes settled · write → dom in ms · this machine · text node

      No diff pass. No list re-render. No selector re-run over the whole table. The cost of an update is the length of the path it travels — which is why the race above holds at any rate, and why it scales to datasets that make recompute-everything systems stall.

      02

      The operator surface

      One source, many derived views. Wrap a collection in $() and chain operators; each returns a view maintained incrementally from the deltas its source emits — the same three row verbs and three order verbs, in and out. Every row below runs live off one shared $(trades) feed — pick a side, drag a bound, watch the top-5 reshuffle, see the groups recount as qty and px tick.

      $(trades) · 12 rows · seq 0 · —

      rowop 10one row in, one row out — the delta passes straight through
      filterrows matching a predicatetrades.filter(t => t.side === 'buy')O(Δ)
      ——
      mapper-row transformtrades.map(t => t.qty * t.px)O(Δ)
      gt / lt / gte / ltesingle-threshold comparisontrades.gt('qty', 300)O(Δ)
      betweencolumn in a reactive rangetrades.between('px', [100, 200])O(Δ)
      boundsa value handle — drag to write it
      —of 12 rows · px ∈ [100, 200]

      const bounds = $([100, 200]) · trades.between('px', bounds) · a drag is one write — bounds.set(0, 100) — and the view re-bounds

      between('px', [100, 200])—
      intersectrows in every view, by keybuys.intersect(large)O(Δ)
      union / exceptset algebra over viewsbuys.except(large)O(Δ)
      aggregate-decomposable 7maintained from the delta alone — prev out, next in
      sum / avga running scalar, nudged per deltatrades.avg('px')O(Δ)
      avg('px')
      —
      sum('qty')
      —

      —

      lengthrow count, or counts per keytrades.length(t => t.sym)O(Δ)
      grouprows nested under a computed keytrades.group(t => t.sym)O(Δ)
      lengthBucketscounts per key; empties stay at 0trades.lengthBuckets(t => band(t.qty))O(Δ)
      some / everyany / all rows matchingtrades.some(t => t.qty > 1000)O(Δ)
      holistic 13needs an order or the whole set — an index does the work
      az / zasorted; with n, the top-nbuys.za('qty', 5)O(Δ log n)
      buys.za('qty', 5)—
      top / limitthe first n of an ordered viewtrades.az('px').top(3)O(Δ log n)
      reversethe order, flippedtrades.reverse()O(n)
      max / minthe extreme, by column or rowtrades.max('px')O(Δ log n)
      reducea fold with an add and a removetrades.reduce(add, remove, 0)O(Δ)
      distinctfirst-seen unique by projectiontrades.distinct(t => t.sym)O(Δ)
      towhole-value transform, sees prevtrades.to(v => Object.keys(v).length)O(n)
      quantile / percentile / medianR-type-7 over a columntrades.median('px')O(Δ log n)
      median('px')
      —
      percentile('px', 90)
      —

      —

      iter 3side effects and projections — no view state of their own
      tapa side effect per change recordtrades.tap(c => log(c))O(1)
      keys / valuesprojections over the shapetrades.keys()O(1)

      Every row is the real operator on a live feed; the cost column is the class of work a change costs, not a benchmark. 33 operators in 4 categories — exactly what exportContract() reports.

      02

      The operator surface

      One source, many derived views. Wrap a collection in $() and chain operators; each returns a view maintained incrementally from the deltas its source emits — the same three row verbs and three order verbs, in and out. All below run live off one shared $(trades) feed — pick any operator and its result ticks in the panel as qty and px move.

      $(trades) · rows · seq · —

      rowop

      one row in, one row out — the delta passes straight through

      aggregate-decomposable

      maintained from the delta alone — prev out, next in

      holistic

      needs an order or the whole set — an index does the work

      iter

      side effects and projections — no view state of their own

      filter
      
                

      cost per change · this operator · nine engines

      the multiple is data's cost as the unit; peers re-walk the source on every change — nothing prints here until it has been timed in this tab

      Every chip is the real operator on a live feed. The cost word is the class of work a change costs; the multiple is that cost against eight peers doing the idiomatic thing — re-walking the source per change — measured in this tab on demand, and the figure on the panel is the peak, the widest gap. operators in categories — exactly what exportContract() reports.

      03

      From zero

      one package, nothing bundled — dist/ is the source type-stripped file-for-file, one module per file. In the repo it stays source-served: Node runs the .ts with --experimental-strip-types; this page runs the same files, type-stripped the same way.

      api/the public surface — $(), the verbs, the operators, batch, runtime. The one entry you import.
      seam/the outside-world boundary — ingest, lane, wireSink, mount
      render/ + jsx/el / list / text, the HTML / SVG builders, classic + automatic JSX
      devtools/the inspector panel + DOM overlay — mounts on demand, never auto

      a source and its views

      import { $ } from 'data'
      
      const trades = $({
        t1: { sym: 'AAPL', side: 'buy',  qty: 200, px: 187.5 },
        t2: { sym: 'MSFT', side: 'sell', qty: 120, px: 412.1 },
        t3: { sym: 'NVDA', side: 'buy',  qty: 500, px: 118.4 },
      })
      const buys    = trades.filter(t => t.side === 'buy')
      const largest = buys.za('qty', 2)
      const volume  = buys.sum('qty')
      
      trades.t3.qty.update(380)   // → update t3 .qty 500 → 380

      bound to the DOM

      import { render, el, list, batch } from 'data'
      
      const ol = document.querySelector('ol')
      
      render(ol, list(largest, t =>
        el('li', null, el('b', null, t.sym), t.qty)))
      
      batch(() => {
        trades.t3.qty.update(380)
        trades.t1.qty.update(260)
      })   // one commit, one paint

      JSX works too (jsxImportSource: "data" — classic h or the automatic runtime). render() does per-key surgical updates — element identity and focus survive across changes.

      03

      From zero

      one package, nothing bundled — dist/ is the source type-stripped file-for-file, one module per file. In the repo it stays source-served: Node runs the .ts with --experimental-strip-types; this page runs the same files, type-stripped the same way.

      twelve lines · one import · one package
      import { $, batch, render, el, list } from 'data'
      const trades = $({ t1: { sym: 'AAPL', side: 'buy', qty: 200, px: 187.5 },
      t2: { sym: 'MSFT', side: 'sell', qty: 120, px: 412.1 },
      t3: { sym: 'NVDA', side: 'buy', qty: 500, px: 118.4 } })
      const buys = trades.filter(t => t.side === 'buy')
      const largest = buys.za('qty', 2)
      const volume = buys.sum('qty')
      trades.sink({ apply: b => console.log(b) })
      render(ol, list(largest, t => el('li', null, el('b', null, t.sym), t.qty)))
      batch(() => trades.t3.qty.update(380))
      ol · live
        04

        See your graph

        devtools/ ships an inspector panel and a DOM overlay. The panel is a right-edge dock: the live graph as a Tree or a DAG, every commit's deltas as they settle, and the settle time of each node. It mounts on demand — never on its own — and the console API alone ($.inspect, $.graph, $.trace, $.profile, $.cascades) doesn't pay the panel's bytes. The overlay walks the other way: $.fromDOM($0) picks any bound element back to the view that rendered it; $.highlight(view) lights up its rows.

        not mounted · this page runs a five-node graph

        Everything the panel shows is a projection of two runtime primitives — runtime().graph() and runtime().onCommit(c => …) — so the numbers are the kernel's own, measured only while a hook is live.

        04

        See your graph

        devtools/ ships an inspector panel and a DOM overlay. The panel is a right-edge dock — this is it, drawn inline over a five-node graph this section runs on the page's engine, with a scripted writer feeding it real writes. devtools/index.ts alone — the graph, trace and profile helpers this page imports — never loads the panel; the data/devtools entry also docks it on import unless the URL says ?nopanel, and devtools/panel/ mounts only through mountPanel().

        • Tree / DAGthe live graph — id · kind · op · height · parents, from runtime().graph()
        • Eventsone line per commit: the write, the cascade in settle order, deltas per node
        • settle msper-node timing from onCommit — measured only while a hook is live
        • $.fromDOM($0)the overlay: any bound element → the view that rendered it; $.highlight(view) lights its rows

        Console-only: $.inspect(h) · $.graph() · $.trace(fn) · $.profile(fn) · $.cascades(fn). $.trace subscribes for the duration of fn and disposes after — tracing has zero steady-state cost.

        lazy — devtools/panel/ loads on click

        $.devtools.panel.open() / .close() drive it from the console in any page that imports data/devtools — $.inspect(h) alone stays in the console.

        05

        The contract

        Everything runs under an executable timing contract — SCHEDULE.md, SCHEDULE_VERSION 4: eleven numbered clauses, each pinned by a test, and the same test file runs in the consumer's CI against data HEAD.

        1. 01Apply phase every write routes through the runtime's commit1 test✓
        2. 02Read-your-writes source reads see post-write values inside the batch1 test✓
        3. 03Flush phase consolidated batches settle height-ordered at batch close1 test✓
        4. 04Effects last, isolated effect sinks run after settle; a throwing sink can't corrupt state (AggregateError)1 test✓
        5. 05Re-entrancy a write inside an effect queues as the next commit2 tests✓
        6. 06Origin tokens every batch carries its origin; replicas suppress their own echo1 test✓
        7. 07Snapshot-then-deltas a sink attached between commits gets init(snapshot) then only deltas (a mid-batch attach defers to the commit)2 tests✓
        8. 08Emission legality at most one row delta per key per batch; add on an existing key is illegal; consolidation rules2 tests✓
        9. 09Coalescing opt-in coalesce('microtask' | 'frame') — sugar, not semantics1 test✓
        10. 10The deep-path law replication of nested writes: pathCopy / pathDelete (SCHEDULE_VERSION 2, fero W3)4 tests✓
        11. 11The value-domain portability table which JS values cross the wire intact (SCHEDULE_VERSION 3, W15)1 test✓
        read SCHEDULE.md →

        conform(node) attaches a legality checker and a replay sink to any view: every commit is re-folded and the fold must equal the view.

        05

        The contract

        Everything runs under an executable timing contract — SCHEDULE.md, SCHEDULE_VERSION —: eleven numbered clauses, each pinned by a test, and the same test file runs in the consumer's CI against data HEAD.

        lawproof · conformance/schedule.test.ts · executed in this tab·
        01Apply phaseevery write routes through the runtime's commit
        const trades = $({ t3: { sym: 'AAPL', qty: 500 } }), got = []
        trades.sink({ apply: b => got.push(b) })
        trades.t3.qty.update(380)  // a bare write = a synchronous batch of one
        print(`seq ${got[0].seq}: ${show(got[0].rows[0])} — delivered before update() returned`)
        // →
        trades.t3.qty.update(380)  // Object.is-equal at the leaf
        print(`nothing emitted: still ${got.length} batch, seq still ${runtime().seq}`)
        // →
        ·
        02Read-your-writessource reads see post-write values inside the batch
        const rows = $([{ sym: 'AAPL', qty: 200 }, { sym: 'NVDA', qty: 90 }]); let fired = 0
        rows.sink({ apply: b => print(`at batch close: the sink fires once (seq ${b.seq}) · fired ${++fired}`) })
        batch(() => {
          rows.insert({ sym: 'MSFT', qty: 40 })
          print(`mid-batch: rowCount() → ${rows.rowCount()}, the read sees the write · sinks fired: ${fired}`)
        })
        // →
        // →
        ·
        03Flush phaseconsolidated batches settle height-ordered at batch close
        const rows = $([{ qty: 50 }, { qty: 120 }, { qty: 300 }])
        const big = rows.filter(r => r.qty > 100), n = big.length()  // height 1, height 2
        const name = { [rows[node].id]: 'source', [big[node].id]: 'filter', [n[node].id]: 'length' }
        const hook = runtime().onCommit(c => print(`seq ${c.seq} settled ${c.nodes.map(x => name[x.id]).join(' → ')}, each once`))
        big.sink({ apply: () => print(`the sink fires after settle: n already reads ${n[value]}`) })
        rows.get(0).set('qty', 500); hook.dispose()  // 50 → 500 joins big; n 2 → 3
        // →
        // →
        ·
        04Effects last, isolatedeffect sinks run after settle; a throwing sink can't corrupt state
        const rows = $([{ qty: 1 }, { qty: 2 }]), was = rows.rowCount(); let sibling = 0
        rows.sink({ apply() { throw new Error('boom') } })
        rows.sink({ apply: () => sibling++ })
        try { rows.insert({ qty: 3 }) } catch (e) { print(`${e.constructor.name} after the drain: ${e.errors.length} sink failed (${e.errors[0].message})`) }
        print(`state intact: rowCount ${was} → ${rows.rowCount()}, the write committed · the sibling sink still fired ${sibling}×`)
        // →
        // →
        ·
        05Re-entrancya write inside an effect queues as the next commit
        const a = $({ n: 1 }), b = $({ n: 0 }), log = []
        a.sink({ apply: c => { log.push(`seq ${c.seq}: a.n = ${a.n[value]}`); b.n.update(a.n[value] * 2) } })
        b.sink({ apply: c => log.push(`seq ${c.seq}: b.n = ${b.n[value]}`) })
        a.n.update(21)  // an effect that writes
        print(`${log.join(' · ')} — the effect's write became the NEXT commit`)
        // →
        ·
        06Origin tokensevery batch carries its origin; replicas suppress their own echo
        const mine = Symbol('peer-A'), replica = $({}), calls = { mine: 0, other: 0 }
        replica.sink({ origin: mine, apply: () => calls.mine++ })  // declares what it must not hear
        replica.sink({ apply: b => { calls.other++; calls.echo = b.origin === mine } })
        ingest(replica, [{ t: 'add', k: 'a', v: { n: 1 } }], { origin: mine })
        print(`peer-A's own sink: ${calls.mine} applies, skipped · the other sink: ${calls.other} apply, batch.origin === mine: ${calls.echo}`)
        // →
        ·
        07Snapshot-then-deltasa sink attached between commits gets init(snapshot) then only deltas
        const rows = $([{ qty: 1 }, { qty: 2 }]), got = {}
        const sink = { init: s => (got.init = s.size), apply: b => (got.first ??= b.seq) }
        batch(() => { rows.insert({ qty: 3 }); rows.sink(sink); rows.insert({ qty: 4 }) })  // attached mid-batch
        print(`init saw ${got.init} rows — the settled batch (seq ${runtime().seq}), both inserts · applies so far: ${got.first ?? 'none'}`)
        rows.get(0).set('qty', 9)
        print(`apply first fired at seq ${got.first} — the NEXT commit`)
        // →
        // →
        ·
        08Emission legalityat most one row delta per key per batch; add on an existing key is illegal
        const rows = $([{ qty: 1 }]), t = $({ t: { qty: 500 } }), got = []
        rows.sink({ apply: b => got.push(b) }); t.sink({ apply: b => got.push(b) })
        batch(() => { const k = rows.insert({ qty: 2 }); rows.get(k).remove() })  // add + remove, one batch
        print(`add + remove annihilated: ${got.length} batches delivered, no delta`)
        batch(() => { t.t.qty.update(1); t.t.qty.update(2); t.t.qty.update(3) })  // three writes, one key
        print(`${got.length} batch, ${got[0].rows.length} delta: ${show(got[0].rows[0])} — first prev, last value`)
        // →
        // →
        ·
        09Coalescingopt-in coalesce('microtask' | 'frame') — sugar, not semantics
        const q = () => [[{ n: 1 }], [{ n: 2 }]], commits = { sync: 0, co: 0 }  // two chunks that settle in one tick
        const feed = (c = q()) => ({ [Symbol.asyncIterator]: () => ({ next: () => Promise.resolve({ done: !c.length, value: c.shift() }) }) })
        const sync = fromAsync(runtime(), feed()), co = fromAsync(runtime(), feed(), { coalesce: 'microtask' })
        handleFor(sync.source).sink({ apply: () => commits.sync++ }); handleFor(co.source).sink({ apply: () => commits.co++ })
        while (sync.status() === 'pending' || co.status() === 'pending') await new Promise(r => setTimeout(r))
        print(`default: ${q().length} chunks → ${commits.sync} commits · coalesce('microtask'): ${commits.co} commit · same ${co.source.rowCount()} rows each`)
        // →
        ·
        10The deep-path lawreplication of nested writes: pathCopy / pathDelete
        const t = $({ t3: { meta: { tag: 'old', book: null } } }), got = []; t.sink({ apply: b => got.push(...b.rows) })
        t.t3.meta.tag.remove()  // nested remove: first-class (pathDelete)
        t.t9.meta.tag.remove()  // absent target: idempotent no-op
        t.t3.meta.book.depth.update(3)  // null intermediate: vivified (pathCopy)
        print(`${show(got[0])} · deleted: ${got[0].deleted} · row.meta = ${JSON.stringify(got[0].row.meta)}`)
        print(`${got.length} deltas for three writes — the absent-target remove emitted nothing · ${show(got[1])}, vivified from null`)
        // →
        // →
        ·
        11The value-domain portability tablewhich JS values cross the wire intact
        const src = $({})
        ingest(src, [{ t: 'add', k: '\x00key', v: { px: NaN, at: undefined } }])
        const row = src.get('\x00key')[value]
        print(`${src.rowCount()} row · key ${JSON.stringify('\x00key')} live: ${row !== undefined} — a NUL byte is just a key`)
        print(`px is NaN: ${Number.isNaN(row.px)} · row owns 'at': ${'at' in row}, at === undefined: ${row.at === undefined} — both first-class`)
        // →
        // →
        ·
        conformance/schedule.test.ts · loading the suite… · runs in data's CI and fero's read SCHEDULE.md →

        Every proof above is a real program run in this tab against the page's engine — the code is literal, print() writes its // → line, show(delta) formats a row delta as op key .path prev → next. conform(node) attaches a legality checker and a replay sink to any view: every commit is re-folded and the fold must equal the view.

        06

        Across the wire

        Every commit can leave the runtime as a WireBatch — JSON-safe, replayable in application order — and enter another one through ingest() or the hot lane(). The two runtimes below are joined by nothing but that wire; write on either side and the other side receives exactly the change.

        here · runtime Arows
        keysymsideqtypx
        wireSink(here, cross, { origin: fromWire })
        the wireJSON-safe
        A → BwireSink → ingest
        // nothing on the wire yet
        crossed · echoed back
        there · runtime Brows
        keysymsideqtypx
        empty — seeded by the wire's opening snapshot
        wireSink(there, cross, { origin: fromWire, initial: false })
        fold(wire) ≡ there · waiting for the wire …

        origin tokens: each side ignores batches that carry its own origin — clause 6.