Rindle

API index and search · Build metadata

Source snapshot

packages/devtools/src/types.ts

Source revision 05d0bf2c2e56 · build details
Source revision: 05d0bf2c2e56.
TypeScript input SHA-256: aabe6cfcc4172b870d5e272142958e9ea8d8784c2aa23133156e5a7ee633318e
Generated 2026-09-04T23:58:25.590Z with TypeScript 6.0.3. Public TypeScript checks and declaration emit passed. Package runtime tests are separate.
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