@pythia-software/query-table-core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/index.d.ts +194 -11
- package/dist/index.js +1483 -65
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/query.ts","../src/schema.ts","../src/ops.ts","../src/agg.ts","../src/encode.ts","../src/apply.ts","../src/adapters.ts"],"sourcesContent":["// query.ts — the canonical query state shape.\n//\n// We lean into SQL vocabulary at every layer (per design feedback): a QueryState\n// reads top-to-bottom like the statement it compiles to —\n//\n// SELECT <select> WHERE <where> ORDER BY <orderBy> LIMIT <limit> OFFSET <offset>\n//\n// Both \"what data\" (where/orderBy/limit/offset) and \"how viewed\" (select +\n// per-column width) can be serialized together when a consumer explicitly opts\n// into URL synchronization.\n\n/** All filter operators across both source projects, unioned. Every value can\n * be NULL, so `is_null`/`is_not_null` are valid for every field type. */\nexport type FilterOp =\n // comparison — number / datetime (and =,!= for any scalar)\n | \"=\"\n | \"!=\"\n | \">\"\n | \">=\"\n | \"<\"\n | \"<=\"\n // text matching\n | \"contains\"\n | \"starts_with\"\n | \"ends_with\"\n // array (textarray) containment\n | \"includes\"\n // nullity — valid for EVERY type; for arrays, \"empty\" vs \"non-empty\"\n | \"is_null\"\n | \"is_not_null\";\n\n/** A single AND-combined filter. `value` is always the raw string the UI\n * captured; coercion to number/bool/date happens at apply/compile time based on\n * the field's declared type (never on the value's runtime shape). Unused for\n * the nullary ops (`is_null`/`is_not_null`). */\nexport interface WhereClause {\n field: string; // FieldDef.name\n op: FilterOp;\n value: string;\n}\n\n/** One ORDER BY term. Array order in `QueryState.orderBy` is the sort priority. */\nexport interface OrderByClause {\n field: string; // FieldDef.name (server may sort a different expr via FieldDef.sort.field)\n dir: \"asc\" | \"desc\";\n /** NULL placement. Omitted = \"last\" (the historical default both projects used,\n * preserved so old `?q=` links round-trip identically). */\n nulls?: \"first\" | \"last\";\n}\n\n/** One column in the SELECT list, plus the view state that travels with it.\n * Order in `QueryState.select` is the left-to-right display order. */\nexport interface SelectColumn {\n field: string; // FieldDef.name\n /** Pixel width override. Omitted = FieldDef.select.width, else the type default. */\n width?: number;\n}\n\n/** A single aggregate operation. `count` is the only op that needs no measure\n * column (it counts rows); the rest reduce one column's values. The server-side\n * matrix (backends/go aggOpAllowed + the TS AGG_OPS_BY_TYPE) decides which ops a\n * field's type permits. */\nexport type AggOp = \"count\" | \"count_distinct\" | \"sum\" | \"avg\" | \"min\" | \"max\";\n\n/** One optional \"metric\" pinned above the table: a single aggregate over a single\n * measure column, optionally broken down by one or more group columns.\n *\n * Scope is deliberately the *whole filtered set* — the same WHERE as the table,\n * but NOT its ORDER BY / LIMIT / OFFSET. It is always evaluated on the server\n * (a real GROUP BY), so it reflects every matching row, not just the visible\n * page. Lives inside QueryState so it round-trips through `?q=`, saved queries,\n * and undo/redo for free — a saved query is a saved dashboard. */\nexport interface AggregationClause {\n /** Stable id; keys the metric panel and survives a `?q=` round-trip. */\n id: string;\n op: AggOp;\n /** Measure column (FieldDef.name). Omit only for `count` (⇒ COUNT(*)). */\n field?: string;\n /** Group-by columns, in axis order. `[]` = a single grand-total value.\n * One ⇒ a list/bar breakdown; two ⇒ an x/y pivot; three+ ⇒ a flat table. */\n groupBy: string[];\n /** Panel header override; defaults to a derived label like \"avg total\". */\n label?: string;\n}\n\nexport interface QueryState {\n /** Ordered SELECT list + per-column widths. Empty = the schema's default columns. */\n select: SelectColumn[];\n /** AND-combined filters. */\n where: WhereClause[];\n /** Multi-sort terms in priority order. Empty = the schema's default sort. */\n orderBy: OrderByClause[];\n /** Page size. */\n limit: number;\n /** Page offset (rows). */\n offset: number;\n /** Optional aggregate metrics shown above the table. Omitted/empty = none.\n * Each runs as its own server GROUP BY over the WHERE-filtered set (no paging). */\n aggregations?: AggregationClause[];\n}\n\n/** A row's stable identity, used for selection and per-row refresh. */\nexport type RowId = string | number;\n\n/** The query a fresh table starts from before schema defaults are layered on.\n * Empty `select`/`orderBy`/`where` mean \"defer to the schema\", which also keeps\n * encoded `?q=` tokens short (defaults are omitted). */\nexport const EMPTY_QUERY: QueryState = {\n select: [],\n where: [],\n orderBy: [],\n limit: 100,\n offset: 0,\n};\n\n/** Structural limits applied whenever query state crosses a trust boundary\n * (URL, storage, an imperative setQuery call, or a server projection). Row\n * limits may use the full safe-integer range; consumers and their backends own\n * any smaller operational cap appropriate for their dataset. */\nexport const MAX_QUERY_LIMIT = Number.MAX_SAFE_INTEGER;\nexport const MAX_QUERY_OFFSET = 1_000_000;\nexport const MAX_SELECT_COLUMNS = 200;\nexport const MAX_WHERE_CLAUSES = 100;\nexport const MAX_ORDER_BY_TERMS = 20;\nexport const MAX_AGGREGATIONS = 20;\nexport const MAX_GROUP_BY_FIELDS = 20;\nexport const MAX_QUERY_TOKEN_LENGTH = 2 * 1024 * 1024;\n\nconst MAX_FIELD_NAME_LENGTH = 256;\nconst MAX_FILTER_VALUE_LENGTH = 10_000;\nconst MAX_LABEL_LENGTH = 1_000;\nconst MIN_COLUMN_WIDTH = 24;\nconst MAX_COLUMN_WIDTH = 2_000;\n\nconst FILTER_OPS: ReadonlySet<string> = new Set([\n \"=\",\n \"!=\",\n \">\",\n \">=\",\n \"<\",\n \"<=\",\n \"contains\",\n \"starts_with\",\n \"ends_with\",\n \"includes\",\n \"is_null\",\n \"is_not_null\",\n]);\n\nconst AGG_OPS: ReadonlySet<string> = new Set([\"count\", \"count_distinct\", \"sum\", \"avg\", \"min\", \"max\"]);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value != null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction boundedString(value: unknown, maxLength: number): string | null {\n return typeof value === \"string\" && value.length > 0 && value.length <= maxLength ? value : null;\n}\n\nfunction boundedInteger(value: unknown, fallback: number, min: number, max: number): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return fallback;\n return Math.max(min, Math.min(max, Math.round(value)));\n}\n\n/** Convert unknown input into a bounded, structurally valid QueryState.\n * Invalid members are dropped; invalid scalar paging values use the supplied\n * fallback. This function is intentionally schema-agnostic—field allowlisting\n * still happens in toServerQuery and in the backend compiler. */\nexport function normalizeQueryState(input: unknown, fallback: QueryState = EMPTY_QUERY): QueryState {\n const raw = isRecord(input) ? input : {};\n\n const select: SelectColumn[] = [];\n if (Array.isArray(raw.select)) {\n for (const item of raw.select.slice(0, MAX_SELECT_COLUMNS)) {\n if (!isRecord(item)) continue;\n const field = boundedString(item.field, MAX_FIELD_NAME_LENGTH);\n if (!field) continue;\n const column: SelectColumn = { field };\n if (typeof item.width === \"number\" && Number.isFinite(item.width)) {\n column.width = boundedInteger(item.width, MIN_COLUMN_WIDTH, MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH);\n }\n select.push(column);\n }\n } else {\n select.push(...fallback.select.map((column) => ({ ...column })));\n }\n\n const where: WhereClause[] = [];\n if (Array.isArray(raw.where)) {\n for (const item of raw.where.slice(0, MAX_WHERE_CLAUSES)) {\n if (!isRecord(item)) continue;\n const field = boundedString(item.field, MAX_FIELD_NAME_LENGTH);\n const value = typeof item.value === \"string\" && item.value.length <= MAX_FILTER_VALUE_LENGTH ? item.value : null;\n if (!field || typeof item.op !== \"string\" || !FILTER_OPS.has(item.op) || value == null) continue;\n where.push({ field, op: item.op as FilterOp, value });\n }\n } else {\n where.push(...fallback.where.map((clause) => ({ ...clause })));\n }\n\n const orderBy: OrderByClause[] = [];\n if (Array.isArray(raw.orderBy)) {\n for (const item of raw.orderBy.slice(0, MAX_ORDER_BY_TERMS)) {\n if (!isRecord(item)) continue;\n const field = boundedString(item.field, MAX_FIELD_NAME_LENGTH);\n if (!field || (item.dir !== \"asc\" && item.dir !== \"desc\")) continue;\n const term: OrderByClause = { field, dir: item.dir };\n if (item.nulls === \"first\" || item.nulls === \"last\") term.nulls = item.nulls;\n orderBy.push(term);\n }\n } else {\n orderBy.push(...fallback.orderBy.map((term) => ({ ...term })));\n }\n\n const out: QueryState = {\n select,\n where,\n orderBy,\n limit: boundedInteger(raw.limit, fallback.limit, 1, MAX_QUERY_LIMIT),\n offset: boundedInteger(raw.offset, fallback.offset, 0, MAX_QUERY_OFFSET),\n };\n\n if (Array.isArray(raw.aggregations)) {\n const aggregations: AggregationClause[] = [];\n for (const item of raw.aggregations.slice(0, MAX_AGGREGATIONS)) {\n if (!isRecord(item)) continue;\n const id = boundedString(item.id, MAX_FIELD_NAME_LENGTH);\n if (!id || typeof item.op !== \"string\" || !AGG_OPS.has(item.op) || !Array.isArray(item.groupBy)) continue;\n const groupBy = item.groupBy\n .slice(0, MAX_GROUP_BY_FIELDS)\n .map((field) => boundedString(field, MAX_FIELD_NAME_LENGTH))\n .filter((field): field is string => field != null);\n const aggregation: AggregationClause = { id, op: item.op as AggOp, groupBy };\n const field = boundedString(item.field, MAX_FIELD_NAME_LENGTH);\n const label = boundedString(item.label, MAX_LABEL_LENGTH);\n if (field) aggregation.field = field;\n if (label) aggregation.label = label;\n aggregations.push(aggregation);\n }\n if (aggregations.length > 0) out.aggregations = aggregations;\n } else if (fallback.aggregations?.length) {\n out.aggregations = fallback.aggregations.map((aggregation) => ({ ...aggregation, groupBy: [...aggregation.groupBy] }));\n }\n\n return out;\n}\n\n/** Structural equality on two queries — used to decide whether a `?q=` write or a\n * refetch is actually needed (avoids history spam / redundant fetches). */\nexport function queriesEqual(a: QueryState, b: QueryState): boolean {\n if (a === b) return true;\n if (a.limit !== b.limit || a.offset !== b.offset) return false;\n if (!sameArray(a.where, b.where)) return false;\n if (!sameArray(a.orderBy, b.orderBy)) return false;\n if (!sameArray(a.select, b.select)) return false;\n if (!sameArray(a.aggregations ?? [], b.aggregations ?? [])) return false;\n return true;\n}\n\nfunction sameArray<T>(a: readonly T[], b: readonly T[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n // Members are flat records of primitives — JSON compare is correct and cheap here.\n if (JSON.stringify(a[i]) !== JSON.stringify(b[i])) return false;\n }\n return true;\n}\n","// schema.ts — the field schema: the contract that binds frontend and backend.\n//\n// A `FieldSchema` is the runtime, frontend-facing projection of a JSON schema\n// document (schema/query-table.schema.json). The same document is projected on\n// the backend into a Go `Schema` of SQL bindings. Codegen keeps the two in sync.\n//\n// `FieldDef` is organized to make two things obvious (per design feedback):\n//\n// 1. WHAT KIND of field it is — the `source` discriminated union. A `backend`\n// field is a column the server returns and can filter/sort; a `derived`\n// field is computed on the client (or render-only) and is never pushed to\n// the server. Each kind uses its own property subset, so you can't, say,\n// mark a client-only column as server-filterable.\n//\n// 2. CONFIG vs CODE. Everything except `source.accessor` and `render` is\n// declarative, serializable config grouped by capability (`filter`, `sort`,\n// `select`). The only code-bearing members are the derived `accessor` and\n// the `render` function — kept apart from the config so the two don't mix.\n\nimport type { AggOp, FilterOp, OrderByClause, SelectColumn } from \"./query\";\n\nexport type FieldType =\n | \"text\"\n | \"number\"\n | \"enum\"\n | \"datetime\"\n | \"bool\"\n | \"textarray\"; // array-of-text; supports `includes`, and null-as-empty semantics\n\nexport type Align = \"left\" | \"right\" | \"center\";\n\n/** A cell renderer is resolved by name against a `RenderRegistry` (in\n * @pythia-software/query-table-ui) or supplied inline. The type lives in core so FieldDef can\n * reference it, but core never imports React — `unknown` stands in for the node\n * and @pythia-software/query-table-ui narrows it to `React.ReactNode`. */\nexport type CellRenderer<Row = any, V = unknown> = (ctx: {\n value: V;\n row: Row;\n field: FieldDef<Row, V>;\n}) => unknown;\n\n// ---- source: WHAT KIND of field (discriminated union) ---------------------\n\n/** A column the backend returns and can filter/sort. Its SQL expression lives in\n * the JSON schema doc's `bindings` (and the Go Schema), never on the frontend. */\nexport interface BackendField {\n kind: \"backend\";\n /** Dotted path into the API row when it differs from `name` (e.g. \"enqueued_at.Time\"). */\n path?: string;\n /** Computed SQL with no backing column (e.g. is_starred). Documentation hint\n * surfaced to the picker; the actual expression is server-side. */\n synthetic?: boolean;\n}\n\n/** A value computed on the client, or a purely render-driven column. Never\n * pushed to the server; client-side filtering/sorting (if any) reads `accessor`. */\nexport interface DerivedField<Row = any, V = unknown> {\n kind: \"derived\";\n /** Compute the value from the row for client filter/sort. Omit for render-only\n * columns (the renderer reads the whole row from CellContext instead). */\n accessor?: (row: Row) => V;\n}\n\nexport type FieldSource<Row = any, V = unknown> = BackendField | DerivedField<Row, V>;\n\n// ---- capability config (declarative; serializable; shared with backend) ----\n\n/** How filter-value candidates are offered. Autocomplete is the DEFAULT for\n * every field (design feedback): the value input is a combobox backed by\n * Transport.fetchDistinctValues, which takes a search string so large domains\n * refine per keystroke on the backend. `static` is for closed domains you don't\n * want a round-trip for; `freeform` opts out of suggestions entirely. */\nexport type FilterValues =\n | { source: \"autocomplete\" }\n | { source: \"static\"; options: string[] }\n | { source: \"freeform\" };\n\nexport interface FilterConfig {\n /** Whether this field is filterable. Default: backend ⇒ true, derived ⇒ false. */\n enabled?: boolean;\n /** Evaluate on the backend (predicate pushdown)? Default: backend ⇒ true.\n * false ⇒ the clause is stripped from the server request and applied locally\n * by applyQuery (the \"serverFilter:false\" derived/JSONB-less columns). */\n pushdown?: boolean;\n /** Override the default operator set for the field's type. */\n ops?: FilterOp[];\n /** Value-suggestion strategy. Default { source: \"autocomplete\" }. */\n values?: FilterValues;\n}\n\nexport interface SortConfig {\n /** Whether this field is sortable. Default: backend ⇒ true, derived ⇒ false. */\n enabled?: boolean;\n /** Server field to ORDER BY when it differs from the display field\n * (e.g. a \"★\" column whose server sort key is `is_starred`). */\n field?: string;\n}\n\nexport interface SelectConfig {\n /** Can this appear as a visible column? Default true. false ⇒ filter-only field. */\n enabled?: boolean;\n /** Shown by default when QueryState.select is empty. */\n default?: boolean;\n /** Default column width (px); a per-column override rides in QueryState.select. */\n width?: number;\n align?: Align;\n}\n\nexport interface AggregateConfig {\n /** Can this field be a metric's MEASURE (sum/avg/min/max/count_distinct)?\n * Default: backend ⇒ true, derived ⇒ false. */\n measure?: boolean;\n /** Can this field be a GROUP BY key? Default: enum/text/bool ⇒ true. */\n groupable?: boolean;\n /** Override the default aggregate-op set for the field's type. */\n ops?: AggOp[];\n}\n\n// ---- the field ------------------------------------------------------------\n\nexport interface FieldDef<Row = any, V = unknown> {\n // identity\n name: string; // canonical id; matches the backend binding key + Where/OrderBy `field`\n label: string;\n type: FieldType;\n\n // WHAT KIND of field (value origin + server capability)\n source: FieldSource<Row, V>;\n\n // capabilities (declarative config; omit for sensible per-source defaults)\n filter?: FilterConfig;\n sort?: SortConfig;\n select?: SelectConfig;\n aggregate?: AggregateConfig;\n\n // discovery (picker UX, declarative)\n group?: string; // grouping bucket\n alias?: string; // single alternative search term\n aliases?: string[]; // extra search terms\n\n // presentation (the only code-bearing field): registry key or inline fn\n render?: string | CellRenderer<Row, V>;\n}\n\n/** The frontend's loaded schema for one dataset. */\nexport interface FieldSchema<Row = any> {\n /** Dataset id, also the StorageAdapter namespace for saved queries. */\n name: string;\n /** Field that yields a row's stable id for selection / per-row refresh. */\n idField: string;\n fields: FieldDef<Row>[];\n /** Sort applied when QueryState.orderBy is empty. */\n defaultSort?: OrderByClause[];\n /** Columns shown when QueryState.select is empty. */\n defaultSelect?: SelectColumn[];\n /** Page size for a fresh query. */\n defaultLimit?: number;\n}\n\n// ---- derived capability helpers (the per-source defaults, in one place) ----\n\nexport function isFilterable(f: FieldDef): boolean {\n return f.filter?.enabled ?? f.source.kind === \"backend\";\n}\nexport function isPushdownFilter(f: FieldDef): boolean {\n return isFilterable(f) && (f.filter?.pushdown ?? f.source.kind === \"backend\");\n}\nexport function isSortable(f: FieldDef): boolean {\n return f.sort?.enabled ?? f.source.kind === \"backend\";\n}\nexport function isSelectable(f: FieldDef): boolean {\n return f.select?.enabled ?? true;\n}\nexport function filterValues(f: FieldDef): FilterValues {\n return f.filter?.values ?? { source: \"autocomplete\" };\n}\n\n// ---- schema operations ----------------------------------------------------\n\n/** Index fields by name for O(1) lookup. */\nexport function indexFields<Row>(schema: FieldSchema<Row>): Map<string, FieldDef<Row>> {\n return new Map(schema.fields.map((f) => [f.name, f]));\n}\n\n/** Resolve the ordered, selectable FieldDefs for a query, falling back to schema\n * defaults, then to every `select.default` field. Unknown/duplicate/unselectable\n * names in `select` are dropped. */\nexport function selectedFields<Row>(\n schema: FieldSchema<Row>,\n q: { select: SelectColumn[] },\n): FieldDef<Row>[] {\n const order =\n q.select.length > 0\n ? q.select.map((c) => c.field)\n : (schema.defaultSelect ?? schema.fields.filter((f) => f.select?.default).map((f) => ({ field: f.name }))).map(\n (c) => c.field,\n );\n const seen = new Set<string>();\n const out: FieldDef<Row>[] = [];\n for (const name of order) {\n if (seen.has(name)) continue;\n const f = resolveField(schema, name);\n if (!f || !isSelectable(f)) continue;\n seen.add(name);\n seen.add(f.name);\n out.push(f);\n }\n return out;\n}\n\n/** Resolve a field token to a concrete field by canonical name, case-insensitive\n * name match, or alias match. */\nexport function resolveField<Row>(schema: FieldSchema<Row>, field: string): FieldDef<Row> | undefined {\n const token = field.trim();\n if (!token) return undefined;\n\n const byName = indexFields(schema);\n const byExactName = byName.get(token);\n if (byExactName) return byExactName;\n\n const needle = token.toLowerCase();\n for (const f of schema.fields) {\n if (f.name.toLowerCase() === needle) return f;\n if (aliasMatches(f, needle)) return f;\n }\n return undefined;\n}\n\n/** Return the canonical name for a query token, or undefined when unresolved. */\nexport function resolveFieldName<Row>(schema: FieldSchema<Row>, field: string): string | undefined {\n return resolveField(schema, field)?.name;\n}\n\n/** All configured aliases for a field (deduped, preserving display shape). */\nexport function aliasTermsFor<Row>(field: FieldDef<Row>): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n\n const add = (raw: unknown) => {\n if (typeof raw !== \"string\") return;\n const value = raw.trim();\n if (!value) return;\n const key = value.toLowerCase();\n if (seen.has(key)) return;\n seen.add(key);\n out.push(value);\n };\n\n add(field.alias);\n if (field.aliases) {\n for (const alias of field.aliases) add(alias);\n }\n\n return out;\n}\n\nfunction aliasMatches(field: FieldDef, needle: string): boolean {\n for (const alias of aliasTermsFor(field)) {\n if (alias.toLowerCase() === needle) return true;\n }\n return false;\n}\n\n/** Read a field's value from a row: derived `accessor` if present, else the\n * backend `path` (dotted) / `name`. Returns null when any path segment is missing. */\nexport function readFieldValue<Row, V = unknown>(field: FieldDef<Row, V>, row: Row): V | null {\n if (row == null) return null;\n if (field.source.kind === \"derived\" && field.source.accessor) {\n return field.source.accessor(row);\n }\n const path = (field.source.kind === \"backend\" && field.source.path) || field.name;\n const r = row as Record<string, unknown>;\n if (path in r) return (r[path] ?? null) as V | null;\n let cur: unknown = row;\n for (const part of path.split(\".\")) {\n if (cur == null || typeof cur !== \"object\") return null;\n cur = (cur as Record<string, unknown>)[part];\n }\n return (cur ?? null) as V | null;\n}\n\n/** Parse + validate a raw JSON schema document into a FieldSchema. The doc is the\n * same one the Go backend loads; here we drop backend `bindings` (the frontend\n * never sees SQL) and leave `render` as a string key the consumer maps to a\n * renderer at runtime. Throws with a readable message on a malformed document. */\nexport function loadSchema<Row = any>(doc: unknown): FieldSchema<Row> {\n const d = doc as Record<string, any>;\n if (!d || typeof d !== \"object\") throw new Error(\"schema: document is not an object\");\n if (typeof d.name !== \"string\") throw new Error(\"schema: missing `name`\");\n if (typeof d.idField !== \"string\") throw new Error(\"schema: missing `idField`\");\n if (!Array.isArray(d.fields) || d.fields.length === 0) throw new Error(\"schema: `fields` must be a non-empty array\");\n\n const fields: FieldDef<Row>[] = d.fields.map((raw: any, i: number) => projectField<Row>(raw, i));\n return {\n name: d.name,\n idField: d.idField,\n fields,\n defaultSort: d.defaultSort,\n defaultSelect: d.defaultSelect,\n defaultLimit: d.defaultLimit,\n };\n}\n\nfunction projectField<Row>(raw: any, i: number): FieldDef<Row> {\n if (!raw || typeof raw.name !== \"string\") throw new Error(`schema: field[${i}] missing \\`name\\``);\n if (typeof raw.label !== \"string\") throw new Error(`schema: field ${raw.name} missing \\`label\\``);\n if (typeof raw.type !== \"string\") throw new Error(`schema: field ${raw.name} missing \\`type\\``);\n\n // `source` accepts a string shorthand (\"backend\"|\"derived\") or an object.\n const rs = raw.source ?? (raw.bindings ? \"backend\" : \"derived\");\n let source: FieldSource<Row>;\n if (typeof rs === \"string\") {\n source = rs === \"derived\" ? { kind: \"derived\" } : { kind: \"backend\" };\n } else if (rs.kind === \"derived\") {\n source = { kind: \"derived\" }; // accessor is code → attached at runtime, never from JSON\n } else {\n source = { kind: \"backend\", path: rs.path, synthetic: rs.synthetic };\n }\n\n return {\n name: raw.name,\n label: raw.label,\n type: raw.type,\n source,\n filter: raw.filter,\n sort: raw.sort,\n select: raw.select,\n aggregate: raw.aggregate,\n group: raw.group,\n alias: raw.alias,\n aliases: normalizeAliases(raw.aliases, raw.alias),\n render: raw.render, // string key from JSON; consumer may override with a fn\n };\n}\n\nfunction normalizeAliases(rawAliases: unknown, rawAlias: unknown): string[] {\n const aliases: string[] = [];\n const seen = new Set<string>();\n const add = (raw: unknown) => {\n if (typeof raw !== \"string\") return;\n const value = raw.trim();\n if (!value) return;\n const key = value.toLowerCase();\n if (seen.has(key)) return;\n seen.add(key);\n aliases.push(value);\n };\n\n add(rawAlias);\n if (Array.isArray(rawAliases)) {\n for (const item of rawAliases) add(item);\n }\n return aliases;\n}\n","// ops.ts — which operators a field type allows, and value coercion.\n//\n// Single source of truth for \"what filters can I build on this field\", shared by\n// the QueryBuilder UI, the CellMenu quick-filters, and client-side applyQuery.\n// The backend (backends/go) enforces the same matrix independently — never trust\n// the client.\n//\n// Every value can be NULL, so `is_null`/`is_not_null` are available on EVERY\n// type, including bool (design feedback).\n\nimport type { FilterOp, FieldType, FieldDef } from \"./index\";\n\nconst NULLITY: FilterOp[] = [\"is_null\", \"is_not_null\"];\n\n/** Default operator set per type. A FieldDef.filter.ops overrides this. */\nexport const OPS_BY_TYPE: Record<FieldType, FilterOp[]> = {\n text: [\"=\", \"!=\", \"contains\", \"starts_with\", \"ends_with\", ...NULLITY],\n enum: [\"=\", \"!=\", ...NULLITY],\n number: [\"=\", \"!=\", \">\", \">=\", \"<\", \"<=\", ...NULLITY],\n datetime: [\"=\", \"!=\", \">\", \">=\", \"<\", \"<=\", ...NULLITY],\n bool: [\"=\", \"!=\", ...NULLITY],\n textarray: [\"includes\", ...NULLITY],\n};\n\n/** Operators that take no value (the value input is hidden). */\nexport const NULLARY_OPS: ReadonlySet<FilterOp> = new Set<FilterOp>(NULLITY);\n\n/** Effective operators for a field (its override, else the type default). */\nexport function opsForField(field: Pick<FieldDef, \"type\" | \"filter\">): FilterOp[] {\n return field.filter?.ops ?? OPS_BY_TYPE[field.type];\n}\n\n/** Whether an op is valid for a type per the default matrix (server mirror lives\n * in Go opAllowed; keep them in lockstep). */\nexport function opAllowedForType(type: FieldType, op: FilterOp): boolean {\n return OPS_BY_TYPE[type].includes(op);\n}\n\n/** Coerce a raw string value to the type the field expects, for client-side\n * comparison. Throws on invalid input (e.g. a non-numeric value on a number\n * field) so callers can surface it rather than silently mis-filtering. */\nexport function coerceValue(type: FieldType, raw: string): string | number | boolean {\n switch (type) {\n case \"number\": {\n const n = Number(raw);\n if (raw.trim() === \"\" || Number.isNaN(n)) throw new Error(`not a number: ${JSON.stringify(raw)}`);\n return n;\n }\n case \"bool\": {\n const v = raw.toLowerCase();\n if (v === \"true\" || v === \"1\" || v === \"t\") return true;\n if (v === \"false\" || v === \"0\" || v === \"f\") return false;\n throw new Error(`not a bool: ${JSON.stringify(raw)}`);\n }\n case \"datetime\": {\n const ms = Date.parse(raw);\n if (Number.isNaN(ms)) throw new Error(`not a datetime: ${JSON.stringify(raw)}`);\n return ms;\n }\n default:\n return raw;\n }\n}\n","// agg.ts — which aggregate ops a field type allows, and which fields can be a\n// measure or a group-by key. The single source of truth shared by the\n// QueryBuilder's metric editor, the client-side applyAggregations, and (mirrored\n// independently in backends/go) the SQL compiler. Never trust the client: the Go\n// `aggOpAllowed` enforces the same matrix before any GROUP BY is built.\n\nimport type { AggOp } from \"./query\";\nimport type { FieldType, FieldDef } from \"./schema\";\n\n/** Default aggregate ops per field type. A FieldDef.aggregate.ops overrides this.\n * `count`/`count_distinct` work on anything countable; sum/avg are numeric-only;\n * min/max need an ordered domain (numbers, datetimes, and lexical text/enum). */\nexport const AGG_OPS_BY_TYPE: Record<FieldType, AggOp[]> = {\n number: [\"count\", \"count_distinct\", \"sum\", \"avg\", \"min\", \"max\"],\n datetime: [\"count\", \"count_distinct\", \"min\", \"max\"],\n enum: [\"count\", \"count_distinct\", \"min\", \"max\"],\n text: [\"count\", \"count_distinct\", \"min\", \"max\"],\n bool: [\"count\", \"count_distinct\"],\n textarray: [\"count\"],\n};\n\n/** Ops that reduce a measure column. `count` is the only op that may omit a\n * field (counting rows), so it's the only one absent here. */\nexport const AGG_OPS_NEEDING_FIELD: ReadonlySet<AggOp> = new Set<AggOp>([\n \"count_distinct\",\n \"sum\",\n \"avg\",\n \"min\",\n \"max\",\n]);\n\n/** Whether an op requires a measure column. */\nexport function aggOpNeedsField(op: AggOp): boolean {\n return AGG_OPS_NEEDING_FIELD.has(op);\n}\n\n/** Effective aggregate ops for a field (its override, else the type default). */\nexport function aggOpsForField(field: Pick<FieldDef, \"type\" | \"aggregate\">): AggOp[] {\n return field.aggregate?.ops ?? AGG_OPS_BY_TYPE[field.type];\n}\n\n/** Whether an op is valid for a type per the default matrix (Go aggOpAllowed is\n * the server mirror; keep them in lockstep). */\nexport function aggOpAllowedForType(type: FieldType, op: AggOp): boolean {\n return AGG_OPS_BY_TYPE[type].includes(op);\n}\n\n/** Can this field be the MEASURE of a metric? Only backend fields (they have SQL\n * to aggregate); derived/render-only columns are never pushed to the server. An\n * explicit `aggregate.measure` wins over the type default. */\nexport function isMeasurable(field: FieldDef): boolean {\n if (field.source.kind !== \"backend\") return false;\n if (field.aggregate?.measure != null) return field.aggregate.measure;\n return aggOpsForField(field).length > 0;\n}\n\n/** Can this field be a GROUP BY key? Backend fields only. Default: low-cardinality\n * types (enum/text/bool); numbers/datetimes need bucketing (out of scope) so they\n * default off, but `aggregate.groupable: true` opts any backend field in. */\nexport function isGroupable(field: FieldDef): boolean {\n if (field.source.kind !== \"backend\") return false;\n if (field.aggregate?.groupable != null) return field.aggregate.groupable;\n return field.type === \"enum\" || field.type === \"text\" || field.type === \"bool\";\n}\n","// encode.ts — URL serialization + the server-bound query subset.\n//\n// Two distinct serializations, deliberately kept apart:\n//\n// 1. encodeQuery / decodeQuery — the *whole* QueryState (incl. view state:\n// select columns + widths) ⇄ a base64url `?q=` token. This is what makes\n// reload, back/forward, bookmarks, and \"send a coworker your view\" all work\n// (decisions #4 + #5). Defaults are omitted to keep the token short.\n//\n// 2. toServerQuery — the subset the backend acts on: pushdown\n// WHERE clauses, server-sortable ORDER BY (remapped through sort.field), the\n// page window, and the field names to SELECT (so the backend evaluates the\n// computed/synthetic columns the view needs — requested ∪ always-needed,\n// design decision #2). Per-column widths never leave the client.\n//\n// Back-compat: decodeQuery accepts legacy compact shapes: `o` as a single\n// object (pre-multi-sort) and `c`/`s` as a string[] (pre-width).\n\nimport { EMPTY_QUERY, MAX_QUERY_TOKEN_LENGTH, normalizeQueryState } from \"./query\";\nimport type { QueryState, WhereClause, OrderByClause, SelectColumn, AggregationClause } from \"./query\";\nimport type { FieldSchema } from \"./schema\";\nimport { indexFields, isFilterable, isPushdownFilter, isSortable, resolveFieldName, selectedFields } from \"./schema\";\n\n// ---- base64url (browser + node) -------------------------------------------\n\nfunction toBase64Url(json: string): string {\n let b64: string;\n if (typeof Buffer !== \"undefined\") {\n b64 = Buffer.from(json, \"utf8\").toString(\"base64\");\n } else {\n const bytes = new TextEncoder().encode(json);\n let bin = \"\";\n for (const b of bytes) bin += String.fromCharCode(b);\n b64 = btoa(bin);\n }\n return b64.replace(/=+$/, \"\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\");\n}\n\nfunction fromBase64Url(s: string): string {\n const b64 = s.replace(/-/g, \"+\").replace(/_/g, \"/\") + \"===\".slice((s.length + 3) % 4);\n if (typeof Buffer !== \"undefined\") return Buffer.from(b64, \"base64\").toString(\"utf8\");\n const bin = atob(b64);\n const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));\n return new TextDecoder().decode(bytes);\n}\n\n// ---- compact wire form ----------------------------------------------------\n// Keys are short to keep `?q=` small; any field at its default is omitted.\n// s = select (as [field] or [field, width] tuples)\n// w = where, o = orderBy, l = limit, f = offset\n\ninterface CompactQuery {\n s?: Array<[string] | [string, number]> | string[]; // string[] = legacy\n w?: WhereClause[];\n o?: OrderByClause[] | OrderByClause; // object accepted for legacy decode\n l?: number;\n f?: number;\n g?: AggregationClause[]; // aggregation metrics (omitted when none)\n c?: string[]; // legacy alias for select column names\n}\n\n/** QueryState → base64url token (omitting defaults). All-default query → \"\". */\nexport function encodeQuery(q: QueryState): string {\n q = normalizeQueryState(q);\n const c: CompactQuery = {};\n if (q.select.length)\n c.s = q.select.map((col): [string] | [string, number] => (col.width != null ? [col.field, col.width] : [col.field]));\n if (q.where.length) c.w = q.where;\n if (q.orderBy.length) c.o = q.orderBy;\n c.l = q.limit;\n if (q.offset) c.f = q.offset;\n if (q.aggregations?.length) c.g = q.aggregations;\n if (Object.keys(c).length === 0) return \"\";\n return toBase64Url(JSON.stringify(c));\n}\n\n/** base64url token → QueryState. Tolerant: bad input and legacy shapes both\n * normalize to a valid QueryState (never throws). */\nexport function decodeQuery(token: string): QueryState {\n if (!token) return { ...EMPTY_QUERY };\n if (token.length > MAX_QUERY_TOKEN_LENGTH) return { ...EMPTY_QUERY };\n try {\n const c = JSON.parse(fromBase64Url(token)) as CompactQuery;\n const out = normalizeQueryState({\n select: normalizeSelect(c.s ?? c.c),\n where: Array.isArray(c.w) ? c.w : [],\n orderBy: normalizeOrderBy(c.o),\n limit: c.l,\n offset: c.f,\n aggregations: c.g,\n });\n return out;\n } catch {\n return { ...EMPTY_QUERY };\n }\n}\n\nfunction normalizeSelect(s: CompactQuery[\"s\"] | CompactQuery[\"c\"]): SelectColumn[] {\n if (!Array.isArray(s)) return [];\n return s\n .map((item): SelectColumn | null => {\n if (typeof item === \"string\") return { field: item }; // legacy string[]\n if (Array.isArray(item) && typeof item[0] === \"string\") {\n return typeof item[1] === \"number\" ? { field: item[0], width: item[1] } : { field: item[0] };\n }\n return null;\n })\n .filter((x): x is SelectColumn => x != null);\n}\n\nfunction normalizeOrderBy(o: CompactQuery[\"o\"]): OrderByClause[] {\n if (!o) return [];\n if (Array.isArray(o)) return o;\n return [o]; // legacy single-object form → one-element array\n}\n\n// ---- server-bound subset --------------------------------------------------\n\n/** The backend request derived from a QueryState + its schema. Mirrors the Go\n * `WireQuery`. View-only state (column widths) is intentionally absent. */\nexport interface ServerQuery {\n /** field names to return — visible columns ∪ fields referenced by where/orderBy. */\n select: string[];\n /** only clauses on pushdown-filterable fields. */\n where: WhereClause[];\n /** only terms on server-sortable fields, with `field` already remapped to the\n * field's server sort key (FieldDef.sort.field) when set. */\n orderBy: OrderByClause[];\n limit: number;\n offset: number;\n}\n\n/** Project a QueryState into the server request, dropping client-only filters,\n * client-only sorts, and remapping sort fields through `sort.field`. */\nexport function toServerQuery<Row>(q: QueryState, schema: FieldSchema<Row>): ServerQuery {\n q = normalizeQueryState(q);\n const byName = indexFields(schema);\n const resolveField = (name: string) => resolveFieldName(schema, name) ?? name;\n\n const where = q.where.filter((cl) => {\n const field = resolveField(cl.field);\n const f = byName.get(field);\n return f != null && isPushdownFilter(f);\n });\n\n const orderBy: OrderByClause[] = [];\n for (const term of q.orderBy) {\n const field = resolveField(term.field);\n const f = byName.get(field);\n if (!f || !isSortable(f) || f.source.kind !== \"backend\") continue;\n orderBy.push({ ...term, field: f.sort?.field ?? f.name });\n }\n\n // SELECT = the BACKEND columns the response rows must carry: visible backend\n // columns, the id (for selection/refresh), and any backend column a\n // CLIENT-SIDE filter reads. Derived/render-only columns are computed on the\n // client and have no SQL to select; pushdown filters/sorts run in SQL and need\n // not be returned (design decision #2: requested ∪ always-needed).\n const select = new Set<string>(\n selectedFields(schema, q)\n .filter((f) => f.source.kind === \"backend\")\n .map((f) => f.name),\n );\n select.add(schema.idField);\n for (const cl of q.where) {\n const field = resolveField(cl.field);\n const f = byName.get(field);\n if (f && isFilterable(f) && !isPushdownFilter(f) && f.source.kind === \"backend\") select.add(field);\n }\n\n return { select: [...select], where, orderBy, limit: q.limit, offset: q.offset };\n}\n\n// ---- aggregation server subset --------------------------------------------\n\n/** The backend request for the metric panel. Scope is the whole filtered set:\n * the SAME pushdown WHERE as the rows query, but NO ORDER BY / LIMIT / OFFSET —\n * metrics describe every matching row, not the visible page. Mirrors the Go\n * AggSpec list. */\nexport interface AggregationRequest {\n where: WhereClause[];\n aggregations: AggregationClause[];\n}\n\n/** One group's result. `keys` has one entry per AggregationClause.groupBy field,\n * in axis order (`[]` for a grand total); a null key is the NULL/empty bucket. */\nexport interface AggregationBucket {\n keys: (string | null)[];\n /** The metric: a number for count/sum/avg, or the column's value for min/max\n * (which may be a string for text/datetime). null when undefined (e.g. avg of\n * an all-null column). */\n value: number | string | null;\n /** COUNT(*) of rows in the group — always present, even when `value` isn't a\n * count, so the panel can show group sizes / shares. */\n count: number;\n}\n\nexport interface AggregationResultEntry {\n /** Echoes AggregationClause.id. */\n id: string;\n buckets: AggregationBucket[];\n}\n\nexport interface AggregationResult {\n /** One entry per requested aggregation, in request order. */\n metrics: AggregationResultEntry[];\n}\n\n/** Project a QueryState into the metric request: the pushdown WHERE subset (same\n * rule as toServerQuery) plus the aggregations whose measure + group fields are\n * all server-capable backend columns. Aggregations referencing a derived /\n * unknown field are dropped — the server has no SQL for them. */\nexport function toAggregationQuery<Row>(q: QueryState, schema: FieldSchema<Row>): AggregationRequest {\n q = normalizeQueryState(q);\n const byName = indexFields(schema);\n const resolveField = (name: string) => resolveFieldName(schema, name) ?? name;\n\n const where = q.where.filter((cl) => {\n const f = byName.get(resolveField(cl.field));\n return f != null && isPushdownFilter(f);\n });\n\n const isBackend = (name: string | undefined): boolean => {\n if (name == null) return true; // omitted measure (count(*)) is fine\n const field = resolveField(name);\n const f = byName.get(field);\n return f != null && f.source.kind === \"backend\";\n };\n\n const aggregations = (q.aggregations ?? []).filter(\n (a) => isBackend(a.field) && a.groupBy.every((g) => isBackend(g)),\n );\n\n return { where, aggregations };\n}\n","// apply.ts — client-side filter + multi-sort + paginate.\n//\n// applyQuery is the client mirror of the backend compiler. Three jobs:\n// 1. The executor for purely client-side datasets (no backend at all).\n// 2. The executor for derived / non-pushdown columns the backend can't filter.\n// 3. Idempotent defense-in-depth over server-filtered fields (re-applying a\n// server-handled clause must not change the result).\n//\n// It must agree with backends/go's Compile on operator semantics:\n// - `contains`/`starts_with`/`ends_with` are case-insensitive\n// - textarray `is_null` = empty-or-missing; `is_not_null` = any element;\n// `includes` = case-insensitive membership\n// - NULLs sort per OrderByClause.nulls (default \"last\")\n// - multi-sort is a stable lexicographic fold over orderBy in priority order\n\nimport type { AggOp, AggregationClause, QueryState, WhereClause } from \"./query\";\nimport type { FieldSchema, FieldDef } from \"./schema\";\nimport type { AggregationBucket, AggregationResult } from \"./encode\";\nimport { indexFields, readFieldValue, resolveFieldName } from \"./schema\";\nimport { coerceValue } from \"./ops\";\n\nexport interface ApplyResult<Row> {\n /** The page slice (after filter + sort + offset/limit). */\n rows: Row[];\n /** Total matching rows *before* pagination — powers \"showing N of M\". */\n total: number;\n}\n\n/** Filter + multi-sort + paginate `rows` per `q`, resolving field types/paths\n * from `schema`. Pure; never mutates `rows`. Clauses/sorts on unknown fields are\n * ignored (the backend already enforced its own allowlist). */\nexport function applyQuery<Row>(rows: Row[], q: QueryState, schema: FieldSchema<Row>): ApplyResult<Row> {\n const byName = indexFields(schema);\n const resolveField = (name: string) => resolveFieldName(schema, name) ?? name;\n const whereClauses = q.where.map((clause) => ({ ...clause, field: resolveField(clause.field) }));\n const orderBy = q.orderBy.map((term) => ({ ...term, field: resolveField(term.field) }));\n\n let out = rows.filter((row) => whereClauses.every((cl) => matchesWith(byName, row, cl)));\n const total = out.length;\n\n if (orderBy.length) {\n const terms = orderBy\n .map((t) => ({ field: byName.get(t.field), dir: t.dir, nullsLast: (t.nulls ?? \"last\") === \"last\" }))\n .filter((t): t is { field: FieldDef<Row>; dir: \"asc\" | \"desc\"; nullsLast: boolean } => t.field != null);\n out = [...out].sort((a, b) => {\n for (const t of terms) {\n const av = readFieldValue(t.field, a);\n const bv = readFieldValue(t.field, b);\n const aNull = av == null;\n const bNull = bv == null;\n if (aNull || bNull) {\n if (aNull && bNull) continue;\n return (aNull ? 1 : -1) * (t.nullsLast ? 1 : -1);\n }\n const cmp = compare(av, bv);\n if (cmp !== 0) return t.dir === \"asc\" ? cmp : -cmp;\n }\n return 0;\n });\n }\n\n const start = q.offset > 0 ? q.offset : 0;\n const end = q.limit > 0 ? start + q.limit : out.length;\n return { rows: out.slice(start, end), total };\n}\n\n/** Does one row satisfy one clause? Exposed for the CellMenu preview + tests. */\nexport function matchesClause<Row>(row: Row, clause: WhereClause, schema: FieldSchema<Row>): boolean {\n return matchesWith(indexFields(schema), row, clause);\n}\n\n/** Client-side mirror of the backend GROUP BY (the executor for `clientRows`\n * mode / the demo). Scope matches the server contract: the WHERE-filtered set\n * only — ORDER BY / LIMIT / OFFSET are intentionally ignored, so a metric\n * reflects every matching row, not the visible page. Must agree with\n * backends/go's aggregate compile on op semantics. */\nexport function applyAggregations<Row>(rows: Row[], q: QueryState, schema: FieldSchema<Row>): AggregationResult {\n const byName = indexFields(schema);\n const resolveField = (name: string) => resolveFieldName(schema, name) ?? name;\n const whereClauses = q.where.map((clause) => ({ ...clause, field: resolveField(clause.field) }));\n const filtered = rows.filter((row) => whereClauses.every((cl) => matchesWith(byName, row, cl)));\n const metrics = (q.aggregations ?? []).map((agg) => ({\n id: agg.id,\n buckets: computeBuckets(filtered, agg, byName, resolveField),\n }));\n return { metrics };\n}\n\nfunction computeBuckets<Row>(\n rows: Row[],\n agg: AggregationClause,\n byName: Map<string, FieldDef<Row>>,\n resolveField: (field: string) => string,\n): AggregationBucket[] {\n // One slot per groupBy entry, preserving position. An unresolved field stays\n // in the array as `undefined` so every bucket's `keys` has exactly one element\n // per requested group field — the panel renders keys positionally against\n // `clause.groupBy`, so dropping an entry here would shift every later column.\n const groupFields = agg.groupBy.map((n) => byName.get(resolveField(n)));\n const measure = agg.field ? byName.get(resolveField(agg.field)) : undefined;\n\n const groups = new Map<string, { keys: (string | null)[]; rows: Row[] }>();\n for (const row of rows) {\n const keys = groupFields.map((f) => {\n if (!f) return null; // unresolved group field → NULL bucket, never a missing column\n const v = readFieldValue(f, row);\n return v == null || v === \"\" ? null : String(v);\n });\n const k = JSON.stringify(keys);\n let g = groups.get(k);\n if (!g) {\n g = { keys, rows: [] };\n groups.set(k, g);\n }\n g.rows.push(row);\n }\n\n const buckets = [...groups.values()].map((g) => ({\n keys: g.keys,\n count: g.rows.length,\n value: aggValue(agg.op, measure, g.rows),\n }));\n return sortBuckets(buckets);\n}\n\nfunction aggValue<Row>(op: AggOp, measure: FieldDef<Row> | undefined, rows: Row[]): number | string | null {\n if (op === \"count\") {\n if (!measure) return rows.length; // COUNT(*)\n let n = 0;\n for (const r of rows) if (readFieldValue(measure, r) != null) n++; // COUNT(expr) = non-null\n return n;\n }\n if (!measure) return null;\n const values = rows.map((r) => readFieldValue(measure, r)).filter((v) => v != null);\n\n switch (op) {\n case \"count_distinct\":\n return new Set(values.map((v) => String(v))).size;\n case \"sum\":\n case \"avg\": {\n let sum = 0;\n let n = 0;\n for (const v of values) {\n const num = typeof v === \"number\" ? v : Number(v);\n if (Number.isFinite(num)) {\n sum += num;\n n++;\n }\n }\n if (op === \"avg\") return n ? sum / n : null;\n return n ? sum : null;\n }\n case \"min\":\n case \"max\": {\n let best: unknown;\n for (const v of values) {\n if (best === undefined) {\n best = v;\n continue;\n }\n const cmp = compare(v, best);\n if (op === \"min\" ? cmp < 0 : cmp > 0) best = v;\n }\n return (best as number | string | undefined) ?? null;\n }\n default:\n return null;\n }\n}\n\n/** Stable display order: descending by the metric (or group size when the metric\n * isn't numeric), then by group key. The 2D pivot ignores this; the 1D bar list\n * and the flat N-column table read top-to-bottom from it. */\nfunction sortBuckets(buckets: AggregationBucket[]): AggregationBucket[] {\n return [...buckets].sort((a, b) => {\n const av = typeof a.value === \"number\" ? a.value : a.count;\n const bv = typeof b.value === \"number\" ? b.value : b.count;\n if (av !== bv) return bv - av;\n return JSON.stringify(a.keys).localeCompare(JSON.stringify(b.keys));\n });\n}\n\nfunction matchesWith<Row>(byName: Map<string, FieldDef<Row>>, row: Row, clause: WhereClause): boolean {\n const field = byName.get(clause.field);\n if (!field) return true; // unknown field → no client opinion\n const v = readFieldValue(field, row);\n\n // Nullity, with array-aware semantics.\n if (clause.op === \"is_null\") return Array.isArray(v) ? v.length === 0 : v == null || v === \"\";\n if (clause.op === \"is_not_null\") return Array.isArray(v) ? v.length > 0 : v != null && v !== \"\";\n\n // Empty value on a non-equality op is a \"no filter\" signal (matches the UI +\n // the Go compiler, which skip such clauses).\n if (clause.value === \"\" && clause.op !== \"=\" && clause.op !== \"!=\") return true;\n\n // `includes` is array membership (ARRAY_HAS): a null/missing array contains\n // nothing. Handle before the array/scalar split so null can't fall through.\n if (clause.op === \"includes\") {\n const arr = Array.isArray(v) ? v : [];\n const needle = clause.value.toLowerCase();\n return arr.some((x) => String(x).toLowerCase() === needle);\n }\n\n if (Array.isArray(v)) {\n const needle = clause.value.toLowerCase();\n const hay = v.map((x) => String(x).toLowerCase());\n switch (clause.op) {\n case \"contains\":\n return hay.some((s) => s.includes(needle));\n case \"=\":\n return hay.includes(needle);\n case \"!=\":\n return !hay.includes(needle);\n default:\n return true;\n }\n }\n\n switch (clause.op) {\n case \"=\":\n return String(v ?? \"\") === String(coerceSafe(field, clause.value));\n case \"!=\":\n return String(v ?? \"\") !== String(coerceSafe(field, clause.value));\n case \">\":\n return compare(v, coerceSafe(field, clause.value)) > 0;\n case \">=\":\n return compare(v, coerceSafe(field, clause.value)) >= 0;\n case \"<\":\n return compare(v, coerceSafe(field, clause.value)) < 0;\n case \"<=\":\n return compare(v, coerceSafe(field, clause.value)) <= 0;\n case \"contains\":\n return String(v ?? \"\").toLowerCase().includes(clause.value.toLowerCase());\n case \"starts_with\":\n return String(v ?? \"\").toLowerCase().startsWith(clause.value.toLowerCase());\n case \"ends_with\":\n return String(v ?? \"\").toLowerCase().endsWith(clause.value.toLowerCase());\n default:\n return true;\n }\n}\n\n/** Coerce per the field type, falling back to the raw string if coercion fails\n * (so a malformed value degrades to a string compare rather than throwing). */\nfunction coerceSafe(field: FieldDef, raw: string): string | number | boolean {\n try {\n return coerceValue(field.type, raw);\n } catch {\n return raw;\n }\n}\n\nfunction compare(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (typeof a === \"boolean\" && typeof b === \"boolean\") return a === b ? 0 : a ? 1 : -1;\n return String(a).localeCompare(String(b));\n}\n","// adapters.ts — the pluggable seams. Everything project-specific is injected\n// here so the package core stays generic.\n//\n// - Transport : how rows + filter-value suggestions are fetched.\n// - StorageAdapter: where saved/last queries live (in-memory by default in\n// the React hook; explicitly durable when callers opt in).\n\nimport { normalizeQueryState } from \"./query\";\nimport type { QueryState, RowId } from \"./query\";\nimport type { ServerQuery, AggregationRequest, AggregationResult } from \"./encode\";\n\n// ---- Transport ------------------------------------------------------------\n\nexport interface FetchRowsResult<Row> {\n rows: Row[];\n /** Total matching rows before pagination (for \"N of M\" + paging bounds). */\n total: number;\n}\n\n/** A keystroke-driven distinct-value lookup. Autocomplete is the DEFAULT value\n * source for every field (design feedback), so this is how the filter combobox\n * finds options. `search` is the user's current input; the backend returns the\n * best matches and whether it truncated (so the UI can prompt \"keep typing\"). */\nexport interface DistinctValuesQuery {\n field: string;\n /** Current substring/prefix the user has typed (empty = top values). */\n search: string;\n /** Max suggestions to return (default chosen by the adapter, ~50). */\n limit?: number;\n}\n\nexport interface DistinctValuesResult {\n values: string[];\n /** true when more matches exist than were returned — refine by typing. */\n hasMore: boolean;\n /** True if the selected field has at least one NULL in the source dataset.\n * False if that field is guaranteed non-null. Omitted when the backend does\n * not compute this metadata. */\n hasNull?: boolean;\n}\n\n/** Per-field metadata for the field picker. */\nexport interface FieldStats {\n distinct?: number;\n min?: string | number;\n max?: string | number;\n}\n\nexport interface Transport<Row = any> {\n /** Run a server query. The only required method — a purely client-side table\n * can omit a Transport entirely and rely on applyQuery over local rows. */\n fetchRows(query: ServerQuery, signal?: AbortSignal): Promise<FetchRowsResult<Row>>;\n\n /** Distinct values for filter autocomplete. Strongly recommended: without it,\n * fields fall back to freeform input. The backend should match `search`\n * server-side (e.g. ILIKE prefix) and cap at `limit` so large domains stay\n * fast and refine per keystroke. */\n fetchDistinctValues?(q: DistinctValuesQuery, signal?: AbortSignal): Promise<DistinctValuesResult>;\n\n /** Run the metric panel's GROUP BY queries (one per requested aggregation),\n * scoped to the WHERE-filtered set with no paging. Optional: without it, the\n * metric panel falls back to applyAggregations over local rows (client mode).\n * Implementations compile each AggSpec with backends/go CompileAggregation,\n * sharing the same WHERE as fetchRows. */\n fetchAggregations?(q: AggregationRequest, signal?: AbortSignal): Promise<AggregationResult>;\n\n /** Re-fetch a single row by id, for in-place updates without a full re-query\n * after a mutation. Optional. */\n fetchRow?(id: RowId, signal?: AbortSignal): Promise<Row | null>;\n\n /** Stats for the field picker. Optional. */\n fetchFieldStats?(fields: string[], signal?: AbortSignal): Promise<Record<string, FieldStats>>;\n}\n\n// ---- Saved queries --------------------------------------------------------\n\nexport interface SavedQuery {\n id: string;\n name: string;\n /** Epoch millis; injected by the caller so core stays deterministic/testable. */\n savedAt: number;\n query: QueryState;\n}\n\n/** Persistence for the \"last\" query (auto-restored) and named saved queries. All\n * methods are namespaced by `key` (the schema/dataset name) so multiple tables\n * on one origin don't collide. Async to allow backend implementations; the\n * browser adapters can still resolve synchronously. */\nexport interface StorageAdapter {\n loadLast(key: string): Promise<QueryState | null>;\n saveLast(key: string, query: QueryState): Promise<void>;\n\n listSaved(key: string): Promise<SavedQuery[]>;\n /** Load the saved query selected as the default view for this key. Optional so\n * existing backend adapters keep working until they add first-class support. */\n loadDefaultSaved?(key: string): Promise<SavedQuery | null>;\n /** Set or clear the saved query used as the default view for this key. */\n setDefaultSaved?(key: string, id: string | null): Promise<void>;\n /** Persist a named query snapshot. Rejects when a saved query with the same\n * name already exists in the key namespace. */\n saveNamed(key: string, name: string, query: QueryState, savedAt: number): Promise<SavedQuery>;\n deleteSaved(key: string, id: string): Promise<void>;\n}\n\nconst LAST_PREFIX = \"query-table:last:\";\nconst SAVED_PREFIX = \"query-table:saved:\";\nconst DEFAULT_PREFIX = \"query-table:default:\";\nconst MAX_SAVED_QUERIES = 100;\nconst MAX_SAVED_NAME_LENGTH = 200;\n\nfunction normalizeSavedQuery(value: unknown): SavedQuery | null {\n if (value == null || typeof value !== \"object\" || Array.isArray(value)) return null;\n const raw = value as Record<string, unknown>;\n if (\n typeof raw.id !== \"string\" ||\n !raw.id ||\n raw.id.length > 256 ||\n typeof raw.name !== \"string\" ||\n !raw.name ||\n raw.name.length > MAX_SAVED_NAME_LENGTH ||\n typeof raw.savedAt !== \"number\" ||\n !Number.isFinite(raw.savedAt) ||\n raw.query == null ||\n typeof raw.query !== \"object\"\n ) {\n return null;\n }\n return { id: raw.id, name: raw.name, savedAt: raw.savedAt, query: normalizeQueryState(raw.query) };\n}\n\nfunction normalizeSavedQueries(value: unknown): SavedQuery[] {\n if (!Array.isArray(value)) return [];\n return value\n .slice(0, MAX_SAVED_QUERIES)\n .map(normalizeSavedQuery)\n .filter((item): item is SavedQuery => item != null);\n}\n\n/** In-memory persistence used by default by the React package. It supports the\n * full saved-query UI for the lifetime of a mounted table without writing\n * filter values to durable browser storage. */\nexport function memoryStorageAdapter(): StorageAdapter {\n const last = new Map<string, QueryState>();\n const saved = new Map<string, SavedQuery[]>();\n const defaults = new Map<string, string>();\n\n return {\n async loadLast(key) {\n const query = last.get(key);\n return query ? normalizeQueryState(query) : null;\n },\n async saveLast(key, query) {\n last.set(key, normalizeQueryState(query));\n },\n async listSaved(key) {\n return (saved.get(key) ?? []).map((item) => ({ ...item, query: normalizeQueryState(item.query) }));\n },\n async loadDefaultSaved(key) {\n const id = defaults.get(key);\n return id ? (saved.get(key) ?? []).find((item) => item.id === id) ?? null : null;\n },\n async setDefaultSaved(key, id) {\n if (id == null) {\n defaults.delete(key);\n return;\n }\n if (!(saved.get(key) ?? []).some((item) => item.id === id)) throw new Error(`Saved query \"${id}\" does not exist.`);\n defaults.set(key, id);\n },\n async saveNamed(key, name, query, savedAt) {\n const normalizedName = name.trim();\n if (!normalizedName || normalizedName.length > MAX_SAVED_NAME_LENGTH) {\n throw new Error(`Saved query name must be between 1 and ${MAX_SAVED_NAME_LENGTH} characters.`);\n }\n const items = saved.get(key) ?? [];\n if (items.length >= MAX_SAVED_QUERIES) throw new Error(`At most ${MAX_SAVED_QUERIES} saved queries are allowed.`);\n if (items.some((item) => item.name === normalizedName)) throw new Error(`Saved query \"${normalizedName}\" already exists.`);\n const item: SavedQuery = {\n id: `${savedAt}-${Math.round((savedAt * 9301 + 49297) % 233280)}`,\n name: normalizedName,\n savedAt,\n query: normalizeQueryState(query),\n };\n saved.set(key, [...items, item]);\n return item;\n },\n async deleteSaved(key, id) {\n saved.set(key, (saved.get(key) ?? []).filter((item) => item.id !== id));\n if (defaults.get(key) === id) defaults.delete(key);\n },\n };\n}\n\n/** Opt-in StorageAdapter over window.localStorage. Query state includes raw\n * filter values and is stored as cleartext JSON; do not use it for sensitive\n * datasets. Falls back to no persistence when storage is unavailable. */\nexport function localStorageAdapter(): StorageAdapter {\n const ls: Storage | null = (() => {\n try {\n return typeof localStorage !== \"undefined\" ? localStorage : null;\n } catch {\n return null; // access can throw in some sandboxes\n }\n })();\n\n const readSaved = (key: string): SavedQuery[] => {\n if (!ls) return [];\n try {\n const raw = ls.getItem(SAVED_PREFIX + key);\n return normalizeSavedQueries(raw ? JSON.parse(raw) : []);\n } catch {\n return [];\n }\n };\n const writeSaved = (key: string, items: SavedQuery[]): void => {\n if (ls) ls.setItem(SAVED_PREFIX + key, JSON.stringify(items));\n };\n const readDefaultId = (key: string): string | null => {\n if (!ls) return null;\n return ls.getItem(DEFAULT_PREFIX + key);\n };\n const writeDefaultId = (key: string, id: string | null): void => {\n if (!ls) return;\n if (id) ls.setItem(DEFAULT_PREFIX + key, id);\n else ls.removeItem(DEFAULT_PREFIX + key);\n };\n\n return {\n async loadLast(key) {\n if (!ls) return null;\n try {\n const raw = ls.getItem(LAST_PREFIX + key);\n return raw ? normalizeQueryState(JSON.parse(raw)) : null;\n } catch {\n return null;\n }\n },\n async saveLast(key, query) {\n if (ls) ls.setItem(LAST_PREFIX + key, JSON.stringify(normalizeQueryState(query)));\n },\n async listSaved(key) {\n return readSaved(key).sort((a, b) => b.savedAt - a.savedAt);\n },\n async loadDefaultSaved(key) {\n const id = readDefaultId(key);\n if (!id) return null;\n const found = readSaved(key).find((q) => q.id === id);\n if (!found) writeDefaultId(key, null);\n return found ?? null;\n },\n async setDefaultSaved(key, id) {\n if (id && !readSaved(key).some((q) => q.id === id)) {\n throw new Error(`Saved query \"${id}\" does not exist.`);\n }\n writeDefaultId(key, id);\n },\n async saveNamed(key, name, query, savedAt) {\n const normalizedName = name.trim();\n if (!normalizedName || normalizedName.length > MAX_SAVED_NAME_LENGTH) {\n throw new Error(`Saved query name must be between 1 and ${MAX_SAVED_NAME_LENGTH} characters.`);\n }\n const items = readSaved(key);\n if (items.length >= MAX_SAVED_QUERIES) throw new Error(`At most ${MAX_SAVED_QUERIES} saved queries are allowed.`);\n if (items.some((q) => q.name === normalizedName)) {\n throw new Error(`Saved query \"${normalizedName}\" already exists.`);\n }\n const item: SavedQuery = {\n id: `${savedAt}-${Math.round((savedAt * 9301 + 49297) % 233280)}`,\n name: normalizedName,\n savedAt,\n query: normalizeQueryState(query),\n };\n items.push(item);\n writeSaved(key, items);\n return item;\n },\n async deleteSaved(key, id) {\n writeSaved(\n key,\n readSaved(key).filter((q) => q.id !== id),\n );\n if (readDefaultId(key) === id) writeDefaultId(key, null);\n },\n };\n}\n"],"mappings":"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","names":["field","resolveField","resolveField","item"]}
|
|
1
|
+
{"version":3,"sources":["../src/computed.ts","../src/query.ts","../src/schema.ts","../src/ops.ts","../src/agg.ts","../src/encode.ts","../src/apply.ts","../src/adapters.ts","../src/formula.ts"],"sourcesContent":["import type { FormulaResult, FormulaValue } from \"./formula\";\n\n/** Reserved field token namespace. Query state stores IDs, never formula source. */\nexport const COMPUTED_PREFIX = \"@computed/\";\nexport const computedFieldName = (id: string): string => COMPUTED_PREFIX + id;\nexport const isComputedField = (name: string): boolean =>\n name.trim().toLowerCase().startsWith(COMPUTED_PREFIX);\nexport interface ComputedColumn {\n id: string;\n label: string;\n expression: { language: \"qt-expr\"; version: 1; source: string };\n /** Optimistic concurrency token issued by the store. */\n revision: string;\n}\nexport type ComputedColumnDraft = Omit<ComputedColumn, \"revision\">;\nexport interface ComputedColumnStore {\n /** Return the complete authorized catalogue for this dataset. */\n list(dataset: string, signal?: AbortSignal): Promise<ComputedColumn[]>;\n /** null creates a new ID; updates must match the current revision. */\n save(\n dataset: string,\n column: ComputedColumnDraft,\n expectedRevision: string | null,\n ): Promise<ComputedColumn>;\n /** Optional push invalidation, including changes from other sessions. */\n subscribe?(dataset: string, listener: () => void): () => void;\n}\nexport function validateComputedColumn(input: unknown): ComputedColumn {\n const c = input as ComputedColumn;\n if (\n !c ||\n typeof c.id !== \"string\" ||\n !/^[a-zA-Z0-9_-]{1,128}$/.test(c.id) ||\n typeof c.label !== \"string\" ||\n !c.label.trim() ||\n c.label.length > 200 ||\n typeof c.revision !== \"string\" ||\n !c.revision ||\n c.revision.length > 256 ||\n c.expression?.language !== \"qt-expr\" ||\n c.expression.version !== 1 ||\n typeof c.expression.source !== \"string\" ||\n !c.expression.source.trim() ||\n c.expression.source.length > 10000\n ) {\n throw new Error(\n \"Invalid computed column definition or unsupported formula version.\",\n );\n }\n return {\n id: c.id,\n label: c.label,\n revision: c.revision,\n expression: { ...c.expression },\n };\n}\nexport function memoryComputedColumnStore(\n seed: Record<string, ComputedColumn[]> = {},\n): ComputedColumnStore {\n const data = new Map(\n Object.entries(seed).map(([k, v]) => [\n k,\n new Map(v.map((c) => [c.id, validateComputedColumn(c)])),\n ]),\n );\n // Revisions are opaque: never derive a new token by parsing an existing one.\n // Reserve all seed tokens so even a seed from this namespace cannot recur.\n const seedRevisions = new Set(\n [...data.values()].flatMap((entries) =>\n [...entries.values()].map((c) => c.revision),\n ),\n );\n let revisionSequence = 0n;\n function freshRevision(): string {\n let revision: string;\n do {\n revision = `memory:${++revisionSequence}`;\n } while (seedRevisions.has(revision));\n return revision;\n }\n const listeners = new Map<string, Set<() => void>>();\n return {\n async list(dataset) {\n return [...(data.get(dataset)?.values() ?? [])].map(\n validateComputedColumn,\n );\n },\n async save(dataset, column, expectedRevision) {\n const entries = data.get(dataset) ?? new Map<string, ComputedColumn>();\n const current = entries.get(column.id);\n if ((current?.revision ?? null) !== expectedRevision)\n throw new Error(\n \"This definition changed. Reload it before saving again.\",\n );\n const next = validateComputedColumn({\n ...column,\n revision: freshRevision(),\n });\n entries.set(next.id, next);\n data.set(dataset, entries);\n listeners.get(dataset)?.forEach((fn) => fn());\n return validateComputedColumn(next);\n },\n subscribe(dataset, listener) {\n const set = listeners.get(dataset) ?? new Set();\n set.add(listener);\n listeners.set(dataset, set);\n return () => {\n set.delete(listener);\n };\n },\n };\n}\n\n/** REST adapter. The implementing server owns authorization and DB persistence.\n * GET base?dataset=… → ComputedColumn[]\n * PUT base?dataset=… → {column, expectedRevision} → ComputedColumn\n * Return HTTP 409 for a stale revision. No formula evaluation happens there. */\nexport function httpComputedColumnStore(\n base: string,\n fetcher: typeof fetch = fetch,\n): ComputedColumnStore {\n const listeners = new Map<string, Set<() => void>>();\n const url = (dataset: string) =>\n `${base}${base.includes(\"?\") ? \"&\" : \"?\"}dataset=${encodeURIComponent(dataset)}`;\n async function read(response: Response): Promise<unknown> {\n if (response.status === 409)\n throw new Error(\n \"This definition changed. Reload it before saving again.\",\n );\n if (!response.ok)\n throw new Error(\n `Computed column store request failed (${response.status}).`,\n );\n return response.json();\n }\n return {\n async list(dataset, signal) {\n const raw = await read(\n await fetcher(url(dataset), { ...(signal ? { signal } : {}) }),\n );\n if (!Array.isArray(raw))\n throw new Error(\"Invalid computed column catalogue.\");\n return raw.map(validateComputedColumn);\n },\n async save(dataset, column, expectedRevision) {\n const result = validateComputedColumn(\n await read(\n await fetcher(url(dataset), {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ column, expectedRevision }),\n }),\n ),\n );\n listeners.get(dataset)?.forEach((fn) => fn());\n return result;\n },\n subscribe(dataset, listener) {\n const set = listeners.get(dataset) ?? new Set();\n set.add(listener);\n listeners.set(dataset, set);\n return () => {\n set.delete(listener);\n };\n },\n };\n}\nexport interface ComputedCellError {\n computedError: string;\n}\nexport function isComputedCellError(\n value: unknown,\n): value is ComputedCellError {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"computedError\" in value &&\n typeof (value as ComputedCellError).computedError === \"string\"\n );\n}\nexport interface PreviewGroup {\n inputs: FormulaValue[];\n result: FormulaResult;\n count: number;\n}\nexport interface ColumnPreview {\n dependencies: string[];\n groups: PreviewGroup[];\n processed: number;\n total: number;\n nulls: number;\n errors: number;\n}\nexport function groupPreview(\n dependencies: string[],\n inputs: Record<string, FormulaValue>[],\n results: FormulaResult[],\n total: number,\n): ColumnPreview {\n const groups = new Map<string, PreviewGroup>();\n let nulls = 0,\n errors = 0;\n inputs.forEach((row, i) => {\n const values = dependencies.map((name) => row[name] ?? null),\n result = results[i] ?? { value: null, error: \"No result.\" };\n const key = JSON.stringify([values, result.value, result.error ?? null]);\n const group = groups.get(key);\n if (group) group.count++;\n else groups.set(key, { inputs: values, result, count: 1 });\n if (result.error) errors++;\n else if (result.value === null) nulls++;\n });\n return {\n dependencies,\n groups: [...groups.values()].sort((a, b) => b.count - a.count),\n processed: inputs.length,\n total,\n nulls,\n errors,\n };\n}\n","import { isComputedField } from \"./computed\";\n// query.ts — the canonical query state shape.\n//\n// We lean into SQL vocabulary at every layer (per design feedback): a QueryState\n// reads top-to-bottom like the statement it compiles to —\n//\n// SELECT <select> WHERE <where> ORDER BY <orderBy> LIMIT <limit> OFFSET <offset>\n//\n// Both \"what data\" (where/orderBy/limit/offset) and \"how viewed\" (select +\n// per-column width) can be serialized together when a consumer explicitly opts\n// into URL synchronization.\n\n/** All filter operators across both source projects, unioned. Every value can\n * be NULL, so `is_null`/`is_not_null` are valid for every field type. */\nexport type FilterOp =\n // comparison — number / datetime (and =,!= for any scalar)\n | \"=\"\n | \"!=\"\n | \">\"\n | \">=\"\n | \"<\"\n | \"<=\"\n // text matching\n | \"contains\"\n | \"starts_with\"\n | \"ends_with\"\n | \"matches_regex\"\n | \"not_matches_regex\"\n // array (textarray) containment\n | \"includes\"\n // nullity — valid for EVERY type; for arrays, \"empty\" vs \"non-empty\"\n | \"is_null\"\n | \"is_not_null\";\n\n/** A single filter predicate (a \"literal\" in CNF terms). `value` is always the\n * raw string the UI captured; coercion to number/bool/date happens at\n * apply/compile time based on the field's declared type (never on the value's\n * runtime shape). Unused for the nullary ops (`is_null`/`is_not_null`).\n *\n * `negated` wraps the predicate in a logical NOT. It is only ever set for ops\n * that have no complementary operator (`contains`/`starts_with`/`ends_with`/\n * `includes`); ops with a complement negate by flipping the op itself (`>=`→`<`,\n * `=`→`!=`, `is_null`→`is_not_null`, `matches_regex`→`not_matches_regex`) — see\n * `negateClause` in ops.ts. NOT is null-exclusive: a NULL/empty value satisfies\n * neither the predicate nor its negation, matching `not_matches_regex`. */\nexport interface WhereClause {\n field: string; // FieldDef.name\n op: FilterOp;\n value: string;\n negated?: boolean;\n}\n\n/** A disjunction of predicates — the inner OR of conjunctive normal form. Every\n * member is a plain literal; nesting is intentionally not allowed yet (CNF\n * only), but the object shape leaves room to grow. */\nexport interface OrGroup {\n any: WhereClause[];\n}\n\n/** One conjunct of the WHERE clause: either a single predicate or an OR group.\n * `QueryState.where` is the AND of these terms, so the whole WHERE is\n * conjunctive normal form — `(a) AND (b OR c) AND (d)`. A bare literal is\n * equivalent to a one-element OR group; normalization flattens singleton groups\n * back to a literal so equality/encoding stay canonical. */\nexport type WhereTerm = WhereClause | OrGroup;\n\n/** Narrow a term to an OR group. A literal always carries `field`; a group never\n * does and always carries an `any` array. */\nexport function isOrGroup(term: WhereTerm): term is OrGroup {\n return Array.isArray((term as OrGroup).any);\n}\n\n/** The predicate literals a term contributes, in order: the term itself for a\n * literal, or its members for a group. Handy for anything that must visit every\n * predicate regardless of grouping (field allowlisting, SELECT projection). */\nexport function predicatesOf(term: WhereTerm): WhereClause[] {\n return isOrGroup(term) ? term.any : [term];\n}\n\n/** One ORDER BY term. Array order in `QueryState.orderBy` is the sort priority. */\nexport interface OrderByClause {\n field: string; // FieldDef.name (server may sort a different expr via FieldDef.sort.field)\n dir: \"asc\" | \"desc\";\n /** NULL placement. Omitted = \"last\" (the historical default both projects used,\n * preserved so old `?q=` links round-trip identically). */\n nulls?: \"first\" | \"last\";\n /** Sort by the first capture group of a regex match (or by the whole match\n * when the pattern has no capture group). A non-match produces NULL. Regex\n * syntax is interpreted by the executor (JavaScript locally, PostgreSQL on\n * the bundled Go backend), so portable patterns should use their common\n * syntax subset. */\n extract?: { regex: string };\n}\n\n/** One column in the SELECT list, plus the view state that travels with it.\n * Order in `QueryState.select` is the left-to-right display order. */\nexport interface SelectColumn {\n field: string; // FieldDef.name, or @computed/<id> (source lives in ComputedColumnStore)\n /** Pixel width override. Omitted = FieldDef.select.width, else the type default. */\n width?: number;\n}\n\n/** A single aggregate operation. `count` is the only op that needs no measure\n * column (it counts rows); the rest reduce one column's values. The server-side\n * matrix (backends/go aggOpAllowed + the TS AGG_OPS_BY_TYPE) decides which ops a\n * field's type permits. */\nexport type AggOp = \"count\" | \"count_distinct\" | \"sum\" | \"avg\" | \"min\" | \"max\";\n\n/** One optional \"metric\" pinned above the table: a single aggregate over a single\n * measure column, optionally broken down by one or more group columns.\n *\n * Scope is deliberately the *whole filtered set* — the same WHERE as the table,\n * but NOT its ORDER BY / LIMIT / OFFSET. It is always evaluated on the server\n * (a real GROUP BY), so it reflects every matching row, not just the visible\n * page. Lives inside QueryState so it round-trips through `?q=`, saved queries,\n * and undo/redo for free — a saved query is a saved dashboard. */\nexport interface AggregationClause {\n /** Stable id; keys the metric panel and survives a `?q=` round-trip. */\n id: string;\n op: AggOp;\n /** Measure column (FieldDef.name). Omit only for `count` (⇒ COUNT(*)). */\n field?: string;\n /** Group-by columns, in axis order. `[]` = a single grand-total value.\n * One ⇒ a list/bar breakdown; two ⇒ an x/y pivot; three+ ⇒ a flat table. */\n groupBy: string[];\n /** Panel header override; defaults to a derived label like \"avg total\". */\n label?: string;\n}\n\nexport interface QueryState {\n /** Ordered SELECT list + per-column widths. Empty = the schema's default columns. */\n select: SelectColumn[];\n /** WHERE in conjunctive normal form: the AND of these terms, each a single\n * predicate or an OR group. A flat list of literals (no groups) is the common\n * case and the legacy shape, decoded unchanged. */\n where: WhereTerm[];\n /** Multi-sort terms in priority order. Empty = the schema's default sort. */\n orderBy: OrderByClause[];\n /** Page size. */\n limit: number;\n /** Page offset (rows). */\n offset: number;\n /** Optional aggregate metrics shown above the table. Omitted/empty = none.\n * Each runs as its own server GROUP BY over the WHERE-filtered set (no paging). */\n aggregations?: AggregationClause[];\n}\n\n/** A row's stable identity, used for selection and per-row refresh. */\nexport type RowId = string | number;\n\n/** The query a fresh table starts from before schema defaults are layered on.\n * Empty `select`/`orderBy`/`where` mean \"defer to the schema\", which also keeps\n * encoded `?q=` tokens short (defaults are omitted). */\nexport const EMPTY_QUERY: QueryState = {\n select: [],\n where: [],\n orderBy: [],\n limit: 100,\n offset: 0,\n};\n\n/** Structural limits applied whenever query state crosses a trust boundary\n * (URL, storage, an imperative setQuery call, or a server projection). Row\n * limits may use the full safe-integer range; consumers and their backends own\n * any smaller operational cap appropriate for their dataset. */\nexport const MAX_QUERY_LIMIT = Number.MAX_SAFE_INTEGER;\nexport const MAX_QUERY_OFFSET = 1_000_000;\nexport const MAX_SELECT_COLUMNS = 200;\nexport const MAX_WHERE_CLAUSES = 100;\nexport const MAX_ORDER_BY_TERMS = 20;\nexport const MAX_AGGREGATIONS = 20;\nexport const MAX_GROUP_BY_FIELDS = 20;\nexport const MAX_QUERY_TOKEN_LENGTH = 2 * 1024 * 1024;\n\nconst MAX_FIELD_NAME_LENGTH = 256;\nconst MAX_FILTER_VALUE_LENGTH = 10_000;\nconst MAX_LABEL_LENGTH = 1_000;\nconst MIN_COLUMN_WIDTH = 24;\nconst MAX_COLUMN_WIDTH = 2_000;\n\nconst FILTER_OPS: ReadonlySet<string> = new Set([\n \"=\",\n \"!=\",\n \">\",\n \">=\",\n \"<\",\n \"<=\",\n \"contains\",\n \"starts_with\",\n \"ends_with\",\n \"matches_regex\",\n \"not_matches_regex\",\n \"includes\",\n \"is_null\",\n \"is_not_null\",\n]);\n\nconst AGG_OPS: ReadonlySet<string> = new Set([\"count\", \"count_distinct\", \"sum\", \"avg\", \"min\", \"max\"]);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value != null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction boundedString(value: unknown, maxLength: number): string | null {\n return typeof value === \"string\" && value.length > 0 && value.length <= maxLength ? value : null;\n}\n\nfunction boundedInteger(value: unknown, fallback: number, min: number, max: number): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return fallback;\n return Math.max(min, Math.min(max, Math.round(value)));\n}\n\n/** Validate one raw predicate into a bounded WhereClause, or null if malformed.\n * Shared by top-level literals and the members of an OR group. */\nfunction normalizeWhereClause(item: unknown): WhereClause | null {\n if (!isRecord(item)) return null;\n const field = boundedString(item.field, MAX_FIELD_NAME_LENGTH);\n const value = typeof item.value === \"string\" && item.value.length <= MAX_FILTER_VALUE_LENGTH ? item.value : null;\n if (!field || isComputedField(field) || typeof item.op !== \"string\" || !FILTER_OPS.has(item.op) || value == null) {\n return null;\n }\n const clause: WhereClause = { field, op: item.op as FilterOp, value };\n if (item.negated === true) clause.negated = true;\n return clause;\n}\n\n/** Deep-clone one WHERE term (literal or group) so a normalized/fallback state\n * never aliases the caller's arrays. */\nfunction cloneWhereTerm(term: WhereTerm): WhereTerm {\n return isOrGroup(term) ? { any: term.any.map((c) => ({ ...c })) } : { ...term };\n}\n\n/** Convert unknown input into a bounded, structurally valid QueryState.\n * Invalid members are dropped; invalid scalar paging values use the supplied\n * fallback. This function is intentionally schema-agnostic—field allowlisting\n * still happens in toServerQuery and in the backend compiler. */\nexport function normalizeQueryState(input: unknown, fallback: QueryState = EMPTY_QUERY): QueryState {\n const raw = isRecord(input) ? input : {};\n\n const select: SelectColumn[] = [];\n if (Array.isArray(raw.select)) {\n for (const item of raw.select.slice(0, MAX_SELECT_COLUMNS)) {\n if (!isRecord(item)) continue;\n const field = boundedString(item.field, MAX_FIELD_NAME_LENGTH);\n if (!field) continue;\n const column: SelectColumn = { field };\n if (typeof item.width === \"number\" && Number.isFinite(item.width)) {\n column.width = boundedInteger(item.width, MIN_COLUMN_WIDTH, MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH);\n }\n select.push(column);\n }\n } else {\n select.push(...fallback.select.map((column) => ({ ...column })));\n }\n\n // WHERE is CNF: an array of terms, each a literal or an `{ any: [...] }` OR\n // group. MAX_WHERE_CLAUSES bounds the TOTAL predicate count across all terms\n // (legacy flat lists are all literals, so the cap is unchanged for them).\n // Singleton/empty groups are canonicalized (flattened / dropped).\n const where: WhereTerm[] = [];\n if (Array.isArray(raw.where)) {\n let literalBudget = MAX_WHERE_CLAUSES;\n for (const item of raw.where) {\n if (literalBudget <= 0) break;\n if (isRecord(item) && Array.isArray((item as { any?: unknown }).any)) {\n const members: WhereClause[] = [];\n for (const inner of (item as { any: unknown[] }).any) {\n if (literalBudget <= 0) break;\n const clause = normalizeWhereClause(inner);\n if (clause) {\n members.push(clause);\n literalBudget--;\n }\n }\n if (members.length === 1) where.push(members[0]!);\n else if (members.length > 1) where.push({ any: members });\n // 0 members → drop the term entirely\n } else {\n const clause = normalizeWhereClause(item);\n if (clause) {\n where.push(clause);\n literalBudget--;\n }\n }\n }\n } else {\n where.push(...fallback.where.map(cloneWhereTerm));\n }\n\n const orderBy: OrderByClause[] = [];\n if (Array.isArray(raw.orderBy)) {\n for (const item of raw.orderBy.slice(0, MAX_ORDER_BY_TERMS)) {\n if (!isRecord(item)) continue;\n const field = boundedString(item.field, MAX_FIELD_NAME_LENGTH);\n if (!field || isComputedField(field) || (item.dir !== \"asc\" && item.dir !== \"desc\")) continue;\n const term: OrderByClause = { field, dir: item.dir };\n if (item.nulls === \"first\" || item.nulls === \"last\") term.nulls = item.nulls;\n if (\n isRecord(item.extract) &&\n typeof item.extract.regex === \"string\" &&\n item.extract.regex.length <= MAX_FILTER_VALUE_LENGTH\n ) {\n term.extract = { regex: item.extract.regex };\n }\n orderBy.push(term);\n }\n } else {\n orderBy.push(\n ...fallback.orderBy.map((term) => (term.extract ? { ...term, extract: { ...term.extract } } : { ...term })),\n );\n }\n\n const out: QueryState = {\n select,\n where,\n orderBy,\n limit: boundedInteger(raw.limit, fallback.limit, 1, MAX_QUERY_LIMIT),\n offset: boundedInteger(raw.offset, fallback.offset, 0, MAX_QUERY_OFFSET),\n };\n\n if (Array.isArray(raw.aggregations)) {\n const aggregations: AggregationClause[] = [];\n for (const item of raw.aggregations.slice(0, MAX_AGGREGATIONS)) {\n if (!isRecord(item)) continue;\n const id = boundedString(item.id, MAX_FIELD_NAME_LENGTH);\n if (!id || typeof item.op !== \"string\" || !AGG_OPS.has(item.op) || !Array.isArray(item.groupBy)) continue;\n const groupBy = item.groupBy\n .slice(0, MAX_GROUP_BY_FIELDS)\n .map((field) => boundedString(field, MAX_FIELD_NAME_LENGTH))\n .filter((field): field is string => field != null);\n const aggregation: AggregationClause = { id, op: item.op as AggOp, groupBy };\n const field = boundedString(item.field, MAX_FIELD_NAME_LENGTH);\n const label = boundedString(item.label, MAX_LABEL_LENGTH);\n if ((field && isComputedField(field)) || groupBy.some(isComputedField)) continue;\n if (field) aggregation.field = field;\n if (label) aggregation.label = label;\n aggregations.push(aggregation);\n }\n if (aggregations.length > 0) out.aggregations = aggregations;\n } else if (fallback.aggregations?.length) {\n out.aggregations = fallback.aggregations.map((aggregation) => ({ ...aggregation, groupBy: [...aggregation.groupBy] }));\n }\n\n return out;\n}\n\n/** Structural equality on two queries — used to decide whether a `?q=` write or a\n * refetch is actually needed (avoids history spam / redundant fetches). */\nexport function queriesEqual(a: QueryState, b: QueryState): boolean {\n if (a === b) return true;\n if (a.limit !== b.limit || a.offset !== b.offset) return false;\n if (!sameArray(a.where, b.where)) return false;\n if (!sameArray(a.orderBy, b.orderBy)) return false;\n if (!sameArray(a.select, b.select)) return false;\n if (!sameArray(a.aggregations ?? [], b.aggregations ?? [])) return false;\n return true;\n}\n\nfunction sameArray<T>(a: readonly T[], b: readonly T[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n // Members are records of primitives (WHERE terms may nest one `any` array of\n // them) — a structural JSON compare is correct and cheap here.\n if (JSON.stringify(a[i]) !== JSON.stringify(b[i])) return false;\n }\n return true;\n}\n","// schema.ts — the field schema: the contract that binds frontend and backend.\n//\n// A `FieldSchema` is the runtime, frontend-facing projection of a JSON schema\n// document (schema/query-table.schema.json). The same document is projected on\n// the backend into a Go `Schema` of SQL bindings. Codegen keeps the two in sync.\n//\n// `FieldDef` is organized to make two things obvious (per design feedback):\n//\n// 1. WHAT KIND of field it is — the `source` discriminated union. A `backend`\n// field is a column the server returns and can filter/sort; a `derived`\n// field is computed on the client (or render-only) and is never pushed to\n// the server. Each kind uses its own property subset, so you can't, say,\n// mark a client-only column as server-filterable.\n//\n// 2. CONFIG vs CODE. Everything except `source.accessor` and `render` is\n// declarative, serializable config grouped by capability (`filter`, `sort`,\n// `select`). The only code-bearing members are the derived `accessor` and\n// the `render` function — kept apart from the config so the two don't mix.\n\nimport type { AggOp, FilterOp, OrderByClause, SelectColumn } from \"./query\";\n\nexport type FieldType =\n | \"text\"\n | \"number\"\n | \"enum\"\n | \"datetime\"\n | \"bool\"\n | \"textarray\"; // array-of-text; supports `includes`, and null-as-empty semantics\n\nexport type Align = \"left\" | \"right\" | \"center\";\n\n/** A cell renderer is resolved by name against a `RenderRegistry` (in\n * @pythia-software/query-table-ui) or supplied inline. The type lives in core so FieldDef can\n * reference it, but core never imports React — `unknown` stands in for the node\n * and @pythia-software/query-table-ui narrows it to `React.ReactNode`. */\nexport type CellRenderer<Row = any, V = unknown> = (ctx: {\n value: V;\n row: Row;\n field: FieldDef<Row, V>;\n}) => unknown;\n\n// ---- source: WHAT KIND of field (discriminated union) ---------------------\n\n/** A column the backend returns and can filter/sort. Its SQL expression lives in\n * the JSON schema doc's `bindings` (and the Go Schema), never on the frontend. */\nexport interface BackendField {\n kind: \"backend\";\n /** Dotted path into the API row when it differs from `name` (e.g. \"enqueued_at.Time\"). */\n path?: string;\n /** Computed SQL with no backing column (e.g. is_starred). Documentation hint\n * surfaced to the picker; the actual expression is server-side. */\n synthetic?: boolean;\n}\n\n/** A value computed on the client, or a purely render-driven column. Never\n * pushed to the server; client-side filtering/sorting (if any) reads `accessor`. */\nexport interface DerivedField<Row = any, V = unknown> {\n kind: \"derived\";\n /** Backend inputs required for accessors and computed formulas. */\n dependencies?: string[];\n /** Reusable computed column ID; always select-only. */\n computedId?: string;\n /** Compute the value from the row for client filter/sort. Omit for render-only\n * columns (the renderer reads the whole row from CellContext instead). */\n accessor?: (row: Row) => V;\n}\n\nexport type FieldSource<Row = any, V = unknown> = BackendField | DerivedField<Row, V>;\n\n// ---- capability config (declarative; serializable; shared with backend) ----\n\n/** How filter-value candidates are offered. Autocomplete is the DEFAULT for\n * every field (design feedback): the value input is a combobox backed by\n * Transport.fetchDistinctValues, which takes a search string so large domains\n * refine per keystroke on the backend. `static` is for closed domains you don't\n * want a round-trip for; `freeform` opts out of suggestions entirely. */\nexport type FilterValues =\n | { source: \"autocomplete\" }\n | { source: \"static\"; options: string[] }\n | { source: \"freeform\" };\n\nexport interface FilterConfig {\n /** Whether this field is filterable. Default: backend ⇒ true, derived ⇒ false. */\n enabled?: boolean;\n /** Evaluate on the backend (predicate pushdown)? Default: backend ⇒ true.\n * false ⇒ the clause is stripped from the server request and applied locally\n * by applyQuery (the \"serverFilter:false\" derived/JSONB-less columns). */\n pushdown?: boolean;\n /** Override the default operator set for the field's type. */\n ops?: FilterOp[];\n /** Value-suggestion strategy. Default { source: \"autocomplete\" }. */\n values?: FilterValues;\n}\n\nexport interface SortConfig {\n /** Whether this field is sortable. Default: backend ⇒ true, derived ⇒ false. */\n enabled?: boolean;\n /** Server field to ORDER BY when it differs from the display field\n * (e.g. a \"★\" column whose server sort key is `is_starred`). */\n field?: string;\n}\n\nexport interface SelectConfig {\n /** Can this appear as a visible column? Default true. false ⇒ filter-only field. */\n enabled?: boolean;\n /** Shown by default when QueryState.select is empty. */\n default?: boolean;\n /** Default column width (px); a per-column override rides in QueryState.select. */\n width?: number;\n align?: Align;\n}\n\nexport interface AggregateConfig {\n /** Can this field be a metric's MEASURE (sum/avg/min/max/count_distinct)?\n * Default: backend ⇒ true, derived ⇒ false. */\n measure?: boolean;\n /** Can this field be a GROUP BY key? Default: enum/text/bool ⇒ true. */\n groupable?: boolean;\n /** Override the default aggregate-op set for the field's type. */\n ops?: AggOp[];\n}\n\n// ---- the field ------------------------------------------------------------\n\nexport interface FieldDef<Row = any, V = unknown> {\n // identity\n name: string; // canonical id; matches the backend binding key + Where/OrderBy `field`\n label: string;\n type: FieldType;\n\n // WHAT KIND of field (value origin + server capability)\n source: FieldSource<Row, V>;\n\n // capabilities (declarative config; omit for sensible per-source defaults)\n filter?: FilterConfig;\n sort?: SortConfig;\n select?: SelectConfig;\n aggregate?: AggregateConfig;\n\n // discovery (picker UX, declarative)\n group?: string; // grouping bucket\n alias?: string; // single alternative search term\n aliases?: string[]; // extra search terms\n\n // presentation (the only code-bearing field): registry key or inline fn\n render?: string | CellRenderer<Row, V>;\n}\n\n/** The frontend's loaded schema for one dataset. */\nexport interface FieldSchema<Row = any> {\n /** Dataset id, also the StorageAdapter namespace for saved queries. */\n name: string;\n /** Field that yields a row's stable id for selection / per-row refresh. */\n idField: string;\n fields: FieldDef<Row>[];\n /** Sort applied when QueryState.orderBy is empty. */\n defaultSort?: OrderByClause[];\n /** Columns shown when QueryState.select is empty. */\n defaultSelect?: SelectColumn[];\n /** Page size for a fresh query. */\n defaultLimit?: number;\n}\n\n// ---- derived capability helpers (the per-source defaults, in one place) ----\n\nexport function isFilterable(f: FieldDef): boolean {\n if (f.source.kind === \"derived\" && f.source.computedId) return false;\n return f.filter?.enabled ?? f.source.kind === \"backend\";\n}\nexport function isPushdownFilter(f: FieldDef): boolean {\n return isFilterable(f) && (f.filter?.pushdown ?? f.source.kind === \"backend\");\n}\nexport function isSortable(f: FieldDef): boolean {\n if (f.source.kind === \"derived\" && f.source.computedId) return false;\n return f.sort?.enabled ?? f.source.kind === \"backend\";\n}\nexport function isSelectable(f: FieldDef): boolean {\n return f.select?.enabled ?? true;\n}\nexport function filterValues(f: FieldDef): FilterValues {\n return f.filter?.values ?? { source: \"autocomplete\" };\n}\n\n// ---- schema operations ----------------------------------------------------\n\n/** Index fields by name for O(1) lookup. */\nexport function indexFields<Row>(schema: FieldSchema<Row>): Map<string, FieldDef<Row>> {\n return new Map(schema.fields.map((f) => [f.name, f]));\n}\n\n/** Resolve the ordered, selectable FieldDefs for a query, falling back to schema\n * defaults, then to every `select.default` field. Unknown/duplicate/unselectable\n * names in `select` are dropped. */\nexport function selectedFields<Row>(\n schema: FieldSchema<Row>,\n q: { select: SelectColumn[] },\n): FieldDef<Row>[] {\n const order =\n q.select.length > 0\n ? q.select.map((c) => c.field)\n : (schema.defaultSelect ?? schema.fields.filter((f) => f.select?.default).map((f) => ({ field: f.name }))).map(\n (c) => c.field,\n );\n const seen = new Set<string>();\n const out: FieldDef<Row>[] = [];\n for (const name of order) {\n if (seen.has(name)) continue;\n const f = resolveField(schema, name);\n if (!f || !isSelectable(f)) continue;\n seen.add(name);\n seen.add(f.name);\n out.push(f);\n }\n return out;\n}\n\n/** Resolve a field token to a concrete field by canonical name, case-insensitive\n * name match, or alias match. */\nexport function resolveField<Row>(schema: FieldSchema<Row>, field: string): FieldDef<Row> | undefined {\n const token = field.trim();\n if (!token) return undefined;\n\n const byName = indexFields(schema);\n const byExactName = byName.get(token);\n if (byExactName) return byExactName;\n\n const needle = token.toLowerCase();\n for (const f of schema.fields) {\n if (f.name.toLowerCase() === needle) return f;\n if (aliasMatches(f, needle)) return f;\n }\n return undefined;\n}\n\n/** Return the canonical name for a query token, or undefined when unresolved. */\nexport function resolveFieldName<Row>(schema: FieldSchema<Row>, field: string): string | undefined {\n return resolveField(schema, field)?.name;\n}\n\n/** All configured aliases for a field (deduped, preserving display shape). */\nexport function aliasTermsFor<Row>(field: FieldDef<Row>): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n\n const add = (raw: unknown) => {\n if (typeof raw !== \"string\") return;\n const value = raw.trim();\n if (!value) return;\n const key = value.toLowerCase();\n if (seen.has(key)) return;\n seen.add(key);\n out.push(value);\n };\n\n add(field.alias);\n if (field.aliases) {\n for (const alias of field.aliases) add(alias);\n }\n\n return out;\n}\n\nfunction aliasMatches(field: FieldDef, needle: string): boolean {\n for (const alias of aliasTermsFor(field)) {\n if (alias.toLowerCase() === needle) return true;\n }\n return false;\n}\n\n/** Read a field's value from a row: derived `accessor` if present, else the\n * backend `path` (dotted) / `name`. Returns null when any path segment is missing. */\nexport function readFieldValue<Row, V = unknown>(field: FieldDef<Row, V>, row: Row): V | null {\n if (row == null) return null;\n if (field.source.kind === \"derived\" && field.source.accessor) {\n return field.source.accessor(row);\n }\n const path = (field.source.kind === \"backend\" && field.source.path) || field.name;\n const r = row as Record<string, unknown>;\n if (path in r) return (r[path] ?? null) as V | null;\n let cur: unknown = row;\n for (const part of path.split(\".\")) {\n if (cur == null || typeof cur !== \"object\") return null;\n cur = (cur as Record<string, unknown>)[part];\n }\n return (cur ?? null) as V | null;\n}\n\n/** Parse + validate a raw JSON schema document into a FieldSchema. The doc is the\n * same one the Go backend loads; here we drop backend `bindings` (the frontend\n * never sees SQL) and leave `render` as a string key the consumer maps to a\n * renderer at runtime. Throws with a readable message on a malformed document. */\nexport function loadSchema<Row = any>(doc: unknown): FieldSchema<Row> {\n const d = doc as Record<string, any>;\n if (!d || typeof d !== \"object\") throw new Error(\"schema: document is not an object\");\n if (typeof d.name !== \"string\") throw new Error(\"schema: missing `name`\");\n if (typeof d.idField !== \"string\") throw new Error(\"schema: missing `idField`\");\n if (!Array.isArray(d.fields) || d.fields.length === 0) throw new Error(\"schema: `fields` must be a non-empty array\");\n\n const fields: FieldDef<Row>[] = d.fields.map((raw: any, i: number) => projectField<Row>(raw, i));\n return {\n name: d.name,\n idField: d.idField,\n fields,\n defaultSort: d.defaultSort,\n defaultSelect: d.defaultSelect,\n defaultLimit: d.defaultLimit,\n };\n}\n\nfunction projectField<Row>(raw: any, i: number): FieldDef<Row> {\n if (!raw || typeof raw.name !== \"string\") throw new Error(`schema: field[${i}] missing \\`name\\``);\n if (typeof raw.label !== \"string\") throw new Error(`schema: field ${raw.name} missing \\`label\\``);\n if (typeof raw.type !== \"string\") throw new Error(`schema: field ${raw.name} missing \\`type\\``);\n\n // `source` accepts a string shorthand (\"backend\"|\"derived\") or an object.\n const rs = raw.source ?? (raw.bindings ? \"backend\" : \"derived\");\n let source: FieldSource<Row>;\n if (typeof rs === \"string\") {\n source = rs === \"derived\" ? { kind: \"derived\" } : { kind: \"backend\" };\n } else if (rs.kind === \"derived\") {\n source = { kind: \"derived\" }; // accessor is code → attached at runtime, never from JSON\n } else {\n source = { kind: \"backend\", path: rs.path, synthetic: rs.synthetic };\n }\n\n return {\n name: raw.name,\n label: raw.label,\n type: raw.type,\n source,\n filter: raw.filter,\n sort: raw.sort,\n select: raw.select,\n aggregate: raw.aggregate,\n group: raw.group,\n alias: raw.alias,\n aliases: normalizeAliases(raw.aliases, raw.alias),\n render: raw.render, // string key from JSON; consumer may override with a fn\n };\n}\n\nfunction normalizeAliases(rawAliases: unknown, rawAlias: unknown): string[] {\n const aliases: string[] = [];\n const seen = new Set<string>();\n const add = (raw: unknown) => {\n if (typeof raw !== \"string\") return;\n const value = raw.trim();\n if (!value) return;\n const key = value.toLowerCase();\n if (seen.has(key)) return;\n seen.add(key);\n aliases.push(value);\n };\n\n add(rawAlias);\n if (Array.isArray(rawAliases)) {\n for (const item of rawAliases) add(item);\n }\n return aliases;\n}\n","// ops.ts — which operators a field type allows, and value coercion.\n//\n// Single source of truth for \"what filters can I build on this field\", shared by\n// the QueryBuilder UI, the CellMenu quick-filters, and client-side applyQuery.\n// The backend (backends/go) enforces the same matrix independently — never trust\n// the client.\n//\n// Every value can be NULL, so `is_null`/`is_not_null` are available on EVERY\n// type, including bool (design feedback).\n\nimport type { FilterOp, FieldType, FieldDef, WhereClause } from \"./index\";\n\nconst NULLITY: FilterOp[] = [\"is_null\", \"is_not_null\"];\n\n/** Operators that are each other's logical complement. Negating one of these is\n * a clean op flip (no NULL surprises the two ops don't already share), so the\n * UI can render `≥ 10`'s negation as `< 10` rather than `NOT (≥ 10)`. Ops absent\n * here (`contains`/`starts_with`/`ends_with`/`includes`) have no single-op\n * complement and negate via the `WhereClause.negated` flag instead. */\nconst COMPLEMENT_OP: Partial<Record<FilterOp, FilterOp>> = {\n \"=\": \"!=\",\n \"!=\": \"=\",\n \">\": \"<=\",\n \"<=\": \">\",\n \">=\": \"<\",\n \"<\": \">=\",\n is_null: \"is_not_null\",\n is_not_null: \"is_null\",\n matches_regex: \"not_matches_regex\",\n not_matches_regex: \"matches_regex\",\n};\n\n/** The logical negation of a predicate. Prefers flipping to the complementary\n * operator (nicer to read, and the server's op allowlist already covers it);\n * falls back to toggling the `negated` flag when the op has no complement, or\n * when the complement is not in `allowedOps` (a field's `filter.ops` override\n * could disable it, and the server would reject a disabled op). `allowedOps`,\n * when given, is the field's effective operator set from `opsForField`. */\nexport function negateClause(clause: WhereClause, allowedOps?: readonly FilterOp[]): WhereClause {\n const complement = COMPLEMENT_OP[clause.op];\n if (complement && (!allowedOps || allowedOps.includes(complement))) {\n const next: WhereClause = { field: clause.field, op: complement, value: clause.value };\n return next; // a complement fully expresses the negation; drop any stale flag\n }\n const next: WhereClause = { field: clause.field, op: clause.op, value: clause.value };\n if (!clause.negated) next.negated = true;\n return next;\n}\n\n/** Whether a predicate is negative — either an op that reads as a negation\n * (`!=`, `not_matches_regex`, `is_not_null`) or one carrying the `negated`\n * flag. Used to sort candidate filters into the CellMenu's two columns. */\nexport function isNegativePredicate(clause: Pick<WhereClause, \"op\" | \"negated\">): boolean {\n return Boolean(clause.negated) || clause.op === \"!=\" || clause.op === \"not_matches_regex\" || clause.op === \"is_not_null\";\n}\n\n/** A specific operator + negation state, e.g. `{ op: \"<\", negated: false }` or\n * `{ op: \"contains\", negated: true }`. */\nexport interface OpChoice {\n op: FilterOp;\n negated: boolean;\n}\n\n/** A positive operator (\"keep\") and its logical negation (\"exclude\") — the row\n * shape rendered by both the CellMenu quick-filters and the WHERE operator\n * picker, so the two read identically. */\nexport interface OpPair {\n keep: OpChoice;\n exclude: OpChoice;\n}\n\n/** Positive operators surfaced in the \"keep\" column, in display order. Each\n * one's negation lands opposite via `negateClause`, so the pair covers\n * `<`/`<=`/`!=`/`not_matches_regex` without listing them separately. */\nconst POSITIVE_OP_ORDER: FilterOp[] = [\"=\", \">\", \">=\", \"contains\", \"starts_with\", \"ends_with\", \"matches_regex\", \"includes\"];\n\n/** The keep/exclude operator pairs offered for a field: each positive op with\n * its negation opposite, then a nullity row (`is null`/`is not null`) when the\n * field allows it. Derived from the field's effective op set and `negateClause`,\n * so a `filter.ops` override narrows the offered pairs too. */\nexport function opPairsForField(field: Pick<FieldDef, \"type\" | \"filter\">): OpPair[] {\n const ops = opsForField(field);\n const allow = new Set(ops);\n const pairs: OpPair[] = [];\n for (const op of POSITIVE_OP_ORDER) {\n if (!allow.has(op)) continue;\n const neg = negateClause({ field: \"\", op, value: \"\" }, ops);\n pairs.push({ keep: { op, negated: false }, exclude: { op: neg.op, negated: Boolean(neg.negated) } });\n }\n if (allow.has(\"is_null\") || allow.has(\"is_not_null\")) {\n pairs.push({ keep: { op: \"is_null\", negated: false }, exclude: { op: \"is_not_null\", negated: false } });\n }\n return pairs;\n}\n\n/** Default operator set per type. A FieldDef.filter.ops overrides this. */\nexport const OPS_BY_TYPE: Record<FieldType, FilterOp[]> = {\n text: [\n \"=\",\n \"!=\",\n \"contains\",\n \"starts_with\",\n \"ends_with\",\n \"matches_regex\",\n \"not_matches_regex\",\n ...NULLITY,\n ],\n enum: [\"=\", \"!=\", ...NULLITY],\n number: [\"=\", \"!=\", \">\", \">=\", \"<\", \"<=\", ...NULLITY],\n datetime: [\"=\", \"!=\", \">\", \">=\", \"<\", \"<=\", ...NULLITY],\n bool: [\"=\", \"!=\", ...NULLITY],\n textarray: [\"includes\", ...NULLITY],\n};\n\n/** Operators that take no value (the value input is hidden). */\nexport const NULLARY_OPS: ReadonlySet<FilterOp> = new Set<FilterOp>(NULLITY);\n\n/** Effective operators for a field (its override, else the type default). */\nexport function opsForField(field: Pick<FieldDef, \"type\" | \"filter\">): FilterOp[] {\n return field.filter?.ops ?? OPS_BY_TYPE[field.type];\n}\n\n/** Whether an op is valid for a type per the default matrix (server mirror lives\n * in Go opAllowed; keep them in lockstep). */\nexport function opAllowedForType(type: FieldType, op: FilterOp): boolean {\n return OPS_BY_TYPE[type].includes(op);\n}\n\n/** Coerce a raw string value to the type the field expects, for client-side\n * comparison. Throws on invalid input (e.g. a non-numeric value on a number\n * field) so callers can surface it rather than silently mis-filtering. */\nexport function coerceValue(type: FieldType, raw: string): string | number | boolean {\n switch (type) {\n case \"number\": {\n const n = Number(raw);\n if (raw.trim() === \"\" || Number.isNaN(n)) throw new Error(`not a number: ${JSON.stringify(raw)}`);\n return n;\n }\n case \"bool\": {\n const v = raw.toLowerCase();\n if (v === \"true\" || v === \"1\" || v === \"t\") return true;\n if (v === \"false\" || v === \"0\" || v === \"f\") return false;\n throw new Error(`not a bool: ${JSON.stringify(raw)}`);\n }\n case \"datetime\": {\n const ms = Date.parse(raw);\n if (Number.isNaN(ms)) throw new Error(`not a datetime: ${JSON.stringify(raw)}`);\n return ms;\n }\n default:\n return raw;\n }\n}\n","// agg.ts — which aggregate ops a field type allows, and which fields can be a\n// measure or a group-by key. The single source of truth shared by the\n// QueryBuilder's metric editor, the client-side applyAggregations, and (mirrored\n// independently in backends/go) the SQL compiler. Never trust the client: the Go\n// `aggOpAllowed` enforces the same matrix before any GROUP BY is built.\n\nimport type { AggOp } from \"./query\";\nimport type { FieldType, FieldDef } from \"./schema\";\n\n/** Default aggregate ops per field type. A FieldDef.aggregate.ops overrides this.\n * `count`/`count_distinct` work on anything countable; sum/avg are numeric-only;\n * min/max need an ordered domain (numbers, datetimes, and lexical text/enum). */\nexport const AGG_OPS_BY_TYPE: Record<FieldType, AggOp[]> = {\n number: [\"count\", \"count_distinct\", \"sum\", \"avg\", \"min\", \"max\"],\n datetime: [\"count\", \"count_distinct\", \"min\", \"max\"],\n enum: [\"count\", \"count_distinct\", \"min\", \"max\"],\n text: [\"count\", \"count_distinct\", \"min\", \"max\"],\n bool: [\"count\", \"count_distinct\"],\n textarray: [\"count\"],\n};\n\n/** Ops that reduce a measure column. `count` is the only op that may omit a\n * field (counting rows), so it's the only one absent here. */\nexport const AGG_OPS_NEEDING_FIELD: ReadonlySet<AggOp> = new Set<AggOp>([\n \"count_distinct\",\n \"sum\",\n \"avg\",\n \"min\",\n \"max\",\n]);\n\n/** Whether an op requires a measure column. */\nexport function aggOpNeedsField(op: AggOp): boolean {\n return AGG_OPS_NEEDING_FIELD.has(op);\n}\n\n/** Effective aggregate ops for a field (its override, else the type default). */\nexport function aggOpsForField(field: Pick<FieldDef, \"type\" | \"aggregate\">): AggOp[] {\n return field.aggregate?.ops ?? AGG_OPS_BY_TYPE[field.type];\n}\n\n/** Whether an op is valid for a type per the default matrix (Go aggOpAllowed is\n * the server mirror; keep them in lockstep). */\nexport function aggOpAllowedForType(type: FieldType, op: AggOp): boolean {\n return AGG_OPS_BY_TYPE[type].includes(op);\n}\n\n/** Can this field be the MEASURE of a metric? Only backend fields (they have SQL\n * to aggregate); derived/render-only columns are never pushed to the server. An\n * explicit `aggregate.measure` wins over the type default. */\nexport function isMeasurable(field: FieldDef): boolean {\n if (field.source.kind !== \"backend\") return false;\n if (field.aggregate?.measure != null) return field.aggregate.measure;\n return aggOpsForField(field).length > 0;\n}\n\n/** Can this field be a GROUP BY key? Backend fields only. Default: low-cardinality\n * types (enum/text/bool); numbers/datetimes need bucketing (out of scope) so they\n * default off, but `aggregate.groupable: true` opts any backend field in. */\nexport function isGroupable(field: FieldDef): boolean {\n if (field.source.kind !== \"backend\") return false;\n if (field.aggregate?.groupable != null) return field.aggregate.groupable;\n return field.type === \"enum\" || field.type === \"text\" || field.type === \"bool\";\n}\n","// encode.ts — URL serialization + the server-bound query subset.\n//\n// Two distinct serializations, deliberately kept apart:\n//\n// 1. encodeQuery / decodeQuery — the *whole* QueryState (incl. view state:\n// select columns + widths) ⇄ a base64url `?q=` token. This is what makes\n// reload, back/forward, bookmarks, and \"send a coworker your view\" all work\n// (decisions #4 + #5). Defaults are omitted to keep the token short.\n//\n// 2. toServerQuery — the subset the backend acts on: pushdown\n// WHERE clauses, server-sortable ORDER BY (remapped through sort.field), the\n// page window, and the field names to SELECT (so the backend evaluates the\n// computed/synthetic columns the view needs — requested ∪ always-needed,\n// design decision #2). Per-column widths never leave the client.\n//\n// Back-compat: decodeQuery accepts legacy compact shapes: `o` as a single\n// object (pre-multi-sort) and `c`/`s` as a string[] (pre-width).\n\nimport { EMPTY_QUERY, MAX_QUERY_TOKEN_LENGTH, normalizeQueryState, predicatesOf } from \"./query\";\nimport type { QueryState, WhereTerm, OrderByClause, SelectColumn, AggregationClause } from \"./query\";\nimport type { FieldSchema, FieldDef } from \"./schema\";\nimport { indexFields, isFilterable, isPushdownFilter, isSortable, resolveFieldName, selectedFields } from \"./schema\";\n\n// ---- base64url (browser + node) -------------------------------------------\n\nfunction toBase64Url(json: string): string {\n let b64: string;\n if (typeof Buffer !== \"undefined\") {\n b64 = Buffer.from(json, \"utf8\").toString(\"base64\");\n } else {\n const bytes = new TextEncoder().encode(json);\n let bin = \"\";\n for (const b of bytes) bin += String.fromCharCode(b);\n b64 = btoa(bin);\n }\n return b64.replace(/=+$/, \"\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\");\n}\n\nfunction fromBase64Url(s: string): string {\n const b64 = s.replace(/-/g, \"+\").replace(/_/g, \"/\") + \"===\".slice((s.length + 3) % 4);\n if (typeof Buffer !== \"undefined\") return Buffer.from(b64, \"base64\").toString(\"utf8\");\n const bin = atob(b64);\n const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));\n return new TextDecoder().decode(bytes);\n}\n\n// ---- compact wire form ----------------------------------------------------\n// Keys are short to keep `?q=` small; any field at its default is omitted.\n// s = select (as [field] or [field, width] tuples)\n// w = where, o = orderBy, l = limit, f = offset\n\ninterface CompactQuery {\n s?: Array<[string] | [string, number]> | string[]; // string[] = legacy\n w?: WhereTerm[];\n o?: OrderByClause[] | OrderByClause; // object accepted for legacy decode\n l?: number;\n f?: number;\n g?: AggregationClause[]; // aggregation metrics (omitted when none)\n c?: string[]; // legacy alias for select column names\n}\n\n/** QueryState → base64url token (omitting defaults). All-default query → \"\". */\nexport function encodeQuery(q: QueryState): string {\n q = normalizeQueryState(q);\n const c: CompactQuery = {};\n if (q.select.length)\n c.s = q.select.map((col): [string] | [string, number] => (col.width != null ? [col.field, col.width] : [col.field]));\n if (q.where.length) c.w = q.where;\n if (q.orderBy.length) c.o = q.orderBy;\n c.l = q.limit;\n if (q.offset) c.f = q.offset;\n if (q.aggregations?.length) c.g = q.aggregations;\n if (Object.keys(c).length === 0) return \"\";\n return toBase64Url(JSON.stringify(c));\n}\n\n/** base64url token → QueryState. Tolerant: bad input and legacy shapes both\n * normalize to a valid QueryState (never throws). */\nexport function decodeQuery(token: string): QueryState {\n if (!token) return { ...EMPTY_QUERY };\n if (token.length > MAX_QUERY_TOKEN_LENGTH) return { ...EMPTY_QUERY };\n try {\n const c = JSON.parse(fromBase64Url(token)) as CompactQuery;\n const out = normalizeQueryState({\n select: normalizeSelect(c.s ?? c.c),\n where: Array.isArray(c.w) ? c.w : [],\n orderBy: normalizeOrderBy(c.o),\n limit: c.l,\n offset: c.f,\n aggregations: c.g,\n });\n return out;\n } catch {\n return { ...EMPTY_QUERY };\n }\n}\n\nfunction normalizeSelect(s: CompactQuery[\"s\"] | CompactQuery[\"c\"]): SelectColumn[] {\n if (!Array.isArray(s)) return [];\n return s\n .map((item): SelectColumn | null => {\n if (typeof item === \"string\") return { field: item }; // legacy string[]\n if (Array.isArray(item) && typeof item[0] === \"string\") {\n return typeof item[1] === \"number\" ? { field: item[0], width: item[1] } : { field: item[0] };\n }\n return null;\n })\n .filter((x): x is SelectColumn => x != null);\n}\n\nfunction normalizeOrderBy(o: CompactQuery[\"o\"]): OrderByClause[] {\n if (!o) return [];\n if (Array.isArray(o)) return o;\n return [o]; // legacy single-object form → one-element array\n}\n\n// ---- server-bound subset --------------------------------------------------\n\n/** The backend request derived from a QueryState + its schema. Mirrors the Go\n * `WireQuery`. View-only state (column widths) is intentionally absent. */\nexport interface ServerQuery {\n /** field names to return — visible columns ∪ fields referenced by where/orderBy. */\n select: string[];\n /** only terms whose every predicate is on a pushdown-filterable field. */\n where: WhereTerm[];\n /** only terms on server-sortable fields, with `field` already remapped to the\n * field's server sort key (FieldDef.sort.field) when set. */\n orderBy: OrderByClause[];\n limit: number;\n offset: number;\n}\n\n/** Project a QueryState into the server request, dropping client-only filters,\n * client-only sorts, and remapping sort fields through `sort.field`. */\nexport function toServerQuery<Row>(q: QueryState, schema: FieldSchema<Row>): ServerQuery {\n q = normalizeQueryState(q);\n const byName = indexFields(schema);\n const resolveField = (name: string) => resolveFieldName(schema, name) ?? name;\n\n const where = q.where.filter((term) => termIsPushdown(term, byName, resolveField));\n\n const orderBy: OrderByClause[] = [];\n for (const term of q.orderBy) {\n const field = resolveField(term.field);\n const f = byName.get(field);\n if (!f || !isSortable(f) || f.source.kind !== \"backend\") continue;\n orderBy.push({ ...term, field: f.sort?.field ?? f.name });\n }\n\n // SELECT = the BACKEND columns the response rows must carry: visible backend\n // columns, the id (for selection/refresh), and any backend column a\n // CLIENT-SIDE filter reads. Derived/render-only columns are computed on the\n // client and have no SQL to select; pushdown filters/sorts run in SQL and need\n // not be returned (design decision #2: requested ∪ always-needed).\n const select = new Set<string>(\n selectedFields(schema, q)\n .filter((f) => f.source.kind === \"backend\")\n .map((f) => f.name),\n );\n for (const field of selectedFields(schema, q)) {\n if (field.source.kind === \"derived\") {\n for (const name of field.source.dependencies ?? []) {\n if (byName.get(name)?.source.kind === \"backend\") select.add(name);\n }\n }\n }\n select.add(schema.idField);\n for (const term of q.where) {\n for (const cl of predicatesOf(term)) {\n const field = resolveField(cl.field);\n const f = byName.get(field);\n if (f && isFilterable(f) && !isPushdownFilter(f) && f.source.kind === \"backend\") select.add(field);\n }\n }\n\n return { select: [...select].sort(), where, orderBy, limit: q.limit, offset: q.offset };\n}\n\n/** A term can push down only if EVERY predicate it contains is on a\n * pushdown-filterable field. An OR group is all-or-nothing: dropping one\n * disjunct would change the set the group matches, so a group with any\n * non-pushdown member stays client-side in full. */\nfunction termIsPushdown<Row>(\n term: WhereTerm,\n byName: Map<string, FieldDef<Row>>,\n resolveField: (name: string) => string,\n): boolean {\n return predicatesOf(term).every((cl) => {\n const f = byName.get(resolveField(cl.field));\n return f != null && isPushdownFilter(f);\n });\n}\n\n// ---- aggregation server subset --------------------------------------------\n\n/** The backend request for the metric panel. Scope is the whole filtered set:\n * the SAME pushdown WHERE as the rows query, but NO ORDER BY / LIMIT / OFFSET —\n * metrics describe every matching row, not the visible page. Mirrors the Go\n * AggSpec list. */\nexport interface AggregationRequest {\n where: WhereTerm[];\n aggregations: AggregationClause[];\n}\n\n/** One group's result. `keys` has one entry per AggregationClause.groupBy field,\n * in axis order (`[]` for a grand total); a null key is the NULL/empty bucket. */\nexport interface AggregationBucket {\n keys: (string | null)[];\n /** The metric: a number for count/sum/avg, or the column's value for min/max\n * (which may be a string for text/datetime). null when undefined (e.g. avg of\n * an all-null column). */\n value: number | string | null;\n /** COUNT(*) of rows in the group — always present, even when `value` isn't a\n * count, so the panel can show group sizes / shares. */\n count: number;\n}\n\nexport interface AggregationResultEntry {\n /** Echoes AggregationClause.id. */\n id: string;\n buckets: AggregationBucket[];\n}\n\nexport interface AggregationResult {\n /** One entry per requested aggregation, in request order. */\n metrics: AggregationResultEntry[];\n}\n\n/** Project a QueryState into the metric request: the pushdown WHERE subset (same\n * rule as toServerQuery) plus the aggregations whose measure + group fields are\n * all server-capable backend columns. Aggregations referencing a derived /\n * unknown field are dropped — the server has no SQL for them. */\nexport function toAggregationQuery<Row>(q: QueryState, schema: FieldSchema<Row>): AggregationRequest {\n q = normalizeQueryState(q);\n const byName = indexFields(schema);\n const resolveField = (name: string) => resolveFieldName(schema, name) ?? name;\n\n const where = q.where.filter((term) => termIsPushdown(term, byName, resolveField));\n\n const isBackend = (name: string | undefined): boolean => {\n if (name == null) return true; // omitted measure (count(*)) is fine\n const field = resolveField(name);\n const f = byName.get(field);\n return f != null && f.source.kind === \"backend\";\n };\n\n const aggregations = (q.aggregations ?? []).filter(\n (a) => isBackend(a.field) && a.groupBy.every((g) => isBackend(g)),\n );\n\n return { where, aggregations };\n}\n","// apply.ts — client-side filter + multi-sort + paginate.\n//\n// applyQuery is the client mirror of the backend compiler. Three jobs:\n// 1. The executor for purely client-side datasets (no backend at all).\n// 2. The executor for derived / non-pushdown columns the backend can't filter.\n// 3. Idempotent defense-in-depth over server-filtered fields (re-applying a\n// server-handled clause must not change the result).\n//\n// It must agree with backends/go's Compile on operator semantics:\n// - `contains`/`starts_with`/`ends_with` are case-insensitive\n// - textarray `is_null` = empty-or-missing; `is_not_null` = any element;\n// `includes` = case-insensitive membership\n// - NULLs sort per OrderByClause.nulls (default \"last\")\n// - multi-sort is a stable lexicographic fold over orderBy in priority order\n\nimport { isComputedField } from \"./computed\";\nimport type { AggOp, AggregationClause, QueryState, WhereClause, WhereTerm } from \"./query\";\nimport type { FieldSchema, FieldDef } from \"./schema\";\nimport type { AggregationBucket, AggregationResult } from \"./encode\";\nimport { indexFields, readFieldValue, resolveFieldName } from \"./schema\";\nimport { isOrGroup, predicatesOf } from \"./query\";\nimport { coerceValue } from \"./ops\";\n\nexport interface ApplyResult<Row> {\n /** The page slice (after filter + sort + offset/limit). */\n rows: Row[];\n /** Total matching rows *before* pagination — powers \"showing N of M\". */\n total: number;\n}\n\n/** Filter + multi-sort + paginate `rows` per `q`, resolving field types/paths\n * from `schema`. Pure; never mutates `rows`. Clauses/sorts on unknown fields are\n * ignored (the backend already enforced its own allowlist). */\nexport function applyQuery<Row>(rows: Row[], q: QueryState, schema: FieldSchema<Row>): ApplyResult<Row> {\n const byName = indexFields(schema);\n const resolveField = (name: string) => resolveFieldName(schema, name) ?? name;\n // Terms touching a computed column are dropped here (the formula engine\n // evaluates those separately; `byName` has no opinion on them anyway). An OR\n // group is all-or-nothing, so a single computed member drops the whole term.\n const whereTerms = q.where\n .filter((term) => !predicatesOf(term).some((c) => isComputedField(c.field)))\n .map((term) => prepareWhereTerm(term, resolveField));\n const orderBy = q.orderBy\n .filter((term) => !isComputedField(term.field))\n .map((term) => ({ ...term, field: resolveField(term.field) }));\n\n let out = rows.filter((row) => whereTerms.every((term) => matchesTerm(byName, row, term)));\n const total = out.length;\n\n if (orderBy.length) {\n const terms = orderBy\n .map((t) => ({\n field: byName.get(t.field),\n dir: t.dir,\n nullsLast: (t.nulls ?? \"last\") === \"last\",\n extract: compileRegex(t.extract?.regex),\n }))\n .filter(\n (t): t is { field: FieldDef<Row>; dir: \"asc\" | \"desc\"; nullsLast: boolean; extract: RegExp | null | undefined } =>\n t.field != null,\n );\n out = [...out].sort((a, b) => {\n for (const t of terms) {\n const av = extractSortValue(readFieldValue(t.field, a), t.extract);\n const bv = extractSortValue(readFieldValue(t.field, b), t.extract);\n const aNull = av == null;\n const bNull = bv == null;\n if (aNull || bNull) {\n if (aNull && bNull) continue;\n return (aNull ? 1 : -1) * (t.nullsLast ? 1 : -1);\n }\n const cmp = compare(av, bv);\n if (cmp !== 0) return t.dir === \"asc\" ? cmp : -cmp;\n }\n return 0;\n });\n }\n\n const start = q.offset > 0 ? q.offset : 0;\n const end = q.limit > 0 ? start + q.limit : out.length;\n return { rows: out.slice(start, end), total };\n}\n\n/** Does one row satisfy one clause? Exposed for the CellMenu preview + tests. */\nexport function matchesClause<Row>(row: Row, clause: WhereClause, schema: FieldSchema<Row>): boolean {\n return matchesWith(indexFields(schema), row, prepareWhereClause(clause));\n}\n\n/** Client-side mirror of the backend GROUP BY (the executor for `clientRows`\n * mode / the demo). Scope matches the server contract: the WHERE-filtered set\n * only — ORDER BY / LIMIT / OFFSET are intentionally ignored, so a metric\n * reflects every matching row, not the visible page. Must agree with\n * backends/go's aggregate compile on op semantics. */\nexport function applyAggregations<Row>(rows: Row[], q: QueryState, schema: FieldSchema<Row>): AggregationResult {\n const byName = indexFields(schema);\n const resolveField = (name: string) => resolveFieldName(schema, name) ?? name;\n const whereTerms = q.where\n .filter((term) => !predicatesOf(term).some((c) => isComputedField(c.field)))\n .map((term) => prepareWhereTerm(term, resolveField));\n const filtered = rows.filter((row) => whereTerms.every((term) => matchesTerm(byName, row, term)));\n const metrics = (q.aggregations ?? [])\n .filter((a) => !isComputedField(a.field ?? \"\") && !a.groupBy.some(isComputedField))\n .map((agg) => ({\n id: agg.id,\n buckets: computeBuckets(filtered, agg, byName, resolveField),\n }));\n return { metrics };\n}\n\nfunction computeBuckets<Row>(\n rows: Row[],\n agg: AggregationClause,\n byName: Map<string, FieldDef<Row>>,\n resolveField: (field: string) => string,\n): AggregationBucket[] {\n // One slot per groupBy entry, preserving position. An unresolved field stays\n // in the array as `undefined` so every bucket's `keys` has exactly one element\n // per requested group field — the panel renders keys positionally against\n // `clause.groupBy`, so dropping an entry here would shift every later column.\n const groupFields = agg.groupBy.map((n) => byName.get(resolveField(n)));\n const measure = agg.field ? byName.get(resolveField(agg.field)) : undefined;\n\n const groups = new Map<string, { keys: (string | null)[]; rows: Row[] }>();\n for (const row of rows) {\n const keys = groupFields.map((f) => {\n if (!f) return null; // unresolved group field → NULL bucket, never a missing column\n const v = readFieldValue(f, row);\n return v == null || v === \"\" ? null : String(v);\n });\n const k = JSON.stringify(keys);\n let g = groups.get(k);\n if (!g) {\n g = { keys, rows: [] };\n groups.set(k, g);\n }\n g.rows.push(row);\n }\n\n const buckets = [...groups.values()].map((g) => ({\n keys: g.keys,\n count: g.rows.length,\n value: aggValue(agg.op, measure, g.rows),\n }));\n return sortBuckets(buckets);\n}\n\nfunction aggValue<Row>(op: AggOp, measure: FieldDef<Row> | undefined, rows: Row[]): number | string | null {\n if (op === \"count\") {\n if (!measure) return rows.length; // COUNT(*)\n let n = 0;\n for (const r of rows) if (readFieldValue(measure, r) != null) n++; // COUNT(expr) = non-null\n return n;\n }\n if (!measure) return null;\n const values = rows.map((r) => readFieldValue(measure, r)).filter((v) => v != null);\n\n switch (op) {\n case \"count_distinct\":\n return new Set(values.map((v) => String(v))).size;\n case \"sum\":\n case \"avg\": {\n let sum = 0;\n let n = 0;\n for (const v of values) {\n const num = typeof v === \"number\" ? v : Number(v);\n if (Number.isFinite(num)) {\n sum += num;\n n++;\n }\n }\n if (op === \"avg\") return n ? sum / n : null;\n return n ? sum : null;\n }\n case \"min\":\n case \"max\": {\n let best: unknown;\n for (const v of values) {\n if (best === undefined) {\n best = v;\n continue;\n }\n const cmp = compare(v, best);\n if (op === \"min\" ? cmp < 0 : cmp > 0) best = v;\n }\n return (best as number | string | undefined) ?? null;\n }\n default:\n return null;\n }\n}\n\n/** Stable display order: descending by the metric (or group size when the metric\n * isn't numeric), then by group key. The 2D pivot ignores this; the 1D bar list\n * and the flat N-column table read top-to-bottom from it. */\nfunction sortBuckets(buckets: AggregationBucket[]): AggregationBucket[] {\n return [...buckets].sort((a, b) => {\n const av = typeof a.value === \"number\" ? a.value : a.count;\n const bv = typeof b.value === \"number\" ? b.value : b.count;\n if (av !== bv) return bv - av;\n return JSON.stringify(a.keys).localeCompare(JSON.stringify(b.keys));\n });\n}\n\ninterface PreparedWhereClause {\n clause: WhereClause;\n regex: RegExp | null | undefined;\n}\n\ntype PreparedWhereTerm =\n | { kind: \"lit\"; predicate: PreparedWhereClause }\n | { kind: \"or\"; predicates: PreparedWhereClause[] };\n\nfunction prepareWhereClause(clause: WhereClause): PreparedWhereClause {\n const usesRegex = clause.op === \"matches_regex\" || clause.op === \"not_matches_regex\";\n return { clause, regex: usesRegex ? compileRegex(clause.value) : undefined };\n}\n\nfunction prepareWhereTerm(term: WhereTerm, resolveField: (name: string) => string): PreparedWhereTerm {\n if (isOrGroup(term)) {\n return { kind: \"or\", predicates: term.any.map((c) => prepareWhereClause({ ...c, field: resolveField(c.field) })) };\n }\n return { kind: \"lit\", predicate: prepareWhereClause({ ...term, field: resolveField(term.field) }) };\n}\n\n/** A row satisfies the WHERE when every term matches (AND); a term matches when\n * its sole predicate matches, or — for an OR group — when any member does. */\nfunction matchesTerm<Row>(byName: Map<string, FieldDef<Row>>, row: Row, term: PreparedWhereTerm): boolean {\n if (term.kind === \"lit\") return matchesWith(byName, row, term.predicate);\n // Normalization never yields an empty group (0 dropped, 1 flattened), so\n // `some` over a non-empty list is the OR.\n return term.predicates.some((p) => matchesWith(byName, row, p));\n}\n\nfunction matchesWith<Row>(byName: Map<string, FieldDef<Row>>, row: Row, prepared: PreparedWhereClause): boolean {\n const { clause } = prepared;\n const field = byName.get(clause.field);\n if (!field) return true; // unknown field → no client opinion\n const v = readFieldValue(field, row);\n\n // Empty value on a non-equality, value-taking op is a \"no filter\" signal —\n // true for BOTH the predicate and its negation (matches the UI + the Go\n // compiler, which skip such clauses). Nullary ops legitimately use value \"\".\n if (\n clause.value === \"\" &&\n clause.op !== \"=\" &&\n clause.op !== \"!=\" &&\n clause.op !== \"is_null\" &&\n clause.op !== \"is_not_null\"\n ) {\n return true;\n }\n\n const base = matchesBase(field, v, prepared);\n if (!clause.negated) return base;\n // Null-exclusive NOT: a NULL/empty value satisfies neither the predicate nor\n // its negation (mirrors `not_matches_regex` and the server's trailing\n // `AND <expr> IS NOT NULL`).\n const isNull = Array.isArray(v) ? v.length === 0 : v == null || v === \"\";\n return !isNull && !base;\n}\n\n/** The bare predicate result, ignoring `negated` and the empty-value skip\n * (both handled by `matchesWith`). */\nfunction matchesBase<Row>(field: FieldDef<Row>, v: unknown, prepared: PreparedWhereClause): boolean {\n const { clause, regex } = prepared;\n\n // Nullity, with array-aware semantics.\n if (clause.op === \"is_null\") return Array.isArray(v) ? v.length === 0 : v == null || v === \"\";\n if (clause.op === \"is_not_null\") return Array.isArray(v) ? v.length > 0 : v != null && v !== \"\";\n\n // `includes` is array membership (ARRAY_HAS): a null/missing array contains\n // nothing. Handle before the array/scalar split so null can't fall through.\n if (clause.op === \"includes\") {\n const arr = Array.isArray(v) ? v : [];\n const needle = clause.value.toLowerCase();\n return arr.some((x) => String(x).toLowerCase() === needle);\n }\n\n if (clause.op === \"matches_regex\" || clause.op === \"not_matches_regex\") {\n if (v == null || Array.isArray(v)) return false;\n if (!regex) return false;\n const matches = regex.test(String(v));\n return clause.op === \"matches_regex\" ? matches : !matches;\n }\n\n if (Array.isArray(v)) {\n const needle = clause.value.toLowerCase();\n const hay = v.map((x) => String(x).toLowerCase());\n switch (clause.op) {\n case \"contains\":\n return hay.some((s) => s.includes(needle));\n case \"=\":\n return hay.includes(needle);\n case \"!=\":\n return !hay.includes(needle);\n default:\n return true;\n }\n }\n\n switch (clause.op) {\n case \"=\":\n return String(v ?? \"\") === String(coerceSafe(field, clause.value));\n case \"!=\":\n return String(v ?? \"\") !== String(coerceSafe(field, clause.value));\n case \">\":\n return compare(v, coerceSafe(field, clause.value)) > 0;\n case \">=\":\n return compare(v, coerceSafe(field, clause.value)) >= 0;\n case \"<\":\n return compare(v, coerceSafe(field, clause.value)) < 0;\n case \"<=\":\n return compare(v, coerceSafe(field, clause.value)) <= 0;\n case \"contains\":\n return String(v ?? \"\").toLowerCase().includes(clause.value.toLowerCase());\n case \"starts_with\":\n return String(v ?? \"\").toLowerCase().startsWith(clause.value.toLowerCase());\n case \"ends_with\":\n return String(v ?? \"\").toLowerCase().endsWith(clause.value.toLowerCase());\n default:\n return true;\n }\n}\n\n/** `null` means an invalid pattern; `undefined` means no regex was requested. */\nfunction compileRegex(pattern: string | undefined): RegExp | null | undefined {\n if (pattern === undefined) return undefined;\n try {\n return new RegExp(pattern);\n } catch {\n return null;\n }\n}\n\nfunction extractSortValue(value: unknown, regex: RegExp | null | undefined): unknown {\n if (regex === undefined) return value;\n if (regex === null || value == null) return null;\n const match = regex.exec(String(value));\n if (!match) return null;\n return match.length > 1 ? (match[1] ?? null) : match[0];\n}\n\n/** Coerce per the field type, falling back to the raw string if coercion fails\n * (so a malformed value degrades to a string compare rather than throwing). */\nfunction coerceSafe(field: FieldDef, raw: string): string | number | boolean {\n try {\n return coerceValue(field.type, raw);\n } catch {\n return raw;\n }\n}\n\nfunction compare(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (typeof a === \"boolean\" && typeof b === \"boolean\") return a === b ? 0 : a ? 1 : -1;\n return String(a).localeCompare(String(b));\n}\n","// adapters.ts — the pluggable seams. Everything project-specific is injected\n// here so the package core stays generic.\n//\n// - Transport : how rows + filter-value suggestions are fetched.\n// - StorageAdapter: where saved/last queries live (in-memory by default in\n// the React hook; explicitly durable when callers opt in).\n\nimport { normalizeQueryState } from \"./query\";\nimport type { QueryState, RowId } from \"./query\";\nimport type { ServerQuery, AggregationRequest, AggregationResult } from \"./encode\";\n\n// ---- Transport ------------------------------------------------------------\n\nexport interface FetchRowsResult<Row> {\n rows: Row[];\n /** Total matching rows before pagination (for \"N of M\" + paging bounds). */\n total: number;\n}\n\n/** A keystroke-driven distinct-value lookup. Autocomplete is the DEFAULT value\n * source for every field (design feedback), so this is how the filter combobox\n * finds options. `search` is the user's current input; the backend returns the\n * best matches and whether it truncated (so the UI can prompt \"keep typing\"). */\nexport interface DistinctValuesQuery {\n field: string;\n /** Current substring/prefix the user has typed (empty = top values). */\n search: string;\n /** Max suggestions to return (default chosen by the adapter, ~50). */\n limit?: number;\n}\n\nexport interface DistinctValuesResult {\n values: string[];\n /** true when more matches exist than were returned — refine by typing. */\n hasMore: boolean;\n /** True if the selected field has at least one NULL in the source dataset.\n * False if that field is guaranteed non-null. Omitted when the backend does\n * not compute this metadata. */\n hasNull?: boolean;\n}\n\n/** Per-field metadata for the field picker. */\nexport interface FieldStats {\n distinct?: number;\n min?: string | number;\n max?: string | number;\n}\n\nexport interface Transport<Row = any> {\n /** Run a server query. The only required method — a purely client-side table\n * can omit a Transport entirely and rely on applyQuery over local rows. */\n fetchRows(query: ServerQuery, signal?: AbortSignal): Promise<FetchRowsResult<Row>>;\n\n /** Distinct values for filter autocomplete. Strongly recommended: without it,\n * fields fall back to freeform input. The backend should match `search`\n * server-side (e.g. ILIKE prefix) and cap at `limit` so large domains stay\n * fast and refine per keystroke. */\n fetchDistinctValues?(q: DistinctValuesQuery, signal?: AbortSignal): Promise<DistinctValuesResult>;\n\n /** Run the metric panel's GROUP BY queries (one per requested aggregation),\n * scoped to the WHERE-filtered set with no paging. Optional: without it, the\n * metric panel falls back to applyAggregations over local rows (client mode).\n * Implementations compile each AggSpec with backends/go CompileAggregation,\n * sharing the same WHERE as fetchRows. */\n fetchAggregations?(q: AggregationRequest, signal?: AbortSignal): Promise<AggregationResult>;\n\n /** Re-fetch a single row by id, for in-place updates without a full re-query\n * after a mutation. Optional. */\n fetchRow?(id: RowId, signal?: AbortSignal): Promise<Row | null>;\n\n /** Stats for the field picker. Optional. */\n fetchFieldStats?(fields: string[], signal?: AbortSignal): Promise<Record<string, FieldStats>>;\n}\n\n// ---- Saved queries --------------------------------------------------------\n\nexport interface SavedQuery {\n id: string;\n name: string;\n /** Epoch millis; injected by the caller so core stays deterministic/testable. */\n savedAt: number;\n query: QueryState;\n}\n\n/** Persistence for the \"last\" query (auto-restored) and named saved queries. All\n * methods are namespaced by `key` (the schema/dataset name) so multiple tables\n * on one origin don't collide. Async to allow backend implementations; the\n * browser adapters can still resolve synchronously. */\nexport interface StorageAdapter {\n loadLast(key: string): Promise<QueryState | null>;\n saveLast(key: string, query: QueryState): Promise<void>;\n\n listSaved(key: string): Promise<SavedQuery[]>;\n /** Load the saved query selected as the default view for this key. Optional so\n * existing backend adapters keep working until they add first-class support. */\n loadDefaultSaved?(key: string): Promise<SavedQuery | null>;\n /** Set or clear the saved query used as the default view for this key. */\n setDefaultSaved?(key: string, id: string | null): Promise<void>;\n /** Persist a named query snapshot. Rejects when a saved query with the same\n * name already exists in the key namespace. */\n saveNamed(key: string, name: string, query: QueryState, savedAt: number): Promise<SavedQuery>;\n deleteSaved(key: string, id: string): Promise<void>;\n}\n\nconst LAST_PREFIX = \"query-table:last:\";\nconst SAVED_PREFIX = \"query-table:saved:\";\nconst DEFAULT_PREFIX = \"query-table:default:\";\nconst MAX_SAVED_QUERIES = 100;\nconst MAX_SAVED_NAME_LENGTH = 200;\n\nfunction normalizeSavedQuery(value: unknown): SavedQuery | null {\n if (value == null || typeof value !== \"object\" || Array.isArray(value)) return null;\n const raw = value as Record<string, unknown>;\n if (\n typeof raw.id !== \"string\" ||\n !raw.id ||\n raw.id.length > 256 ||\n typeof raw.name !== \"string\" ||\n !raw.name ||\n raw.name.length > MAX_SAVED_NAME_LENGTH ||\n typeof raw.savedAt !== \"number\" ||\n !Number.isFinite(raw.savedAt) ||\n raw.query == null ||\n typeof raw.query !== \"object\"\n ) {\n return null;\n }\n return { id: raw.id, name: raw.name, savedAt: raw.savedAt, query: normalizeQueryState(raw.query) };\n}\n\nfunction normalizeSavedQueries(value: unknown): SavedQuery[] {\n if (!Array.isArray(value)) return [];\n return value\n .slice(0, MAX_SAVED_QUERIES)\n .map(normalizeSavedQuery)\n .filter((item): item is SavedQuery => item != null);\n}\n\n/** In-memory persistence used by default by the React package. It supports the\n * full saved-query UI for the lifetime of a mounted table without writing\n * filter values to durable browser storage. */\nexport function memoryStorageAdapter(): StorageAdapter {\n const last = new Map<string, QueryState>();\n const saved = new Map<string, SavedQuery[]>();\n const defaults = new Map<string, string>();\n\n return {\n async loadLast(key) {\n const query = last.get(key);\n return query ? normalizeQueryState(query) : null;\n },\n async saveLast(key, query) {\n last.set(key, normalizeQueryState(query));\n },\n async listSaved(key) {\n return (saved.get(key) ?? []).map((item) => ({ ...item, query: normalizeQueryState(item.query) }));\n },\n async loadDefaultSaved(key) {\n const id = defaults.get(key);\n return id ? (saved.get(key) ?? []).find((item) => item.id === id) ?? null : null;\n },\n async setDefaultSaved(key, id) {\n if (id == null) {\n defaults.delete(key);\n return;\n }\n if (!(saved.get(key) ?? []).some((item) => item.id === id)) throw new Error(`Saved query \"${id}\" does not exist.`);\n defaults.set(key, id);\n },\n async saveNamed(key, name, query, savedAt) {\n const normalizedName = name.trim();\n if (!normalizedName || normalizedName.length > MAX_SAVED_NAME_LENGTH) {\n throw new Error(`Saved query name must be between 1 and ${MAX_SAVED_NAME_LENGTH} characters.`);\n }\n const items = saved.get(key) ?? [];\n if (items.length >= MAX_SAVED_QUERIES) throw new Error(`At most ${MAX_SAVED_QUERIES} saved queries are allowed.`);\n if (items.some((item) => item.name === normalizedName)) throw new Error(`Saved query \"${normalizedName}\" already exists.`);\n const item: SavedQuery = {\n id: `${savedAt}-${Math.round((savedAt * 9301 + 49297) % 233280)}`,\n name: normalizedName,\n savedAt,\n query: normalizeQueryState(query),\n };\n saved.set(key, [...items, item]);\n return item;\n },\n async deleteSaved(key, id) {\n saved.set(key, (saved.get(key) ?? []).filter((item) => item.id !== id));\n if (defaults.get(key) === id) defaults.delete(key);\n },\n };\n}\n\n/** Opt-in StorageAdapter over window.localStorage. Query state includes raw\n * filter values and is stored as cleartext JSON; do not use it for sensitive\n * datasets. Falls back to no persistence when storage is unavailable. */\nexport function localStorageAdapter(): StorageAdapter {\n const ls: Storage | null = (() => {\n try {\n return typeof localStorage !== \"undefined\" ? localStorage : null;\n } catch {\n return null; // access can throw in some sandboxes\n }\n })();\n\n const readSaved = (key: string): SavedQuery[] => {\n if (!ls) return [];\n try {\n const raw = ls.getItem(SAVED_PREFIX + key);\n return normalizeSavedQueries(raw ? JSON.parse(raw) : []);\n } catch {\n return [];\n }\n };\n const writeSaved = (key: string, items: SavedQuery[]): void => {\n if (ls) ls.setItem(SAVED_PREFIX + key, JSON.stringify(items));\n };\n const readDefaultId = (key: string): string | null => {\n if (!ls) return null;\n return ls.getItem(DEFAULT_PREFIX + key);\n };\n const writeDefaultId = (key: string, id: string | null): void => {\n if (!ls) return;\n if (id) ls.setItem(DEFAULT_PREFIX + key, id);\n else ls.removeItem(DEFAULT_PREFIX + key);\n };\n\n return {\n async loadLast(key) {\n if (!ls) return null;\n try {\n const raw = ls.getItem(LAST_PREFIX + key);\n return raw ? normalizeQueryState(JSON.parse(raw)) : null;\n } catch {\n return null;\n }\n },\n async saveLast(key, query) {\n if (ls) ls.setItem(LAST_PREFIX + key, JSON.stringify(normalizeQueryState(query)));\n },\n async listSaved(key) {\n return readSaved(key).sort((a, b) => b.savedAt - a.savedAt);\n },\n async loadDefaultSaved(key) {\n const id = readDefaultId(key);\n if (!id) return null;\n const found = readSaved(key).find((q) => q.id === id);\n if (!found) writeDefaultId(key, null);\n return found ?? null;\n },\n async setDefaultSaved(key, id) {\n if (id && !readSaved(key).some((q) => q.id === id)) {\n throw new Error(`Saved query \"${id}\" does not exist.`);\n }\n writeDefaultId(key, id);\n },\n async saveNamed(key, name, query, savedAt) {\n const normalizedName = name.trim();\n if (!normalizedName || normalizedName.length > MAX_SAVED_NAME_LENGTH) {\n throw new Error(`Saved query name must be between 1 and ${MAX_SAVED_NAME_LENGTH} characters.`);\n }\n const items = readSaved(key);\n if (items.length >= MAX_SAVED_QUERIES) throw new Error(`At most ${MAX_SAVED_QUERIES} saved queries are allowed.`);\n if (items.some((q) => q.name === normalizedName)) {\n throw new Error(`Saved query \"${normalizedName}\" already exists.`);\n }\n const item: SavedQuery = {\n id: `${savedAt}-${Math.round((savedAt * 9301 + 49297) % 233280)}`,\n name: normalizedName,\n savedAt,\n query: normalizeQueryState(query),\n };\n items.push(item);\n writeSaved(key, items);\n return item;\n },\n async deleteSaved(key, id) {\n writeSaved(\n key,\n readSaved(key).filter((q) => q.id !== id),\n );\n if (readDefaultId(key) === id) writeDefaultId(key, null);\n },\n };\n}\n","import type { FieldType } from \"./schema\";\n\nexport type FormulaType = Exclude<FieldType, \"enum\"> | \"null\";\nexport type FormulaValue = string | number | boolean | string[] | null;\nexport interface FormulaDiagnostic {\n message: string;\n from: number;\n to: number;\n}\nexport type FormulaNode =\n | { kind: \"literal\"; value: FormulaValue; from: number; to: number }\n | {\n kind: \"field\";\n name: string;\n valueType?: FormulaType;\n from: number;\n to: number;\n }\n | {\n kind: \"call\";\n name: string;\n args: FormulaNode[];\n from: number;\n to: number;\n };\nexport interface FormulaPlan {\n ast: FormulaNode;\n dependencies: string[];\n type: FormulaType;\n}\nexport interface RegexInspection {\n input: string;\n start: number;\n end: number;\n groups: (string | null)[];\n}\nexport interface FormulaResult {\n value: FormulaValue;\n error?: string;\n regex?: RegexInspection;\n}\nexport interface FormulaFunction {\n name: string;\n signature: string;\n description: string;\n min: number;\n max: number;\n result: FormulaType | \"branch\";\n args: FormulaType[] | \"any\";\n}\n\nconst f = (\n name: string,\n signature: string,\n description: string,\n min: number,\n max: number,\n result: FormulaFunction[\"result\"],\n args: FormulaFunction[\"args\"],\n): FormulaFunction => ({\n name,\n signature,\n description,\n min,\n max,\n result,\n args,\n});\nexport const FORMULA_FUNCTIONS: FormulaFunction[] = [\n ...[\"LEFT\", \"RIGHT\"].map((n) =>\n f(\n n,\n `${n}(text, count)`,\n \"Take Unicode code points from the end indicated.\",\n 2,\n 2,\n \"text\",\n [\"text\", \"number\"],\n ),\n ),\n f(\n \"SUBSTRING\",\n \"SUBSTRING(text, start, length)\",\n \"One-based start; length is optional.\",\n 2,\n 3,\n \"text\",\n [\"text\", \"number\", \"number\"],\n ),\n f(\"LENGTH\", \"LENGTH(text)\", \"Count Unicode code points.\", 1, 1, \"number\", [\n \"text\",\n ]),\n ...[\"LOWER\", \"UPPER\", \"TRIM\", \"LTRIM\", \"RTRIM\"].map((n) =>\n f(\n n,\n `${n}(text)`,\n \"Change case or remove surrounding whitespace.\",\n 1,\n 1,\n \"text\",\n [\"text\"],\n ),\n ),\n f(\n \"REPLACE\",\n \"REPLACE(text, search, replacement)\",\n \"Replace all literal occurrences.\",\n 3,\n 3,\n \"text\",\n [\"text\", \"text\", \"text\"],\n ),\n ...[\"LPAD\", \"RPAD\"].map((n) =>\n f(\n n,\n `${n}(text, length, padding)`,\n \"Pad to a code-point length; default padding is a space.\",\n 2,\n 3,\n \"text\",\n [\"text\", \"number\", \"text\"],\n ),\n ),\n f(\n \"SPLIT_PART\",\n \"SPLIT_PART(text, separator, index)\",\n \"One-based part; missing part returns null.\",\n 3,\n 3,\n \"text\",\n [\"text\", \"text\", \"number\"],\n ),\n f(\n \"CONCAT\",\n \"CONCAT(text, ...)\",\n \"Combine text; null propagates.\",\n 1,\n 50,\n \"text\",\n [\"text\"],\n ),\n f(\n \"CONCAT_WS\",\n \"CONCAT_WS(separator, text, ...)\",\n \"Combine text, skipping null arguments.\",\n 2,\n 50,\n \"text\",\n [\"text\"],\n ),\n ...[\"CONTAINS\", \"STARTS_WITH\", \"ENDS_WITH\"].map((n) =>\n f(\n n,\n `${n}(text, search, ignoreCase)`,\n \"Case-sensitive unless the optional third argument is true.\",\n 2,\n 3,\n \"bool\",\n [\"text\", \"text\", \"bool\"],\n ),\n ),\n f(\n \"REGEX_TEST\",\n \"REGEX_TEST(text, pattern, flags)\",\n \"Test a JavaScript Unicode regex; flags: i, m, s.\",\n 2,\n 3,\n \"bool\",\n [\"text\", \"text\", \"text\"],\n ),\n f(\n \"REGEX_EXTRACT\",\n \"REGEX_EXTRACT(text, pattern, group, flags)\",\n \"Extract a capture (default 0: whole match); no match returns null.\",\n 2,\n 4,\n \"text\",\n [\"text\", \"text\", \"number\", \"text\"],\n ),\n f(\n \"REGEX_REPLACE\",\n \"REGEX_REPLACE(text, pattern, replacement, flags)\",\n \"Replace all matches; supports $1 capture references.\",\n 3,\n 4,\n \"text\",\n [\"text\", \"text\", \"text\", \"text\"],\n ),\n ...[\"ABS\", \"FLOOR\", \"CEIL\", \"TRUNC\", \"SQRT\"].map((n) =>\n f(n, `${n}(number)`, \"Numeric transformation.\", 1, 1, \"number\", [\"number\"]),\n ),\n f(\n \"ROUND\",\n \"ROUND(number, digits)\",\n \"Round to decimal places (default 0, range -15 to 15).\",\n 1,\n 2,\n \"number\",\n [\"number\"],\n ),\n f(\n \"POWER\",\n \"POWER(base, exponent)\",\n \"Raise a number to a power.\",\n 2,\n 2,\n \"number\",\n [\"number\"],\n ),\n f(\n \"CLAMP\",\n \"CLAMP(number, lower, upper)\",\n \"Constrain a number to an inclusive range.\",\n 3,\n 3,\n \"number\",\n [\"number\"],\n ),\n ...[\"LEAST\", \"GREATEST\"].map((n) =>\n f(\n n,\n `${n}(number, ...)`,\n \"Compare numbers within this row.\",\n 1,\n 50,\n \"number\",\n [\"number\"],\n ),\n ),\n f(\n \"IF\",\n \"IF(condition, then, otherwise)\",\n \"Evaluate only the chosen branch; null condition uses otherwise.\",\n 3,\n 3,\n \"branch\",\n \"any\",\n ),\n f(\n \"IFS\",\n \"IFS(condition, value, ..., fallback)\",\n \"First true condition wins; final fallback is required.\",\n 3,\n 49,\n \"branch\",\n \"any\",\n ),\n f(\n \"SWITCH\",\n \"SWITCH(value, match, result, ..., fallback)\",\n \"Match a value to a result; final fallback is required.\",\n 4,\n 50,\n \"branch\",\n \"any\",\n ),\n f(\n \"COALESCE\",\n \"COALESCE(value, ...)\",\n \"First non-null value, evaluated lazily.\",\n 1,\n 50,\n \"branch\",\n \"any\",\n ),\n f(\n \"NULLIF\",\n \"NULLIF(value, other)\",\n \"Return null if the values are equal.\",\n 2,\n 2,\n \"branch\",\n \"any\",\n ),\n ...[\"IS_NULL\", \"IS_EMPTY\"].map((n) =>\n f(\n n,\n `${n}(value)`,\n \"Test null, or null/empty text/empty array.\",\n 1,\n 1,\n \"bool\",\n \"any\",\n ),\n ),\n f(\n \"IFERROR\",\n \"IFERROR(value, fallback)\",\n \"Use fallback only when evaluating value fails.\",\n 2,\n 2,\n \"branch\",\n \"any\",\n ),\n ...(\n [\n [\"TO_TEXT\", \"text\"],\n [\"TO_NUMBER\", \"number\"],\n [\"TO_BOOLEAN\", \"bool\"],\n [\"TO_DATETIME\", \"datetime\"],\n ] as const\n ).map(([n, t]) =>\n f(\n n,\n `${n}(value)`,\n \"Explicit conversion; invalid input produces a cell error.\",\n 1,\n 1,\n t,\n \"any\",\n ),\n ),\n f(\n \"SPLIT\",\n \"SPLIT(text, separator)\",\n \"Split text into an array.\",\n 2,\n 2,\n \"textarray\",\n [\"text\", \"text\"],\n ),\n f(\"JOIN\", \"JOIN(array, separator)\", \"Join an array of text.\", 2, 2, \"text\", [\n \"textarray\",\n \"text\",\n ]),\n f(\n \"ARRAY_LENGTH\",\n \"ARRAY_LENGTH(array)\",\n \"Count array elements.\",\n 1,\n 1,\n \"number\",\n [\"textarray\"],\n ),\n f(\n \"ARRAY_CONTAINS\",\n \"ARRAY_CONTAINS(array, text)\",\n \"Case-sensitive array membership.\",\n 2,\n 2,\n \"bool\",\n [\"textarray\", \"text\"],\n ),\n f(\n \"ARRAY_GET\",\n \"ARRAY_GET(array, index)\",\n \"One-based element; out of range returns null.\",\n 2,\n 2,\n \"text\",\n [\"textarray\", \"number\"],\n ),\n ...[\"ARRAY_UNIQUE\", \"ARRAY_SORT\"].map((n) =>\n f(\n n,\n `${n}(array)`,\n \"Deduplicate or sort text elements by code-point order.\",\n 1,\n 1,\n \"textarray\",\n [\"textarray\"],\n ),\n ),\n ...[\"YEAR\", \"MONTH\", \"DAY\", \"HOUR\", \"WEEKDAY\"].map((n) =>\n f(\n n,\n `${n}(datetime)`,\n \"UTC date component; weekday is Monday=1 through Sunday=7.\",\n 1,\n 1,\n \"number\",\n [\"datetime\"],\n ),\n ),\n f(\n \"DATE_TRUNC\",\n \"DATE_TRUNC(unit, datetime)\",\n \"UTC truncation: year, month, week (Monday), day, hour, minute, second.\",\n 2,\n 2,\n \"datetime\",\n [\"text\", \"datetime\"],\n ),\n f(\n \"DATE_ADD\",\n \"DATE_ADD(unit, amount, datetime)\",\n \"UTC calendar addition; month/year clamp to the last day.\",\n 3,\n 3,\n \"datetime\",\n [\"text\", \"number\", \"datetime\"],\n ),\n f(\n \"DATE_DIFF\",\n \"DATE_DIFF(unit, start, end)\",\n \"Elapsed whole units: week, day, hour, minute, second, millisecond.\",\n 3,\n 3,\n \"number\",\n [\"text\", \"datetime\", \"datetime\"],\n ),\n f(\n \"FORMAT_DATE\",\n \"FORMAT_DATE(datetime, pattern)\",\n \"UTC tokens: YYYY MM DD HH mm ss; other text is literal.\",\n 2,\n 2,\n \"text\",\n [\"datetime\", \"text\"],\n ),\n];\n\nexport class FormulaError extends Error {\n constructor(\n message: string,\n public from = 0,\n public to = from + 1,\n ) {\n super(message);\n }\n}\ninterface Token {\n text: string;\n kind: \"field\" | \"string\" | \"number\" | \"word\" | \"op\" | \"end\";\n from: number;\n to: number;\n}\nexport function compileFormula(\n source: string,\n fields: readonly { name: string; type: FieldType }[],\n resolve?: (name: string) => FormulaPlan | undefined,\n): FormulaPlan {\n if (!source.trim() || source.length > 10000)\n throw new FormulaError(\"Formula must contain 1–10,000 characters.\");\n const tokens: Token[] = [];\n let pos = 0;\n while (pos < source.length) {\n if (/\\s/.test(source[pos]!)) {\n pos++;\n continue;\n }\n const from = pos;\n let match: RegExpMatchArray | null;\n if (source[pos] === \"[\") {\n pos++;\n let text = \"\";\n let closed = false;\n while (pos < source.length) {\n if (source[pos] === \"]\") {\n if (source[pos + 1] === \"]\") {\n text += \"]\";\n pos += 2;\n } else {\n pos++;\n closed = true;\n break;\n }\n } else text += source[pos++];\n }\n if (!closed)\n throw new FormulaError(\"Unclosed field reference.\", from, pos);\n tokens.push({ kind: \"field\", text, from, to: pos });\n } else if (source[pos] === '\"') {\n pos++;\n while (pos < source.length && source[pos] !== '\"') {\n if (source[pos] === \"\\\\\") pos++;\n pos++;\n }\n pos++;\n try {\n tokens.push({\n kind: \"string\",\n text: JSON.parse(source.slice(from, pos)),\n from,\n to: pos,\n });\n } catch {\n throw new FormulaError(\"Invalid JSON string literal.\", from, pos);\n }\n } else if (\n (match = source\n .slice(pos)\n .match(/^(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?/))\n ) {\n pos += match[0].length;\n tokens.push({ kind: \"number\", text: match[0], from, to: pos });\n } else if ((match = source.slice(pos).match(/^[a-zA-Z_][a-zA-Z_0-9]*/))) {\n pos += match[0].length;\n tokens.push({\n kind: \"word\",\n text: match[0].toUpperCase(),\n from,\n to: pos,\n });\n } else if (\n (match = source.slice(pos).match(/^(>=|<=|!=|<>|[=<>+*/%(),-])/))\n ) {\n pos += match[0].length;\n tokens.push({ kind: \"op\", text: match[0], from, to: pos });\n } else throw new FormulaError(`Unexpected character ${source[pos]}.`, from);\n if (tokens.length > 2000)\n throw new FormulaError(\"Formula is too complex.\", from);\n }\n tokens.push({ kind: \"end\", text: \"\", from: pos, to: pos });\n let i = 0,\n depth = 0;\n const peek = () => tokens[i]!;\n const take = () => tokens[i++]!;\n const expect = (s: string) => {\n if (peek().text !== s)\n throw new FormulaError(`Expected ${s}.`, peek().from, peek().to);\n return take();\n };\n const call = (\n name: string,\n args: FormulaNode[],\n from: number,\n to: number,\n ): FormulaNode => ({ kind: \"call\", name, args, from, to });\n const priority: Record<string, number> = {\n OR: 1,\n AND: 2,\n \"=\": 3,\n \"!=\": 3,\n \"<>\": 3,\n \"<\": 3,\n \">\": 3,\n \"<=\": 3,\n \">=\": 3,\n IN: 3,\n BETWEEN: 3,\n \"+\": 4,\n \"-\": 4,\n \"*\": 5,\n \"/\": 5,\n \"%\": 5,\n };\n function expression(min = 0): FormulaNode {\n if (++depth > 50)\n throw new FormulaError(\"Maximum nesting depth is 50.\", peek().from);\n const t = take();\n let node: FormulaNode;\n if (t.kind === \"number\") {\n if (!Number.isFinite(Number(t.text)))\n throw new FormulaError(\"Number must be finite.\", t.from, t.to);\n node = { kind: \"literal\", value: Number(t.text), from: t.from, to: t.to };\n } else if (t.kind === \"string\")\n node = { kind: \"literal\", value: t.text, from: t.from, to: t.to };\n else if (t.kind === \"field\")\n node = { kind: \"field\", name: t.text, from: t.from, to: t.to };\n else if ([\"TRUE\", \"FALSE\", \"NULL\"].includes(t.text))\n node = {\n kind: \"literal\",\n value: t.text === \"NULL\" ? null : t.text === \"TRUE\",\n from: t.from,\n to: t.to,\n };\n else if (t.text === \"(\") {\n node = expression();\n expect(\")\");\n } else if ([\"NOT\", \"-\", \"+\"].includes(t.text)) {\n const arg = expression(t.text === \"NOT\" ? 3 : 6);\n node = call(`unary:${t.text}`, [arg], t.from, arg.to);\n } else if (t.kind === \"word\") {\n expect(\"(\");\n const args: FormulaNode[] = [];\n if (peek().text !== \")\") {\n do {\n args.push(expression());\n if (peek().text !== \",\") break;\n take();\n } while (true);\n }\n node = call(t.text, args, t.from, expect(\")\").to);\n } else\n throw new FormulaError(\n \"Expected a value, field, or function.\",\n t.from,\n t.to,\n );\n while (peek().kind !== \"end\" && (priority[peek().text] ?? -1) >= min) {\n const op = take();\n const p = priority[op.text]!;\n if (op.text === \"IN\") {\n expect(\"(\");\n const args = [node];\n do {\n args.push(expression());\n if (peek().text !== \",\") break;\n take();\n } while (true);\n node = call(\"op:IN\", args, node.from, expect(\")\").to);\n } else if (op.text === \"BETWEEN\") {\n const lo = expression(p + 1);\n expect(\"AND\");\n const hi = expression(p + 1);\n node = call(\"op:BETWEEN\", [node, lo, hi], node.from, hi.to);\n } else {\n const right = expression(p + 1);\n node = call(`op:${op.text}`, [node, right], node.from, right.to);\n }\n }\n depth--;\n return node;\n }\n const ast = expression();\n if (peek().kind !== \"end\")\n throw new FormulaError(\"Unexpected token.\", peek().from, peek().to);\n const deps = new Set<string>();\n const byName = new Map(fields.map((v) => [v.name, v.type]));\n const same = (ts: FormulaType[], n: FormulaNode): FormulaType => {\n const real = [...new Set(ts.filter((t) => t !== \"null\"))];\n if (real.length > 1)\n throw new FormulaError(\n \"Values must have compatible types; use an explicit conversion.\",\n n.from,\n n.to,\n );\n return real[0] ?? \"null\";\n };\n function check(n: FormulaNode, level = 0): FormulaType {\n if (level > 50)\n throw new FormulaError(\"Maximum nesting depth is 50.\", n.from, n.to);\n if (n.kind === \"literal\")\n return n.value === null\n ? \"null\"\n : typeof n.value === \"boolean\"\n ? \"bool\"\n : typeof n.value === \"number\"\n ? \"number\"\n : \"text\";\n if (n.kind === \"field\") {\n const t = byName.get(n.name);\n if (!t) {\n const nested = resolve?.(n.name);\n if (nested) {\n Object.assign(n, nested.ast);\n nested.dependencies.forEach((d) => deps.add(d));\n return nested.type;\n }\n throw new FormulaError(\n `Unknown or unavailable field: ${n.name}`,\n n.from,\n n.to,\n );\n }\n deps.add(n.name);\n n.valueType = t === \"enum\" ? \"text\" : t;\n return n.valueType;\n }\n const ts = n.args.map((a) => check(a, level + 1));\n const need = (index: number, t: FormulaType) => {\n if (ts[index] !== undefined && ts[index] !== \"null\" && ts[index] !== t)\n throw new FormulaError(\n `Expected ${t}, received ${ts[index]}.`,\n n.args[index]!.from,\n n.args[index]!.to,\n );\n };\n if (n.name.startsWith(\"op:\") || n.name.startsWith(\"unary:\")) {\n const op = n.name.split(\":\")[1]!;\n if ([\"AND\", \"OR\", \"NOT\"].includes(op)) {\n ts.forEach((_, j) => need(j, \"bool\"));\n return \"bool\";\n }\n if ([\"+\", \"-\", \"*\", \"/\", \"%\"].includes(op)) {\n ts.forEach((_, j) => need(j, \"number\"));\n return \"number\";\n }\n same(ts, n);\n return \"bool\";\n }\n const spec = FORMULA_FUNCTIONS.find((v) => v.name === n.name);\n if (!spec)\n throw new FormulaError(`Unknown function: ${n.name}`, n.from, n.to);\n if (ts.length < spec.min || ts.length > spec.max)\n throw new FormulaError(`Use ${spec.signature}.`, n.from, n.to);\n if (spec.args !== \"any\") {\n const args = spec.args;\n ts.forEach((_, j) => need(j, args[Math.min(j, args.length - 1)]!));\n }\n if (n.name === \"IF\") {\n need(0, \"bool\");\n return same(ts.slice(1), n);\n }\n if (n.name === \"IFS\") {\n if (ts.length % 2 !== 1)\n throw new FormulaError(\n \"IFS requires condition/value pairs and a fallback.\",\n n.from,\n n.to,\n );\n const branches: FormulaType[] = [];\n ts.forEach((t, j) => {\n if (j === ts.length - 1 || j % 2 === 1) branches.push(t);\n else need(j, \"bool\");\n });\n return same(branches, n);\n }\n if (n.name === \"SWITCH\") {\n if (ts.length % 2 !== 0)\n throw new FormulaError(\n \"SWITCH requires match/result pairs and a fallback.\",\n n.from,\n n.to,\n );\n const branches: FormulaType[] = [];\n for (let j = 1; j < ts.length - 1; j += 2) {\n same([ts[0]!, ts[j]!], n);\n branches.push(ts[j + 1]!);\n }\n branches.push(ts[ts.length - 1]!);\n return same(branches, n);\n }\n if (n.name.startsWith(\"REGEX_\")) {\n const pattern = n.args[1];\n const flags = n.args[n.name === \"REGEX_TEST\" ? 2 : 3];\n if (pattern?.kind === \"literal\" && typeof pattern.value === \"string\") {\n if (pattern.value.length > 2000)\n throw new FormulaError(\n \"Regex pattern exceeds 2,000 characters.\",\n pattern.from,\n pattern.to,\n );\n if (\n !flags ||\n (flags.kind === \"literal\" && typeof flags.value === \"string\")\n ) {\n const value = flags?.value ?? \"\";\n if (!/^(?!.*(.).*\\1)[ims]*$/.test(String(value)))\n throw new FormulaError(\n \"Regex flags must be unique i, m, or s.\",\n n.from,\n n.to,\n );\n try {\n new RegExp(pattern.value, `${value}u`);\n } catch {\n throw new FormulaError(\n \"Invalid regex pattern.\",\n pattern.from,\n pattern.to,\n );\n }\n }\n }\n }\n if (spec.result === \"branch\") return same(ts, n);\n return spec.result;\n }\n const type = check(ast);\n return { ast, dependencies: [...deps].sort(), type };\n}\n\n/** Self-contained runtime: serialized into the browser worker, never user code. */\nexport function formulaRuntime(\n ast: FormulaNode,\n inputs: Record<string, FormulaValue>,\n): FormulaResult {\n const fail = (s: string): never => {\n throw new Error(s);\n };\n const num = (v: FormulaValue): number =>\n typeof v === \"number\" && Number.isFinite(v)\n ? v\n : fail(\"Expected a finite number.\");\n const str = (v: FormulaValue): string =>\n typeof v === \"string\" ? v : fail(\"Expected text.\");\n const arr = (v: FormulaValue): string[] =>\n Array.isArray(v) && v.every((x) => typeof x === \"string\")\n ? v\n : fail(\"Expected a text array.\");\n const integer = (v: FormulaValue, min = 0, max = 100000): number => {\n const n = num(v);\n return Number.isInteger(n) && n >= min && n <= max\n ? n\n : fail(`Expected an integer between ${min} and ${max}.`);\n };\n const equal = (a: FormulaValue, b: FormulaValue) =>\n JSON.stringify(a) === JSON.stringify(b);\n const date = (v: FormulaValue): Date => {\n const s = str(v);\n if (\n !/^\\d{4}-\\d{2}-\\d{2}(?:T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,3})?(?:Z|[+-]\\d{2}:\\d{2}))?$/.test(\n s,\n )\n )\n fail(\"Expected an ISO date or timestamp with timezone.\");\n const d = new Date(s);\n const day = Number(s.slice(8, 10)),\n month = Number(s.slice(5, 7));\n if (\n !Number.isFinite(d.getTime()) ||\n month < 1 ||\n month > 12 ||\n day < 1 ||\n day > new Date(Date.UTC(Number(s.slice(0, 4)), month, 0)).getUTCDate()\n )\n fail(\"Invalid date.\");\n return d;\n };\n const bound = (v: FormulaValue): FormulaValue => {\n if (\n v !== null &&\n typeof v !== \"number\" &&\n typeof v !== \"string\" &&\n typeof v !== \"boolean\" &&\n !Array.isArray(v)\n )\n fail(\"Unsupported input value type.\");\n if (Array.isArray(v) && !v.every((x) => typeof x === \"string\"))\n fail(\"Expected a text array.\");\n if (typeof v === \"number\" && !Number.isFinite(v))\n fail(\"Result is not finite.\");\n if (typeof v === \"string\" && v.length > 100000)\n fail(\"Text result exceeds 100,000 characters.\");\n if (\n Array.isArray(v) &&\n (v.length > 10000 || v.reduce((s, x) => s + x.length, 0) > 100000)\n )\n fail(\"Array result is too large.\");\n return v;\n };\n let inspection: RegexInspection | undefined;\n let budget = 5000;\n function run(n: FormulaNode): FormulaValue {\n if (--budget < 0) fail(\"Formula operation limit exceeded.\");\n if (n.kind === \"literal\") return n.value;\n if (n.kind === \"field\") {\n if (!Object.prototype.hasOwnProperty.call(inputs, n.name)) return null;\n const v = bound(inputs[n.name] ?? null);\n if (v === null) return null;\n if (n.valueType === \"number\") return num(v);\n if (n.valueType === \"bool\" && typeof v !== \"boolean\")\n fail(\"Expected a boolean.\");\n if (n.valueType === \"datetime\") return date(v).toISOString();\n if (n.valueType === \"text\") return str(v);\n if (n.valueType === \"textarray\") return arr(v);\n return v;\n }\n const name = n.name;\n const args = n.args;\n if (name === \"IF\") return run(run(args[0]!) === true ? args[1]! : args[2]!);\n if (name === \"IFS\") {\n for (let j = 0; j < args.length - 1; j += 2)\n if (run(args[j]!) === true) return run(args[j + 1]!);\n return run(args[args.length - 1]!);\n }\n if (name === \"SWITCH\") {\n const v = run(args[0]!);\n for (let j = 1; j < args.length - 1; j += 2)\n if (equal(v, run(args[j]!))) return run(args[j + 1]!);\n return run(args[args.length - 1]!);\n }\n if (name === \"COALESCE\") {\n for (const arg of args) {\n const v = run(arg);\n if (v !== null) return v;\n }\n return null;\n }\n if (name === \"IFERROR\") {\n try {\n return run(args[0]!);\n } catch {\n return run(args[1]!);\n }\n }\n if (name === \"op:AND\" || name === \"op:OR\") {\n const a = run(args[0]!);\n if (name === \"op:AND\" && a === false) return false;\n if (name === \"op:OR\" && a === true) return true;\n const b = run(args[1]!);\n return name === \"op:AND\"\n ? b === false\n ? false\n : a === null || b === null\n ? null\n : true\n : b === true\n ? true\n : a === null || b === null\n ? null\n : false;\n }\n const vs = args.map(run);\n const a = vs[0] ?? null,\n b = vs[1] ?? null,\n c = vs[2] ?? null;\n if (name === \"IS_NULL\") return a === null;\n if (name === \"IS_EMPTY\")\n return a === null || a === \"\" || (Array.isArray(a) && a.length === 0);\n if (name === \"NULLIF\") return equal(a, b) ? null : a;\n if (name === \"CONCAT_WS\")\n return a === null\n ? null\n : bound(\n vs\n .slice(1)\n .filter((v) => v !== null)\n .map(str)\n .join(str(a)),\n );\n if (name === \"op:IN\") {\n if (a === null) return null;\n if (vs.slice(1).some((v) => v !== null && equal(a, v))) return true;\n return vs.slice(1).includes(null) ? null : false;\n }\n if (vs.some((v) => v === null)) return null;\n let out: FormulaValue;\n switch (name) {\n case \"unary:NOT\":\n out = !a;\n break;\n case \"unary:-\":\n out = -num(a);\n break;\n case \"unary:+\":\n out = num(a);\n break;\n case \"op:+\":\n out = num(a) + num(b);\n break;\n case \"op:-\":\n out = num(a) - num(b);\n break;\n case \"op:*\":\n out = num(a) * num(b);\n break;\n case \"op:/\":\n case \"op:%\":\n if (num(b) === 0) fail(\"Division by zero.\");\n out = name === \"op:/\" ? num(a) / num(b) : num(a) % num(b);\n break;\n case \"op:=\":\n out = equal(a, b);\n break;\n case \"op:!=\":\n case \"op:<>\":\n out = !equal(a, b);\n break;\n case \"op:<\":\n out = a! < b!;\n break;\n case \"op:>\":\n out = a! > b!;\n break;\n case \"op:<=\":\n out = a! <= b!;\n break;\n case \"op:>=\":\n out = a! >= b!;\n break;\n case \"op:BETWEEN\":\n out = a! >= b! && a! <= c!;\n break;\n case \"LEFT\":\n out = Array.from(str(a)).slice(0, integer(b)).join(\"\");\n break;\n case \"RIGHT\": {\n const n = integer(b);\n out = n ? Array.from(str(a)).slice(-n).join(\"\") : \"\";\n break;\n }\n case \"SUBSTRING\": {\n const start = integer(b, 1) - 1;\n out = Array.from(str(a))\n .slice(start, vs.length > 2 ? start + integer(c) : undefined)\n .join(\"\");\n break;\n }\n case \"LENGTH\":\n out = Array.from(str(a)).length;\n break;\n case \"LOWER\":\n out = str(a).toLowerCase();\n break;\n case \"UPPER\":\n out = str(a).toUpperCase();\n break;\n case \"TRIM\":\n out = str(a).trim();\n break;\n case \"LTRIM\":\n out = str(a).trimStart();\n break;\n case \"RTRIM\":\n out = str(a).trimEnd();\n break;\n case \"REPLACE\":\n if (b === \"\") fail(\"Search text must not be empty.\");\n const parts = str(a).split(str(b));\n if (parts.length * str(c).length + str(a).length > 200000)\n fail(\"Replacement result is too large.\");\n out = parts.join(str(c));\n break;\n case \"LPAD\":\n case \"RPAD\": {\n const chars = Array.from(str(a)),\n len = integer(b),\n pad = Array.from(vs.length > 2 ? str(c) : \" \");\n if (!pad.length) fail(\"Padding must not be empty.\");\n const fill = Array.from(\n { length: Math.max(0, len - chars.length) },\n (_, i) => pad[i % pad.length]!,\n ).join(\"\");\n out =\n (name === \"LPAD\" ? fill : \"\") +\n chars.slice(0, len).join(\"\") +\n (name === \"RPAD\" ? fill : \"\");\n break;\n }\n case \"SPLIT_PART\":\n out = str(a).split(str(b))[integer(c, 1) - 1] ?? null;\n break;\n case \"CONCAT\":\n out = vs.map(str).join(\"\");\n break;\n case \"CONTAINS\":\n case \"STARTS_WITH\":\n case \"ENDS_WITH\": {\n const s = c === true ? str(a).toLowerCase() : str(a),\n t = c === true ? str(b).toLowerCase() : str(b);\n out =\n name === \"CONTAINS\"\n ? s.includes(t)\n : name === \"STARTS_WITH\"\n ? s.startsWith(t)\n : s.endsWith(t);\n break;\n }\n case \"REGEX_TEST\":\n case \"REGEX_EXTRACT\":\n case \"REGEX_REPLACE\": {\n const flags = vs[name === \"REGEX_TEST\" ? 2 : 3] ?? \"\";\n if (!/^(?!.*(.).*\\1)[ims]*$/.test(str(flags)))\n fail(\"Regex flags must be unique i, m, or s.\");\n if (str(b).length > 2000)\n fail(\"Regex pattern exceeds 2,000 characters.\");\n const re = new RegExp(\n str(b),\n `${flags}u${name === \"REGEX_REPLACE\" ? \"g\" : \"\"}`,\n );\n if (name === \"REGEX_TEST\") out = re.test(str(a));\n else if (name === \"REGEX_REPLACE\") {\n let produced = 0;\n out = str(a).replace(re, (...captures: unknown[]) => {\n const replacement = str(c).replace(\n /\\$(\\$|[0-9]{1,2}|&|`|')/g,\n (_token, key: string) => {\n if (key === \"$\") return \"$\";\n if (key === \"&\") return String(captures[0]);\n if (key === \"`\" || key === \"'\")\n return fail(\n \"Regex replacement supports $$, $&, and numbered captures only.\",\n );\n const extra =\n typeof captures[captures.length - 1] === \"object\" ? 3 : 2;\n const i = Number(key);\n return i > 0 && i < captures.length - extra\n ? String(captures[i] ?? \"\")\n : fail(\"Invalid replacement capture.\");\n },\n );\n produced += replacement.length;\n if (produced > 100000) fail(\"Replacement result is too large.\");\n return replacement;\n });\n } else {\n const match = re.exec(str(a));\n const group = vs.length > 2 ? integer(c, 0, 100) : 0;\n out = match?.[group] ?? null;\n if (match)\n inspection = {\n input: str(a),\n start: match.index,\n end: match.index + match[0].length,\n groups: Array.from(match, (v) => v ?? null),\n };\n }\n break;\n }\n case \"ABS\":\n out = Math.abs(num(a));\n break;\n case \"FLOOR\":\n out = Math.floor(num(a));\n break;\n case \"CEIL\":\n out = Math.ceil(num(a));\n break;\n case \"TRUNC\":\n out = Math.trunc(num(a));\n break;\n case \"SQRT\":\n out = Math.sqrt(num(a));\n break;\n case \"ROUND\": {\n const scale = 10 ** (vs.length > 1 ? integer(b, -15, 15) : 0);\n out = Math.round(num(a) * scale) / scale;\n break;\n }\n case \"POWER\":\n out = Math.pow(num(a), num(b));\n break;\n case \"CLAMP\":\n if (num(b) > num(c)) fail(\"Lower bound exceeds upper bound.\");\n out = Math.max(num(b), Math.min(num(c), num(a)));\n break;\n case \"LEAST\":\n out = Math.min(...vs.map(num));\n break;\n case \"GREATEST\":\n out = Math.max(...vs.map(num));\n break;\n case \"TO_TEXT\":\n out = Array.isArray(a) ? JSON.stringify(a) : String(a);\n break;\n case \"TO_NUMBER\":\n if (\n typeof a !== \"number\" &&\n (typeof a !== \"string\" ||\n !/^[-+]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?$/.test(a.trim()))\n )\n fail(\"Cannot convert to number.\");\n out = Number(a);\n break;\n case \"TO_BOOLEAN\":\n if (a === true || a === false) out = a;\n else if (a === \"true\" || a === 1) out = true;\n else if (a === \"false\" || a === 0) out = false;\n else return fail(\"Cannot convert to boolean.\");\n break;\n case \"TO_DATETIME\":\n out = date(a).toISOString();\n break;\n case \"SPLIT\":\n out = str(a).split(str(b));\n break;\n case \"JOIN\":\n out = arr(a).join(str(b));\n break;\n case \"ARRAY_LENGTH\":\n out = arr(a).length;\n break;\n case \"ARRAY_CONTAINS\":\n out = arr(a).includes(str(b));\n break;\n case \"ARRAY_GET\":\n out = arr(a)[integer(b, 1) - 1] ?? null;\n break;\n case \"ARRAY_UNIQUE\":\n out = [...new Set(arr(a))];\n break;\n case \"ARRAY_SORT\":\n out = [...arr(a)].sort((left, right) => {\n const l = Array.from(left),\n r = Array.from(right);\n for (let i = 0; i < Math.min(l.length, r.length); i++) {\n const delta = l[i]!.codePointAt(0)! - r[i]!.codePointAt(0)!;\n if (delta) return delta;\n }\n return l.length - r.length;\n });\n break;\n case \"YEAR\":\n out = date(a).getUTCFullYear();\n break;\n case \"MONTH\":\n out = date(a).getUTCMonth() + 1;\n break;\n case \"DAY\":\n out = date(a).getUTCDate();\n break;\n case \"HOUR\":\n out = date(a).getUTCHours();\n break;\n case \"WEEKDAY\":\n out = ((date(a).getUTCDay() + 6) % 7) + 1;\n break;\n case \"DATE_TRUNC\": {\n const d = date(b),\n unit = str(a).toLowerCase();\n if (\n ![\n \"year\",\n \"month\",\n \"week\",\n \"day\",\n \"hour\",\n \"minute\",\n \"second\",\n ].includes(unit)\n )\n fail(\"Unsupported date unit.\");\n d.setUTCMilliseconds(0);\n if (unit !== \"second\") d.setUTCSeconds(0);\n if (![\"second\", \"minute\"].includes(unit)) d.setUTCMinutes(0);\n if ([\"year\", \"month\", \"week\", \"day\"].includes(unit)) d.setUTCHours(0);\n if (unit === \"week\")\n d.setUTCDate(d.getUTCDate() - ((d.getUTCDay() + 6) % 7));\n if (unit === \"year\" || unit === \"month\") d.setUTCDate(1);\n if (unit === \"year\") d.setUTCMonth(0);\n out = d.toISOString();\n break;\n }\n case \"DATE_ADD\": {\n const d = date(c),\n unit = str(a).toLowerCase(),\n amount = integer(b, -100000, 100000);\n if (unit === \"year\" || unit === \"month\") {\n const day = d.getUTCDate();\n d.setUTCDate(1);\n d.setUTCMonth(d.getUTCMonth() + amount * (unit === \"year\" ? 12 : 1));\n const last = new Date(d.getTime());\n last.setUTCMonth(last.getUTCMonth() + 1, 0);\n d.setUTCDate(Math.min(day, last.getUTCDate()));\n } else {\n const units: Record<string, number> = {\n week: 604800000,\n day: 86400000,\n hour: 3600000,\n minute: 60000,\n second: 1000,\n millisecond: 1,\n };\n const ms = units[unit];\n if (!ms) fail(\"Unsupported date unit.\");\n d.setTime(d.getTime() + amount * ms!);\n }\n out = d.toISOString();\n break;\n }\n case \"DATE_DIFF\": {\n const units: Record<string, number> = {\n week: 604800000,\n day: 86400000,\n hour: 3600000,\n minute: 60000,\n second: 1000,\n millisecond: 1,\n };\n const ms = units[str(a).toLowerCase()];\n if (!ms) fail(\"Unsupported elapsed date unit.\");\n out = Math.trunc((date(c).getTime() - date(b).getTime()) / ms!);\n break;\n }\n case \"FORMAT_DATE\": {\n const d = date(a),\n pad = (n: number) => String(n).padStart(2, \"0\"),\n parts: Record<string, string> = {\n YYYY: String(d.getUTCFullYear()).padStart(4, \"0\"),\n MM: pad(d.getUTCMonth() + 1),\n DD: pad(d.getUTCDate()),\n HH: pad(d.getUTCHours()),\n mm: pad(d.getUTCMinutes()),\n ss: pad(d.getUTCSeconds()),\n };\n out = str(b).replace(/YYYY|MM|DD|HH|mm|ss/g, (t) => parts[t]!);\n break;\n }\n default:\n return fail(`Unknown operation: ${name}`);\n }\n return bound(out);\n }\n try {\n const value = run(ast);\n return inspection ? { value, regex: inspection } : { value };\n } catch (e) {\n return {\n value: null,\n error: e instanceof Error ? e.message : \"Formula evaluation failed.\",\n };\n }\n}\n"],"mappings":"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","names":["field","f","next","resolveField","f","resolveField","f","item","a","b","n","parts"]}
|