API index and search · Build metadata
Source snapshot
packages/devtools/src/types.ts
1// The devtools read-model (DEBUG-TOOLS-BROWSER-DESIGN.md §4) and the read-only seams the core2// attaches to. Everything here is derived, in dev, from state the client already holds — the core3// adds no hot-path instrumentation (§1.1 "surface, not instrument").45import type {6 Ast,7 BackendDevObserver,8 BackendServerDelta,9 ChangeEvent,10 QueryId,11 ResultType,12 StoreInspect,13} from "@rindle/client";1415export type {16 Ast,17 BackendDevObserver,18 BackendServerDelta,19 ChangeEvent,20 FlatChange,21 FlatOp,22 NormalizedOp,23 QueryId,24 ResultType,25 StoreInspect,26} from "@rindle/client";2728// --- the attach target (duck-typed) ------------------------------------------------29// The core binds to a running client by structural type only — it never imports `@rindle/optimistic`30// (which would drag the wasm artifact into its typecheck). The optimistic backend's `__inspect()`31// shape below is kept IDENTICAL to `OptimisticInspect` in `@rindle/optimistic/backend.ts`; the two32// are cross-referenced by comment rather than a shared import to keep this package wasm-free.3334/** One folded entry's debounce window — mirror of `@rindle/optimistic`'s `FoldInspect`. */35export interface FoldInspect {36 foldKey: string;37 debounceMs: number;38 maxWaitMs?: number;39 deferAcrossWrites: boolean;40 flushed: boolean;41}4243/** One pending mutation — mirror of `@rindle/optimistic`'s `PendingInspect`. */44export interface PendingInspect {45 key: string;46 mid: number | null;47 name: string;48 args: unknown;49 tables: string[];50 fold?: FoldInspect;51}5253/** A snapshot of the optimistic loop — mirror of `@rindle/optimistic`'s `OptimisticInspect`. */54export interface OptimisticInspect {55 pending: PendingInspect[];56 confirmedLmid: number;57 nextMid: number;58 appliedCv: number;59 bufferedFrames: number;60 pendingTables: string[];61}6263/** The Store surface the core reads (a structural subset of `@rindle/client`'s `Store`). The delta64 * + resultType taps are the SUPPORTED app-facing seams (no private devtools back door). */65export interface DevtoolsStore {66 subscribeChanges(listener: (qid: QueryId, ev: ChangeEvent) => void): () => void;67 subscribeResultType(listener: (qid: QueryId, rt: ResultType) => void): () => void;68 __inspect(sampleRows?: number): StoreInspect;69}7071/** Optional backend-side devtools capabilities. `__inspect` is present on `OptimisticBackend`;72 * `__attachDevtoolsServerDeltas` is present on backends that can surface authoritative server73 * frames separately from the Store's post-apply view stream. */74export interface DevtoolsBackend {75 __inspect?(): OptimisticInspect;76 __attachDevtoolsServerDeltas?(observer: BackendDevObserver): () => void;77}7879/** What {@link attachDevtools} binds to: a `createRindleClient` app, or any `{ store, backend }`. */80export interface DevtoolsTarget {81 store: DevtoolsStore;82 /** Narrowed to {@link DevtoolsBackend} at runtime when it carries `__inspect` (capability probe). */83 backend?: unknown;84}8586// --- the read-model the panel renders ----------------------------------------------8788/** A mutation's place in the fork/rebase lifecycle (DEBUG-TOOLS-BROWSER-DESIGN §4.1). */89export type MutationState = "pending" | "confirmed" | "dropped";9091/** One row of the mutation timeline — the optimistic loop made visible. */92export interface TimelineEntry {93 /** Stable identity across snapshots: the pending key (`m:<mid>` once a mid is dealt, else94 * `f:<foldKey>` while a fold debounces). Retained after the entry settles. */95 id: string;96 /** The wire mutation id, or `null` for a still-folding entry. */97 mid: number | null;98 name: string;99 args: unknown;100 /** Tables the mutator touched (its pending-axis footprint). */101 tables: string[];102 state: MutationState;103 /** True for a debounced/folded write; `fold` carries its window while it is live. */104 folded: boolean;105 fold?: FoldInspect;106 /** Devtools-clock ms at first observation (invoke). */107 invokedAt: number;108 /** Devtools-clock ms when it left the pending stack (confirmed or dropped). */109 settledAt?: number;110 /** Heuristic (§4.1): view churn coincided with this mutation's confirmation — a POSSIBLE111 * snap-back (the optimistic prediction diverged from the authoritative server result). Labeled112 * "possible" because unrelated server data released in the same coherent batch also shows churn;113 * a precise signal needs a reconcile-boundary event (a future engine seam). */114 reconciledWithChurn: boolean;115 /** qids of live queries whose tables this mutation touches (computed against the current views). */116 affectedQueries: number[];117}118119/** One materialized view in the queries inspector (DEBUG-TOOLS-BROWSER-DESIGN §4.2). */120export interface QueryEntry {121 qid: number;122 ast: Ast;123 /** The root table (`ast.table`). */124 table: string;125 /** A one-line human summary of the AST (table, filters, order, limit, relationships). */126 summary: string;127 /** Every base table the query reads (root + correlated subqueries). */128 tables: string[];129 resultType: ResultType;130 rowCount: number;131 sample: readonly unknown[];132 /** Does any pending mutation touch this query's tables? (the §7.2 pending axis). */133 pending: boolean;134}135136/** The kind of a delta-stream row. `child` is an Add/Remove/Edit addressed at a NESTED path137 * (`depth > 0`) — rindle's relationship-level change (DEBUG-TOOLS-BROWSER-DESIGN §4.3). */138export type DeltaKind = "hello" | "snapshot" | "add" | "remove" | "edit";139140/** One entry of the live delta stream — the IVM change primitive made visible (§4.3). */141export interface DeltaEntry {142 seq: number;143 at: number;144 qid: number;145 kind: DeltaKind;146 /** Nesting depth of the change path: 0 = top-level row, >0 = a child (relationship) change. */147 depth: number;148 /** A compact, human-readable description of the change. */149 label: string;150}151152/** The whole devtools snapshot a panel renders. Reference-stable arrays between updates where the153 * underlying data did not change is NOT guaranteed — panels should treat each `getState()` as fresh. */154export interface DevtoolsState {155 /** Newest-last mutation timeline (capped). */156 timeline: TimelineEntry[];157 /** Every live materialized view. */158 queries: QueryEntry[];159 /** Newest-last delta stream ring (capped). */160 deltas: DeltaEntry[];161 /** The raw optimistic-loop snapshot, when the backend exposes one (a "loop" summary line). */162 optimistic?: OptimisticInspect;163 capabilities: { optimistic: boolean };164}165166/** Construction options for {@link DevtoolsCore}. */167export interface DevtoolsCoreOptions {168 /** Max timeline rows retained (oldest settled rows drop first). Default 200. */169 timelineCap?: number;170 /** Max delta-stream rows retained. Default 500. */171 deltaCap?: number;172 /** Per-query row sample size pulled from the store. Default 25. */173 sampleRows?: number;174 /** Safety-net poll interval (ms) that catches state moves with no event — a fold's debounced175 * flush, or a pending flip on an already-`complete` query. Default 0 (off); {@link attachDevtools}176 * turns it on. */177 pollMs?: number;178 /** Coalesce event-driven recomputes onto a microtask. Default true; tests pass `false` and drive179 * recomputes explicitly via {@link DevtoolsCore.refresh}. */180 autoFlush?: boolean;181 /** Injectable clock (ms). Default `Date.now`; tests inject a deterministic counter. */182 now?: () => number;183}184