sqllens 0.1.0 → 1.0.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.
Files changed (68) hide show
  1. package/README.md +409 -255
  2. package/dist/api.d.ts +9 -7
  3. package/dist/api.js +15 -6
  4. package/dist/bigquery/parse.d.ts +5 -18
  5. package/dist/bigquery/parse.js +1 -1
  6. package/dist/completion/complete.d.ts +3 -1
  7. package/dist/completion/complete.js +4 -2
  8. package/dist/completion/config.d.ts +1 -1
  9. package/dist/completion/parser-factory.d.ts +1 -1
  10. package/dist/databricks/parse.d.ts +4 -17
  11. package/dist/derived-dialects.d.ts +8 -0
  12. package/dist/derived-dialects.js +50 -0
  13. package/dist/dialect-symbols.d.ts +1 -1
  14. package/dist/dialect.d.ts +3 -0
  15. package/dist/dialect.js +1 -0
  16. package/dist/document/document.d.ts +159 -5
  17. package/dist/document/document.js +473 -18
  18. package/dist/document/node-at.js +2 -132
  19. package/dist/document/shift.d.ts +6 -0
  20. package/dist/document/shift.js +11 -0
  21. package/dist/document/split.d.ts +1 -1
  22. package/dist/duckdb/parse.d.ts +4 -16
  23. package/dist/index.d.ts +12 -7
  24. package/dist/index.js +15 -10
  25. package/dist/ir/ir.d.ts +19 -1
  26. package/dist/ir/part-span.d.ts +11 -0
  27. package/dist/ir/part-span.js +35 -1
  28. package/dist/ir/walk.d.ts +9 -0
  29. package/dist/ir/walk.js +135 -0
  30. package/dist/lineage/lineage.d.ts +4 -1
  31. package/dist/lineage/lineage.js +5 -1
  32. package/dist/minijinja/apply-tags.d.ts +12 -3
  33. package/dist/minijinja/apply-tags.js +30 -16
  34. package/dist/minijinja/engine.d.ts +4 -0
  35. package/dist/minijinja/engine.js +11 -0
  36. package/dist/minijinja/index.d.ts +8 -0
  37. package/dist/minijinja/index.js +8 -0
  38. package/dist/minijinja/parse.d.ts +3 -44
  39. package/dist/minijinja/parse.js +71 -11
  40. package/dist/minijinja/tag-ast.js +16 -2
  41. package/dist/minijinja/variants.d.ts +10 -2
  42. package/dist/minijinja/variants.js +43 -6
  43. package/dist/parse-result.d.ts +11 -0
  44. package/dist/parse-result.js +1 -0
  45. package/dist/postgres/parse.d.ts +4 -16
  46. package/dist/qualify/qualify.d.ts +8 -1
  47. package/dist/qualify/qualify.js +21 -5
  48. package/dist/qualify/template-provider.d.ts +2 -19
  49. package/dist/redshift/parse.d.ts +4 -16
  50. package/dist/references/references.js +2 -0
  51. package/dist/scope/walk.d.ts +15 -0
  52. package/dist/scope/walk.js +73 -0
  53. package/dist/session.d.ts +83 -0
  54. package/dist/session.js +159 -0
  55. package/dist/signature/signatures.d.ts +1 -1
  56. package/dist/snowflake/parse.d.ts +4 -16
  57. package/dist/symbols/symbols.d.ts +14 -0
  58. package/dist/symbols/symbols.js +115 -3
  59. package/dist/template/engine.d.ts +65 -0
  60. package/dist/template/engine.js +1 -0
  61. package/dist/token/classify.d.ts +1 -1
  62. package/dist/token/map.d.ts +1 -1
  63. package/dist/token/tokenize.d.ts +1 -1
  64. package/dist/trino/parse.d.ts +4 -15
  65. package/dist/tsql/parse.d.ts +4 -16
  66. package/package.json +95 -91
  67. package/dist/adapters.d.ts +0 -8
  68. package/dist/adapters.js +0 -43
package/dist/api.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ParserRuleContext } from "antlr4ng";
2
- import type { Expr, QueryExpr } from "./ir/ir.js";
2
+ import type { Expr, Projection, QueryExpr } from "./ir/ir.js";
3
3
  import type { SyntaxDiagnostic } from "./parse-diagnostics.js";
4
4
  import { type Scope, type ScopeTree } from "./scope/scope.js";
5
5
  import { type Qualification } from "./qualify/qualify.js";
@@ -10,9 +10,8 @@ export type { Nullability } from "./infer/nullability.js";
10
10
  import { originsOf as exprOriginsOf, type ColumnLineage, type Origin } from "./lineage/lineage.js";
11
11
  import { type StarExpansion, type Sym } from "./symbols/symbols.js";
12
12
  import type { Token } from "./token/token.js";
