import type { TemplateCall } from "../ir/ir.js"; import type { Column } from "./schema.js"; import type { SchemaProvider } from "./schema-provider.js"; export type { TemplateCall } from "../ir/ir.js"; /** * The syntactic SLOT a call's rendered output occupies — the parse-time answer * the placeholder mechanism needs: * - `nothing` — no output at all (config/docs/…): whitespace fill. * - `statement` — a whole statement / query body (also fits a `(…)` CTE/subquery body). * - `relation` — a relation in FROM (rendered as a query body — same `SELECT 1` fill as `statement`). * - `predicate` — a boolean expression (a WHERE/ON/HAVING slot). * - `column-list`— one or more select items (the slot parses; the real column COUNT differs). * - `conjunct` — a TRAILING boolean conjunct (`and c = false`) appended to a complete ON/WHERE * expression (the dbt `is_deleted_filter`-family macro shape) — fills `AND 1=1`. * - `where-clause` — a LEADING WHERE clause (`where c = false`) after a complete FROM/JOIN * context (the mode-as-argument macro family: `{{ m('col','where') }}` — the * 2026-07-06 gold__vendor F5 finding) — fills `WHERE 1=1`. * - `cte-definition` — one or more complete WITH-clause CTE definitions + trailing comma * (a macro whose whole body is `name as (...),` — the anvil real-model * finding, 2026-07-07): admitted ONLY immediately after a `,` that follows a * completed prior CTE clause; fills a PER-TAG-UNIQUE synthetic `name as (...)` * (the fill introduces a name into the WITH list's namespace, so — unlike * every other shape's fixed fragment — it can't repeat verbatim across tags). * - `expr` — a scalar expression (the identifier fill — the zero-knowledge default). */ export type ExpansionShape = "expr" | "column-list" | "predicate" | "relation" | "statement" | "conjunct" | "where-clause" | "cte-definition" | "nothing"; /** The NEUTRAL value-type vocabulary for `ResolvedExpansion.value` (the engine maps each to its * per-dialect scalar type). Deliberately closed and small — a stringly-typed field with no * vocabulary drifts. */ export type ValueType = "string" | "integer" | "float" | "boolean"; /** The resolved physical relation a relation-producing call maps to (payload unchanged from the * pre-unification `relation()`). `columns` text is DIALECT-NATIVE warehouse type strings. */ export interface ResolvedRelation { /** The resolved relation name parts. The DEFAULT provider answers the dbt-LOGICAL name * (`["orders"]`); an overriding provider answers the PHYSICAL one (`["analytics","orders"]`). */ nameParts: string[]; /** The relation's columns, or undefined until an async describe lands (async warm). */ columns?: Column[]; } /** A completion candidate a host offers for a template call slot (a dbt model for a ref's arg 0, a * source name for a source's arg 0). `label` is what the editor inserts at the caret; `detail` is * optional display text (a schema, a path). The neutral provider offers none. See * `TemplateProvider.templateCandidates`. */ export interface TemplateCandidate { label: string; detail?: string; } /** Everything known about one call's expansion. Every field optional; `undefined` = unknown. */ export interface ResolvedExpansion { /** Parse-time shape. When absent, derived from the strongest present field: * relation → "relation", columns → "column-list", value → "expr" (an explicit shape always wins). */ shape?: ExpansionShape; /** A relation-producing call (ref, source, a TVF-like macro). */ relation?: ResolvedRelation; /** A scalar value — `{{ var('x') }}`, `{{ env_var('Y') }}`, a scalar macro. */ value?: { type: ValueType; }; /** A column-list-producing macro's output columns (dialect-native type text, like relation columns). */ columns?: Column[]; /** A loop collection's items. The engine binds the FIRST item as the loop variable's * representative value; it NEVER unrolls (coordinate preservation is absolute). */ collection?: string[]; } /** dbt builtins that render no output — knowledge of the DEFAULT provider (was a hardcoded set inside * the segmenter). Exported for the tag-AST's syntactic kind labels, which share the same list. */ export declare const NO_OUTPUT_BUILTINS: ReadonlySet; /** * The shipped default provider — concrete, fully functional with zero input, * designed for inheritance. Override the granular methods (`relationOf`, * `valueOf`, `shapeOf`, `columnsOf`, `collectionOf`, and the SchemaProvider * methods `columnsFor`/`tables`) with what your host knows; call * `recordMiss(call)` (or `recordTableMiss(parts)`) from an override when a * lookup is cold, and implement `fetchExpansions`/`fetchTables` so `prime()` * can warm them. Everything you don't override keeps the conservative default. * * Instances may be PER-DOCUMENT and close over document identity + dialect. */ export declare class DefaultTemplateProvider implements SchemaProvider { /** OPEN world by default: a `columnsFor` miss means "unknown — do not diagnose", which makes a * bare instance the safe always-present default. A subclass whose cache is authoritative-and- * self-healing (describe + prime/re-publish) may override to `"closed"` to get unknown-table. */ readonly world: "closed" | "open"; /** Columns of a physical table. Default: unknown. Override with your warm describe cache and * call `recordTableMiss(foldedParts)` on a cold lookup. */ columnsFor(_parts: string[], _dialect?: string): Column[] | undefined; /** Bare table-name candidates for completion. Default: none. */ tables(_dialect?: string): string[]; /** Monotonic invalidation signal — bumps when `prime()` warmed anything new. */ get version(): number; /** The relation a call produces. The NEUTRAL floor knows no macro vocabulary, so it answers * undefined for everything: `ref`/`source` are dbt macros, not minijinja knowledge, and live in * `DbtTemplateProvider`. An overriding provider answers the relation (+columns) it knows. */ relationOf(_call: TemplateCall): ResolvedRelation | undefined; /** The scalar value a call yields. Neutral floor: unknown (no macro vocabulary). `env_var` is a * dbt builtin known by `DbtTemplateProvider`. */ valueOf(_call: TemplateCall): { type: ValueType; } | undefined; /** The rendered-output shape of a call. Neutral floor: unknown (the engine derives a shape from * stronger fields, or falls back to its positional fill). The dbt no-output builtins → "nothing" * is `DbtTemplateProvider` knowledge. */ shapeOf(_call: TemplateCall): ExpansionShape | undefined; /** The columns a column-list-producing macro emits. Default: unknown. */ columnsOf(_call: TemplateCall): Column[] | undefined; /** The items a loop collection holds. Default: unknown (loops analyze one representative pass). */ collectionOf(_call: TemplateCall): string[] | undefined; /** Completion candidates for a template call slot: the caret sits in `callee`'s positional argument * `argIndex` (a dbt ref's arg 0 answers the model names, a source's arg 0 the source names, arg 1 * the table names). `argIndex` is -1 when the caret is still in the callee name itself, so a host * can answer the macro/callee names it knows. `packageName` is the dotted package (`dbt_utils` in * `dbt_utils.star(...)`). The NEUTRAL provider knows no vocabulary and offers none; a host answers * from its catalog. `completeAt` reads this when the caret is inside a jinja tag. The WHOLE * parsed call comes along (issue #37) — a slot's candidates can depend on the sibling args: * `source('raw', '|')`'s candidates are the tables OF the source named in `call.args[0]`. * `argIndex` is the positional arg the caret is in (`-1` = the callee-name slot). */ templateCandidates(_call: TemplateCall, _argIndex: number): TemplateCandidate[]; /** * Everything known about `call`, composed from the granular methods. Field precedence for the * shape (channel-agreed): an EXPLICIT `shapeOf` answer always wins; absent, derived * strongest-first — relation → "relation", columns → "column-list", value → "expr". Returns * undefined when nothing at all is known (the engine's zero-knowledge floor). */ expansion(call: TemplateCall): ResolvedExpansion | undefined; private _version; private readonly _misses; private readonly missSeen; private readonly _tableMisses; private readonly tableMissSeen; private inFlight; /** The recorded misses — table misses first (as folded parts), then call misses (as their * key parts: [package…, name]). Distinct, first-seen order. Drained by prime(). */ get misses(): ReadonlyArray; /** Record a cold `expansion` lookup (call from an overriding granular method). */ protected recordMiss(call: TemplateCall): void; /** Record a cold `columnsFor` lookup. Fold parts first (`foldIdentifier(p, dialect, "table")`) * so the miss key matches your cache key. */ protected recordTableMiss(foldedParts: string[]): void; /** Async warm-up for missed calls — populate the cache your granular overrides read. Default * no-op (nothing ever warms). */ protected fetchExpansions(_missing: TemplateCall[]): Promise; /** Async warm-up for missed tables — populate the cache your `columnsFor` override reads. */ protected fetchTables(_missing: string[][]): Promise; /** * Drain both recorded miss lists through the fetch hooks, RE-PROBE each miss (calls through * `expansion()`, tables through `columnsFor()`), drop the ones that now resolve, and bump * `version` once when anything new arrived. Resolves true when it did (re-analyze). A second * prime() while one is in flight returns the SAME promise (coalescing); a miss recorded DURING * the in-flight fetch is re-recorded by the next analyze and warms one prime later — * never-wrong holds throughout. */ prime(): Promise; private drain; } /** * The shipped dbt overlay: a `DefaultTemplateProvider` that knows dbt's built-in macros and nothing * more. `ref(...)` resolves to the dbt-LOGICAL model name, `source(a,b)` to the logical * [source, table], `env_var` to a string, and the no-output builtins * (config/docs/print/log/return/exceptions) render nothing. Static famous-macro knowledge ONLY: no * manifest, no warehouse, no project config, no runtime. A dbt consumer with runtime knowledge * extends THIS (not the neutral `DefaultTemplateProvider`) and overrides the granular methods with * what its manifest / describe cache resolve to. This class is where the dbt vocabulary lives so the * neutral core and default do not carry it. */ export declare class DbtTemplateProvider extends DefaultTemplateProvider { relationOf(call: TemplateCall): ResolvedRelation | undefined; valueOf(call: TemplateCall): { type: ValueType; } | undefined; shapeOf(call: TemplateCall): ExpansionShape | undefined; } /** The provider type the engine consults — the shipped base (or any subclass of it, e.g. * `DbtTemplateProvider`). */ export type TemplateProvider = DefaultTemplateProvider; /** * The ONE shared no-configuration default — an OPEN world that answers nothing and diagnoses * nothing. Sharing a single instance across documents/calls is safe ONLY because the bare base * is stateless (its granular defaults never record misses — pinned by the statelessness test in * tests/minijinja.template-provider.test.ts). A CONFIGURED provider must stay per-document per * the contract above; this constant is exclusively the "nothing configured" value. */ export declare const OPEN_PROVIDER: TemplateProvider;