Rindle

API index and search · Build metadata

Source snapshot

packages/devtools/src/ast.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// Read-only AST helpers for the queries inspector (DEBUG-TOOLS-BROWSER-DESIGN §4.2): the table2// footprint (for the pending-axis intersection) and a one-line human summary. Pure functions over3// the `@rindle/client` `Ast` — no engine, no side effects.45import type { Ast, Condition, ValuePosition } from "@rindle/client";67/** Every base table this query can read: the root, each `related` subquery, and any correlated8 *  subquery in the `where` tree (recursively). Mirrors the optimistic backend's `queryTables`9 *  derivation (DEBUG-TOOLS-BROWSER-DESIGN §4.2 — a `count(comments)` subquery names `comment`, so a10 *  comment mutation flips this query's pending axis). */11export function collectTables(ast: Ast, into: Set<string> = new Set()): Set<string> {12  into.add(ast.table);13  for (const r of ast.related ?? []) collectTables(r.subquery, into);14  if (ast.where) collectTablesFromCondition(ast.where, into);15  return into;16}1718function collectTablesFromCondition(cond: Condition, into: Set<string>): void {19  if (cond.type === "and" || cond.type === "or") {20    for (const c of cond.conditions) collectTablesFromCondition(c, into);21  } else if (cond.type === "correlatedSubquery") {22    collectTables(cond.related.subquery, into);23  }24}2526/** A compact, one-line description of a query AST for the inspector header — best-effort and27 *  truncation-friendly (the full AST is available for a collapsible pretty-print alongside). */28export function summarizeAst(ast: Ast): string {29  const parts: string[] = [ast.alias ? `${ast.table} as ${ast.alias}` : ast.table];30  if (ast.aggregate) parts.push(ast.aggregate === "count" ? "count(*)" : ast.aggregate);31  if (ast.select?.length) parts.push(`select(${ast.select.join(", ")})`);32  if (ast.where) parts.push(`where(${summarizeCondition(ast.where)})`);33  if (ast.orderBy?.length) parts.push(`order(${ast.orderBy.map(([f, d]) => `${f} ${d}`).join(", ")})`);34  if (ast.limit !== undefined) parts.push(`limit ${ast.limit}`);35  if (ast.one) parts.push("one");36  if (ast.related?.length) {37    const rels = ast.related.map((r) => {38      const sub = r.subquery;39      const tag = sub.aggregate ? `${sub.alias ?? sub.table}(count)` : (sub.alias ?? sub.table);40      return tag;41    });42    parts.push(`{ ${rels.join(", ")} }`);43  }44  return parts.join(" ");45}4647function summarizeCondition(cond: Condition): string {48  switch (cond.type) {49    case "simple":50      return `${valueStr(cond.left)} ${cond.op} ${valueStr(cond.right)}`;51    case "and":52      return cond.conditions.map(summarizeCondition).join(" AND ");53    case "or":54      return `(${cond.conditions.map(summarizeCondition).join(" OR ")})`;55    case "correlatedSubquery":56      return `${cond.op} ${cond.related.subquery.table}`;57  }58}5960function valueStr(v: ValuePosition): string {61  if (v.type === "column") return v.name;62  const lit = v.value;63  if (typeof lit === "string") return JSON.stringify(lit);64  if (Array.isArray(lit)) return `[${lit.length}]`;65  return String(lit);66}67