13
- /** The dialects reachable through the unified surface. Each has its own grammar/CST and a
14
- * parse+lower pair; everything after lower() runs unchanged on all seven. */
15
- export type Dialect = "databricks" | "tsql" | "snowflake" | "bigquery" | "redshift" | "postgres" | "duckdb" | "trino";
13
+ import type { Dialect } from "./dialect.js";
14
+ export type { Dialect } from "./dialect.js";
16
15
  /** Options carrying the dialect, needed only when a lift helper enters from a raw string. */
17
16
  export interface DialectOpts {
18
17
  dialect?: Dialect;
@@ -103,22 +102,25 @@ export declare class Lineage {
103
102
  /** Per output column: the base-table columns it derives from. */
104
103
  readonly all: readonly ColumnLineage[];
105
104
  private readonly byOutput;
105
+ private readonly byNode;
106
106
  constructor(columns: ColumnLineage[]);
107
107
  /** The base-table origins of a named output column, or [] if there is no such output. */
108
108
  originsOf(column: string): Origin[];
109
+ /** Origins keyed by the producing Projection node — unambiguous under duplicate output names. */
110
+ originsOfNode(projection: Projection): Origin[];
109
111
  }
110
112
  export { exprOriginsOf as originsOfExpr };
111
113
  export { lineageAt, lineageOf, type LineageHop, type ViaStep } from "./lineage/hops.js";
112
114
  export { tokenize } from "./token/tokenize.js";
113
115
  export type { Token, TokenRole } from "./token/token.js";
114
- export { SqlDocument, type DocumentAnalysis, type StatementCell } from "./document/document.js";
116
+ export { SqlDocument, type DocumentAnalysis, type StatementCell, type DocumentVariant, type UnionCte, } from "./document/document.js";
115
117
  export { LineIndex } from "./document/line-index.js";
116
118
  export type { StatementCellSpan } from "./document/split.js";
117
- export { complete, type Completion } from "./completion/complete.js";
119
+ export { complete, completeAt, type Completion } from "./completion/complete.js";
118
120
  export { signatureAt, type SignatureInfo } from "./signature/signature.js";
119
121
  export { FUNCTION_SIGNATURES, HARVESTED_SIGNATURES, lookupSignature, hasSignature, type FnSignature, type ParamSig, } from "./signature/signatures.js";
120
122
  export { referencesAt, type Occurrence, type Occurrences } from "./references/references.js";
121
123
  export { dialectSymbols, type DialectSymbols } from "./dialect-symbols.js";
122
124
  export { CallbackSchema, type SchemaProvider, type TableResolver } from "./qualify/schema-provider.js";
123
- export { ADAPTER_DIALECTS, adapterDialect } from "./adapters.js";
125
+ export { DERIVED_DIALECTS, resolveDialect } from "./derived-dialects.js";
124
126
  export { foldIdentifier, displayName, type IdentKind } from "./ident/fold.js";
package/dist/api.js CHANGED
@@ -157,16 +157,25 @@ export class Lineage {
157
157
  /** Per output column: the base-table columns it derives from. */
158
158
  all;
159
159
  byOutput;
160
+ byNode;
160
161
  constructor(columns) {
161
162
  this.all = columns;
162
163
  this.byOutput = new Map();
163
- for (const c of columns)
164
+ this.byNode = new WeakMap();
165
+ for (const c of columns) {
164
166
  this.byOutput.set(c.output, c.origins);
167
+ if (c.projection)
168
+ this.byNode.set(c.projection, c.origins);
169
+ }
165
170
  }
166
171
  /** The base-table origins of a named output column, or [] if there is no such output. */
167
172
  originsOf(column) {
168
173
  return this.byOutput.get(column) ?? [];
169
174
  }
175
+ /** Origins keyed by the producing Projection node — unambiguous under duplicate output names. */
176
+ originsOfNode(projection) {
177
+ return this.byNode.get(projection) ?? [];
178
+ }
170
179
  }
171
180
  // Re-export the single-expression origin walk under its building-block name (distinct from the
172
181
  // Lineage wrapper) so consumers can trace one expression without a full query lineage.
@@ -185,11 +194,11 @@ export { tokenize } from "./token/tokenize.js";
185
194
  // It composes the surface above (parse/toScopes/qualify/deriveSymbols/TypeInfo); the import cycle
186
195
  // (api re-exports SqlDocument, document imports from api) is safe because document.ts only calls
187
196
  // these at call time, never at module-eval time.
188
- export { SqlDocument } from "./document/document.js";
197
+ export { SqlDocument, } from "./document/document.js";
189
198
  export { LineIndex } from "./document/line-index.js";
190
199
  // Scope-aware completion over a SqlDocument — the broken-input editor feature (keywords + schema
191
200
  // tables/columns + function names at the caret). Total: never throws.
192
- export { complete } from "./completion/complete.js";
201
+ export { complete, completeAt } from "./completion/complete.js";
193
202
  // Signature help over a SqlDocument — the broken-input editor feature that shows parameter hints
194
203
  // while typing inside a call's parens. Lookup order: curated (hand-verified) → harvested (doc-derived
195
204
  // long tail, from tools/harvest-signatures.mjs) → name-only fallback. A pure token scan; never throws.
@@ -207,9 +216,9 @@ export { dialectSymbols } from "./dialect-symbols.js";
207
216
  // lazy resolver whose prime() bumps a version to invalidate SqlDocument.analyze's memo) both
208
217
  // satisfy `SchemaProvider`; every analysis entry point accepts the interface.
209
218
  export { CallbackSchema } from "./qualify/schema-provider.js";
210
- // The dbt-adapter → dialect map: resolve a profiles.yml `type:` value (athena, glue, fabric,
211
- // spark, …) to the dialect that parses its SQL, so consumers don't re-derive the family knowledge.
212
- export { ADAPTER_DIALECTS, adapterDialect } from "./adapters.js";
219
+ // The derived-dialect → dialect map: resolve an engine name (athena, glue, fabric, spark, …) to the
220
+ // dialect that parses its SQL, so consumers don't re-derive the family knowledge.
221
+ export { DERIVED_DIALECTS, resolveDialect } from "./derived-dialects.js";
213
222
  // The dialect-true identifier fold — the identity key for name comparison (unquote + case-fold per
214
223
  // the dialect's documented rules) and its display twin (unquote only). Exported so an embedding
215
224
  // consumer comparing names against IR/scope output folds the same way the pipeline does.
@@ -1,21 +1,8 @@
1
- import { type ParserRuleContext } from "antlr4ng";
2
- import { type SyntaxDiagnostic } from "../parse-diagnostics.js";
3
- import type { Token } from "../token/token.js";
4
- export interface ParseResult {
5
- /** The CST rooted at `root` (`stmts EOF`). */
6
- tree: ParserRuleContext;
7
- /** Count of lexer + parser + escape + post-parse syntax errors; equals `diagnostics.length`. */
8
- errors: number;
9
- /** Positioned syntax diagnostics — lexer/parser (listener-captured) plus escape and post-parse
10
- * errors, all carrying a source span. `errors === diagnostics.length`. */
11
- diagnostics: SyntaxDiagnostic[];
12
- /** Every token the parser consumed (trivia included, EOF excluded), as neutral `Token`s. Built
13
- * from the dot-path-rewritten stream, so spans match the consumed source. */
14
- tokens: Token[];
15
- /** True when the fast SLL prediction pass bailed and the parse re-ran under full LL. Same result
16
- * either way — this just says which path produced it, for perf profiling (tools/profile-sll.ts). */
17
- sllFallback: boolean;
18
- }
1
+ import type { ParseResult } from "../parse-result.js";
2
+ /** The CST rooted at `root` (`stmts EOF`); errors count lexer + parser + escape + post-parse
3
+ * diagnostics (`errors === diagnostics.length`); tokens are built from the dot-path-rewritten
4
+ * stream. See `ParseResult` for the full result shape. */
5
+ export type { ParseResult } from "../parse-result.js";
19
6
  /**
20
7
  * Lex + parse BigQuery / GoogleSQL (one statement or a `;`-separated batch). Two-stage parsing:
21
8
  * try the fast SLL prediction mode first (bail on the first conflict), fall back to full LL
@@ -1,4 +1,4 @@
1
- import { BailErrorStrategy, CharStream, CommonTokenStream, PredictionMode, } from "antlr4ng";
1
+ import { BailErrorStrategy, CharStream, CommonTokenStream, PredictionMode } from "antlr4ng";
2
2
  import { GoogleSQLLexer } from "../generated/bigquery/GoogleSQLLexer.js";
3
3
  import { GoogleSQLParser } from "../generated/bigquery/GoogleSQLParser.js";
4
4
  import { dotPathTokenSource } from "./dot-path.js";
@@ -13,4 +13,6 @@ export interface Completion {
13
13
  * (table names + column types). NEVER throws: on broken / mid-edit input it still returns the
14
14
  * keyword candidates the walk can reach.
15
15
  */
16
- export declare function complete(doc: SqlDocument, offset: number, schema?: SchemaProvider): Completion[];
16
+ export declare function completeAt(doc: SqlDocument, offset: number, schema?: SchemaProvider): Completion[];
17
+ /** @deprecated Use completeAt — same function, uniform cursor-verb naming. */
18
+ export declare const complete: typeof completeAt;
@@ -1,5 +1,5 @@
1
1
  // ---------------------------------------------------------------------------
2
- // complete() — scope-aware completion over a SqlDocument.
2
+ // completeAt() — scope-aware completion over a SqlDocument.
3
3
  //
4
4
  // The interactive editor feature that lives in the BROKEN-input world: the user
5
5
  // is mid-keystroke, so this runs its OWN error-tolerant lex+parse of the current
@@ -25,7 +25,7 @@ import { makeParser } from "./parser-factory.js";
25
25
  * (table names + column types). NEVER throws: on broken / mid-edit input it still returns the
26
26
  * keyword candidates the walk can reach.
27
27
  */
28
- export function complete(doc, offset, schema) {
28
+ export function completeAt(doc, offset, schema) {
29
29
  try {
30
30
  return collect(doc, offset, schema);
31
31
  }
@@ -34,6 +34,8 @@ export function complete(doc, offset, schema) {
34
34
  return [];
35
35
  }
36
36
  }
37
+ /** @deprecated Use completeAt — same function, uniform cursor-verb naming. */
38
+ export const complete = completeAt;
37
39
  function collect(doc, offset, schema) {
38
40
  const dialect = doc.dialect;
39
41
  const cfg = COMPLETION_CONFIG[dialect];
@@ -1,4 +1,4 @@
1
- import type { Dialect } from "../api.js";
1
+ import type { Dialect } from "../dialect.js";
2
2
  /**
3
3
  * Per-dialect tuning for the ATN candidate walk (`collectCandidates`).
4
4
  *
@@ -1,5 +1,5 @@
1
1
  import { CommonTokenStream, type Lexer, type Parser, type ParserRuleContext } from "antlr4ng";
2
- import type { Dialect } from "../api.js";
2
+ import type { Dialect } from "../dialect.js";
3
3
  /**
4
4
  * A ready-to-walk parser for the completion engine: the lexer, the token stream, the entry
5
5
  * rule's index (for `atn.ruleToStartState[...]`), and a `runEntry()` that drives the parse far
@@ -1,20 +1,7 @@
1
- import { type ParserRuleContext } from "antlr4ng";
2
- import { type SyntaxDiagnostic } from "../parse-diagnostics.js";
3
- import type { Token } from "../token/token.js";
4
- export interface ParseResult {
5
- /** The CST rooted at `multiStatement` (a `;`-separated batch of statements and/or
6
- * BEGIN…END SQL-scripting compounds, + EOF). */
7
- tree: ParserRuleContext;
8
- /** Count of lexer + parser syntax errors. */
9
- errors: number;
10
- /** Positioned syntax diagnostics (message + line/column/offset/length), in report order. */
11
- diagnostics: SyntaxDiagnostic[];
12
- /** Every lexer token (trivia included, EOF excluded), as neutral `Token`s with exact spans. */
13
- tokens: Token[];
14
- /** True when the fast SLL prediction pass bailed and the parse re-ran under full LL. Same result
15
- * either way — this just says which path produced it, for perf profiling (tools/profile-sll.ts). */
16
- sllFallback: boolean;
17
- }
1
+ import type { ParseResult } from "../parse-result.js";
2
+ /** The CST rooted at `multiStatement` (a `;`-separated batch of statements and/or
3
+ * BEGIN…END SQL-scripting compounds, + EOF); see `ParseResult` for the full result shape. */
4
+ export type { ParseResult } from "../parse-result.js";
18
5
  /**
19
6
  * Lex + parse one Databricks SQL statement. Two-stage parsing: try the fast SLL
20
7
  * prediction mode first (bail on the first conflict), and fall back to full LL only
@@ -0,0 +1,8 @@
1
+ import type { Dialect } from "./dialect.js";
2
+ export declare const DERIVED_DIALECTS: Readonly<Record<string, Dialect>>;
3
+ /**
4
+ * Resolve an engine / product name (or a dialect name) to the dialect that
5
+ * parses its SQL. Case-insensitive. Returns undefined for anything not
6
+ * genuinely served by a gated grammar — never guesses.
7
+ */
8
+ export declare function resolveDialect(engineOrDialect: string): Dialect | undefined;
@@ -0,0 +1,50 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Derived-dialect → dialect map. The eight grammars parse more than eight
3
+ // engines: a *derived dialect* is an engine with no grammar of its own whose
4
+ // SQL surface is a subset of — or identical to — one we already parse (Amazon
5
+ // Athena's engine is Trino, AWS Glue runs Spark, Microsoft Fabric / Azure
6
+ // Synapse / SQL Server speak T-SQL, …). A consumer that only knows an engine
7
+ // name should not have to own that knowledge — this table does.
8
+ //
9
+ // Keys are engine / product names (plus our own dialect names, so both
10
+ // vocabularies resolve). Only engines whose SQL surface our corpus gates
11
+ // genuinely represent are mapped — an unlisted name resolves to undefined,
12
+ // never to a guess.
13
+ // ---------------------------------------------------------------------------
14
+ export const DERIVED_DIALECTS = {
15
+ // identity — the engine name and the dialect name coincide
16
+ databricks: "databricks",
17
+ snowflake: "snowflake",
18
+ bigquery: "bigquery",
19
+ redshift: "redshift",
20
+ postgres: "postgres",
21
+ duckdb: "duckdb",
22
+ trino: "trino",
23
+ // our dialect name (not an engine name) — accepted so both vocabularies work
24
+ tsql: "tsql",
25
+ // Spark SQL family — Databricks SQL = Spark SQL; AWS Glue runs Spark
26
+ spark: "databricks",
27
+ glue: "databricks",
28
+ // T-SQL family — Microsoft Fabric and Azure Synapse (restricted T-SQL subsets)
29
+ // and SQL Server (the reference T-SQL)
30
+ fabric: "tsql",
31
+ synapse: "tsql",
32
+ sqlserver: "tsql",
33
+ // Trino family — Amazon Athena engine v3 routes queries/DML to Trino; Presto
34
+ // is Trino's predecessor
35
+ athena: "trino",
36
+ presto: "trino",
37
+ // Alternate spelling of the engine name (alias class, same as our own dialect
38
+ // names above). Admitted 2026-07-10 on the anvil channel's request; caveat noted
39
+ // there: no dbt adapter is attested to emit `postgresql` as its adapter_type —
40
+ // it is inherited consumer vocabulary, admitted as a cheap, honest alias.
41
+ postgresql: "postgres",
42
+ };
43
+ /**
44
+ * Resolve an engine / product name (or a dialect name) to the dialect that
45
+ * parses its SQL. Case-insensitive. Returns undefined for anything not
46
+ * genuinely served by a gated grammar — never guesses.
47
+ */
48
+ export function resolveDialect(engineOrDialect) {
49
+ return DERIVED_DIALECTS[engineOrDialect.trim().toLowerCase()];
50
+ }
@@ -1,4 +1,4 @@
1
- import type { Dialect } from "./api.js";
1
+ import type { Dialect } from "./dialect.js";
2
2
  /** Per-dialect membership sets — canonical UPPERCASE names. See module header for sources
3
3
  * and heuristic limits per set. */
4
4
  export interface DialectSymbols {
@@ -0,0 +1,3 @@
1
+ /** The dialects reachable through the unified surface. Each has its own grammar/CST and a
2
+ * parse+lower pair; everything after lower() runs unchanged on all eight. */
3
+ export type Dialect = "databricks" | "tsql" | "snowflake" | "bigquery" | "redshift" | "postgres" | "duckdb" | "trino";
@@ -0,0 +1 @@
1
+ export {};
@@ -1,13 +1,18 @@
1
1
  import type { ParserRuleContext } from "antlr4ng";
2
- import { TypeInfo, type Dialect } from "../api.js";
3
- import type { QueryExpr } from "../ir/ir.js";
2
+ import { TypeInfo } from "../api.js";
3
+ import { type LineageHop } from "../lineage/hops.js";
4
+ import { type Occurrences } from "../references/references.js";
5
+ import type { Dialect } from "../dialect.js";
6
+ import type { QueryExpr, PartSpan } from "../ir/ir.js";
4
7
  import type { StatementCategory } from "../ir/statement.js";
5
8
  import type { SyntaxDiagnostic } from "../parse-diagnostics.js";
6
9
  import type { ScopeTree } from "../scope/scope.js";
7
10
  import type { Qualification, Diagnostic } from "../qualify/qualify.js";
8
11
  import type { SchemaProvider } from "../qualify/schema-provider.js";
9
- import type { Sym } from "../symbols/symbols.js";
12
+ import { type TemplateProvider } from "../qualify/template-provider.js";
13
+ import type { Span, Sym } from "../symbols/symbols.js";
10
14
  import type { Token } from "../token/token.js";
15
+ import type { TemplateEngine, TemplatedParseResult, TemplateVariant } from "../template/engine.js";
11
16
  import { LineIndex } from "./line-index.js";
12
17
  import { type NodeHit } from "./node-at.js";
13
18
  import { type StatementCellSpan } from "./split.js";
@@ -45,6 +50,39 @@ export interface DocumentAnalysis {
45
50
  /** Qualification's semantic diagnostics (unknown table/column/field). */
46
51
  diagnostics: Diagnostic[];
47
52
  }
53
+ /** One coherent arm realization of a templated document. Lazy: nothing parses until doc() or
54
+ * text() is first touched. A variant IS a document — everything a plain SqlDocument exposes
55
+ * (ast/tokens/scopes/templated/analyze/cursor members) is available per arm through doc(). */
56
+ export interface DocumentVariant {
57
+ /** The arm this variant activates (undefined = variant 0, all defaults). Mirrors
58
+ * TemplateVariant.active, including the synthetic-empty marker from Task 1. */
59
+ readonly active?: TemplateVariant["active"];
60
+ /** The realized text: original text with inactive arms whitespace-blanked —
61
+ * length- and newline-preserving (spans stay document-true). Memoized. */
62
+ text(): string;
63
+ /** The arm's own SqlDocument: same dialect, SAME engine, SAME provider, SHARING the parent's
64
+ * content-addressed cell-cache family (an arm whose text an edit didn't change is a cache hit).
65
+ * Memoized per variant. */
66
+ doc(): SqlDocument;
67
+ }
68
+ /** One CTE's identity + its output columns unioned across arms (`SqlDocument.unionCtes`). */
69
+ export interface UnionCte {
70
+ /** Fold-normalized, quote-preserving identity form — `foldIdentifier(raw, dialect)`, the same
71
+ * vocabulary the column entries below (and the rest of the resolved surface) speak. The exact
72
+ * written form is recoverable by slicing the source at `declarationSpan`. */
73
+ name: string;
74
+ /** Declaration identity: the CTE name's own span (`CteDef.nameCst`, falling back to the whole
75
+ * `CteDef.cst` when the name itself has no real token). Part of the key: two same-named CTEs
76
+ * declared at different positions stay distinct entries. */
77
+ declarationSpan: PartSpan;
78
+ /** Output columns unioned by NAME across arms; each column's span is the FIRST LIVE ARM's (arm
79
+ * iteration order = `SqlDocument.variants` order) — the representative-span rule pinned by the
80
+ * variant-acceptance brief's A8a-c. Names are fold-normalized like `name` above. */
81
+ columns: {
82
+ name: string;
83
+ span: Span;
84
+ }[];
85
+ }
48
86
  export declare class SqlDocument {
49
87
  readonly uri?: string;
50
88
  readonly version: number;
@@ -73,6 +111,10 @@ export declare class SqlDocument {
73
111
  * facade's scopes (`statement: "compound"`); use `statements`/`cellAt` for per-statement scopes. */
74
112
  readonly scopes: ScopeTree;
75
113
  readonly lines: LineIndex;
114
+ /** The template artifacts when this document was built with a `templating` engine:
115
+ * tags/regions/symbols/placeholder/degraded plus tagOf/nodeOf/diagnosticsOf.
116
+ * Undefined on plain documents. */
117
+ readonly templated?: TemplatedParseResult;
76
118
  /** Schema-keyed memo of the MERGED analyze() result (concat + coordinate shift). Rebuilt per doc
77
119
  * version — the merge must redo when an earlier statement's line count changes and shifts later
78
120
  * cells' doc coordinates — while the per-CELL analysis (the expensive qualify/deriveSymbols) is
@@ -86,6 +128,19 @@ export declare class SqlDocument {
86
128
  /** The content-addressed cross-edit cell cache, carried to withText() children. Its contents stay
87
129
  * mutable (a memo) even though the reference is frozen with the instance. */
88
130
  private readonly _cellCache;
131
+ /** The injected template engine + provider, carried to withText() children so an edit keeps
132
+ * building through the same door its parent used. Undefined on a plain document. */
133
+ private readonly _templating?;
134
+ private readonly _provider?;
135
+ /** Memo box for the `variants` getter (frozen instance, mutable memo — the `_analysisCache`
136
+ * precedent: the box reference is frozen with the instance, but its `.value` stays settable). */
137
+ private readonly _variantsMemo;
138
+ /** Schema-keyed memos for the four union views (`unionSymbols`/`unionDiagnostics`/`unionCtes`/
139
+ * `unionOutputColumns`) — one WeakMap per view, same identity+version pattern as `_analysisCache`. */
140
+ private readonly _unionSymbolsCache;
141
+ private readonly _unionDiagnosticsCache;
142
+ private readonly _unionCtesCache;
143
+ private readonly _unionOutputColumnsCache;
89
144
  private constructor();
90
145
  /** Build one statement cell for `span`: reuse the cached cell-relative parse if its text is
91
146
  * already known (content addressing), else parse+scope it and cache it; then shift tokens /
@@ -100,14 +155,29 @@ export declare class SqlDocument {
100
155
  * NOT replace the cache entry: the first occurrence keeps its stable cross-edit identity (the
101
156
  * common case), and only intra-doc duplicates — rare — pay a re-parse per build. */
102
157
  private buildCell;
158
+ /** Build the ONE cell for a TEMPLATED document: span [0, text.length) — the templated build path
159
+ * bypasses `splitStatements` entirely (one cell, whole text), and its products come from a single
160
+ * `engine.parse(text, dialect, { provider })` call rather than the plain per-dialect `parse()`.
161
+ * `r.tokens`/`r.diagnostics` are ALREADY document coordinates (the cell always starts at 0), so —
162
+ * unlike `buildCell` — nothing is shifted. Mirrors `buildCell`'s CachedCell/StatementCell shapes
163
+ * so every downstream consumer (analyze(), cellAt(), nodeAt()…) sees the same structure whether
164
+ * the document is plain or templated. Cached in the SAME cross-edit `_cellCache` as plain cells,
165
+ * under a prefixed key so a templated cell can never collide with a plain one for the same text. */
166
+ private buildTemplatedCell;
103
167
  /** Build a document for `text` in `dialect`. Total: never throws, even on broken / mid-edit input.
104
- * Starts a FRESH cell cache — cross-edit reuse comes from withText(), not create(). */
168
+ * Starts a FRESH cell cache — cross-edit reuse comes from withText(), not create(). Pass
169
+ * `templating` to parse through an injected TemplateEngine (jinja-SQL etc.) instead of the plain
170
+ * per-dialect parser — absent, this is the exact untouched plain-SQL path (never auto-detected).
171
+ * `provider` feeds the engine's fills/markers and is ignored without `templating`. */
105
172
  static create(text: string, dialect: Dialect, opts?: {
106
173
  uri?: string;
107
174
  version?: number;
175
+ templating?: TemplateEngine;
176
+ provider?: TemplateProvider;
108
177
  }): SqlDocument;
109
178
  /** An edit: a NEW SqlDocument for the new text. This instance is untouched (immutable). The cell
110
- * cache is CARRIED forward, so statements whose text didn't change reuse their parsed cells. */
179
+ * cache is CARRIED forward, so statements whose text didn't change reuse their parsed cells. The
180
+ * injected templating engine + provider (if any) ride the instance to this child too. */
111
181
  withText(text: string, version: number): SqlDocument;
112
182
  /** The statement cell owning `offset` (binary search over the tiling cell spans), or undefined if
113
183
  * there are no cells. An offset at end-of-document resolves to the last cell. */
@@ -122,6 +192,42 @@ export declare class SqlDocument {
122
192
  * `expr.cst` carries CELL-relative spans; a caller turning it into a document Range shifts it by the
123
193
  * owning cell's start (see `cellBaseAt` in src/lsp/ranges.ts). Single-cell: identical to today. */
124
194
  nodeAt(offset: number): NodeHit | undefined;
195
+ /** The declaration + every occurrence of the symbol at `offset` (the references engine,
196
+ * cell-aware): resolved over the CELL owning the offset — its own scopes/ast, with a
197
+ * cell-relative offset — then every returned span (occurrences + declaration) shifted from
198
+ * cell-relative to DOCUMENT coordinates by the cell base. Absorbs the dance the LSP
199
+ * references/documentHighlight/codeLens features hand-rolled. Single-cell documents: base
200
+ * 0/0/0, byte-identical to the free referencesAt over doc.scopes. Total: null off-symbol or
201
+ * with no cells; never throws. */
202
+ referencesAt(offset: number, schema?: SchemaProvider): Occurrences | null;
203
+ /** The per-hop lineage spine anchored at `offset` (cell-aware): resolved over the CELL owning
204
+ * the offset, with a cell-relative offset. NOTE: on a MULTI-statement document the returned
205
+ * hop nodes' cst spans are CELL-relative — the spine references the frozen per-cell IR (hops
206
+ * are references, not copies), so nothing is shifted here; use `cellAt(offset).span.start` as
207
+ * the base to map them to document coordinates. Single-cell documents (every dbt model) are
208
+ * identical either way. Total: undefined off-symbol or with no cells; never throws. */
209
+ lineageAt(offset: number, schema?: SchemaProvider): LineageHop | undefined;
210
+ /** The coherent per-arm variants of a templated document (engine.variants() consumed). `[]` on
211
+ * plain documents and on templated documents with no control-flow regions. Lazy at every level:
212
+ * enumeration on first read, realization+parse per variant on first doc()/text(). Memoized on
213
+ * the instance (frozen instance, mutable memo box — see `_variantsMemo`). */
214
+ get variants(): readonly DocumentVariant[];
215
+ /** Compute (no memo) the variant list: absent `variants` hook, a plain document, or a templated
216
+ * document with no control-flow regions all answer `[]`. Otherwise consults the engine, wrapped
217
+ * defensively (degrade to `[]`, never throw — engine.variants may throw only on engine bugs, the
218
+ * same posture parseTemplated's own catch uses). */
219
+ private buildVariants;
220
+ /** Wrap one engine-produced TemplateVariant as a DocumentVariant: `text()` delegates straight to
221
+ * the engine variant's own memoized realization (never re-realized here); `doc()` builds — once,
222
+ * memoized — the arm's own SqlDocument through the PRIVATE constructor, carrying this document's
223
+ * `_cellCache` (the exact carry `withText` uses, NOT the public `create`'s fresh cache) plus the
224
+ * same templating engine + provider, so an unchanged arm across an edit is a cache hit. */
225
+ private wrapVariant;
226
+ /** The variant whose realization has `offset` LIVE (its arm active): an offset inside a
227
+ * non-default arm routes to that arm's variant; a default-arm or outside-all-regions offset
228
+ * routes to variant 0. undefined on plain documents and no-region templated documents.
229
+ * Never throws; out-of-range offsets answer variant 0 (the honest default). */
230
+ variantAt(offset: number): DocumentVariant | undefined;
125
231
  /** The schema-dependent tiers, over the cached per-cell scopes/ast (no re-parse). Memoized by
126
232
  * schema IDENTITY + VERSION — a plain Schema (version 0) memoizes exactly as before; a
127
233
  * CallbackSchema that has been prime()d bumps its version and so re-computes with the newly
@@ -139,4 +245,52 @@ export declare class SqlDocument {
139
245
  * identity + version) so it survives edits that don't touch this cell, yet re-runs when a primed
140
246
  * CallbackSchema bumps its version. */
141
247
  private cellAnalysis;
248
+ /** Symbols across ALL variants, deduped by span+identity+NAME (`start:end:kind:frame:name` —
249
+ * NAME is load-bearing: schema-expanded star Syms share one zero-width span by design, see
250
+ * symbols.ts's emitColumns). Computed over the VARIANT documents only — never the primary
251
+ * parse, whose all-arms-live SQL can mis-read conflicting arms (e.g. `col_a col_b` reading as
252
+ * an alias) into junk that must never leak into the union. Equals the plain `analyze(schema)`
253
+ * symbols when there are no variants (a plain document, or a templated one with no
254
+ * control-flow regions). Memoized per schema identity+version, like `analyze()`. */
255
+ unionSymbols(schema?: SchemaProvider): Sym[];
256
+ private buildUnionSymbols;
257
+ /** Diagnostics across all variants — syntax + semantic, like `session.diagnostics()` — deduped
258
+ * by position+identity, NOT message text: two arms producing the SAME diagnostic at the SAME
259
+ * position collapse to one entry, but the same message at TWO DIFFERENT positions stays two
260
+ * entries (the exact case a message-keyed merge gets wrong). Same variant-only, memoized
261
+ * semantics as `unionSymbols`. */
262
+ unionDiagnostics(schema?: SchemaProvider): (SyntaxDiagnostic | Diagnostic)[];
263
+ private buildUnionDiagnostics;
264
+ /** Per-CTE column unions across all variants, keyed by NAME + declaration span (two same-named
265
+ * CTEs declared at different positions stay distinct entries; a CTE existing in only one arm
266
+ * still appears). Columns union by NAME; a column's representative span is the FIRST LIVE ARM's
267
+ * (arm iteration order = `this.variants` order) — the rule the variant-acceptance brief's A8a-c
268
+ * pin. A CTE whose body is a set operation answers through the qualification (names per SQL
269
+ * setop semantics, spans from the declaring branch); a PIPE-syntax body answers no columns — a
270
+ * visible, ledgered gap, see `scopeOutputColumns`. Falls through to this document's own
271
+ * (single-arm) answer when there are no variants — there is no pre-existing single-doc
272
+ * equivalent to delegate to, unlike unionSymbols/unionDiagnostics, so the no-variant case is
273
+ * just the one-arm instance of the same algorithm. Variant-only, memoized like `unionSymbols`.
274
+ * KNOWN GAP (visible, ledgered — not silently narrowed): on a multi-statement document these
275
+ * answer `[]` — the compound facade carries no CTEs/outputs; the per-cell merge is a tracked
276
+ * follow-up; single-statement documents (every dbt model) are fully covered. */
277
+ unionCtes(schema?: SchemaProvider): UnionCte[];
278
+ private buildUnionCtes;
279
+ /** The document's final-SELECT (root scope) output columns unioned across arms — same NAME
280
+ * keying and representative-span rule as `unionCtes`. A setop root (the dbt-incremental
281
+ * `… UNION ALL …` arm shape) answers through the qualification — names per SQL setop semantics
282
+ * (left branch positionally, BY NAME appends right-only), spans from the declaring branch. A
283
+ * PIPE-syntax root answers `[]` — a visible, ledgered gap, see `scopeOutputColumns`. Falls
284
+ * through to this document's own root outputs when there are no variants.
285
+ * KNOWN GAP (visible, ledgered — not silently narrowed): on a multi-statement document these
286
+ * answer `[]` — the compound facade carries no CTEs/outputs; the per-cell merge is a tracked
287
+ * follow-up; single-statement documents (every dbt model) are fully covered. */
288
+ unionOutputColumns(schema?: SchemaProvider): {
289
+ name: string;
290
+ span: Span;
291
+ }[];
292
+ private buildUnionOutputColumns;
293
+ /** The per-arm documents `unionCtes`/`unionOutputColumns` aggregate over: the real variants when
294
+ * they exist, else this document itself as the sole arm (see those methods' fall-through note). */
295
+ private arms;
142
296
  }