@telorun/templating 0.4.1 → 0.6.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.
@@ -1,12 +1,13 @@
1
1
  import { Environment } from "@marcbachmann/cel-js";
2
- export interface CelHandlers {
3
- sha256: (s: string) => string;
4
- json: (value: unknown) => string;
5
- }
6
- /** Build a CEL `Environment` with Telo's stdlib of functions. Always registers the
7
- * same function signatures (so `env.check()` succeeds for type-inference) the
8
- * handlers govern what the function does when called at runtime. Analyzer-only
9
- * callers can omit handlers; runtime callers (kernel) must supply real ones.
2
+ import { type CelHandlers } from "./catalog.js";
3
+ export type { CelHandlers } from "./catalog.js";
4
+ /** Build a CEL `Environment` with Telo's stdlib. Every function comes from the
5
+ * single-source catalog (`CEL_FUNCTIONS`), so registration and the documented
6
+ * surface (`telo cel functions`) can never drift. Always registers the same
7
+ * signatures (so `env.check()` succeeds for type-inference); the host-injected
8
+ * handlers govern what `hostBacked` functions do at runtime. Analyzer-only
9
+ * callers can omit handlers (the stubs throw if such a function is evaluated);
10
+ * runtime callers (kernel) supply real ones.
10
11
  *
11
12
  * Also registers the `Stream` object type, backed by the `Stream` class from
12
13
  * `@telorun/sdk`. CEL's type-checker rejects values whose constructor isn't
@@ -16,5 +17,5 @@ export interface CelHandlers {
16
17
  * no fields, so terminal access (passing the value through CEL) succeeds but
17
18
  * member access raises a CEL error at runtime — matching the analyzer's
18
19
  * static check on `x-telo-stream`-marked properties. */
19
- export declare function buildCelEnvironment(handlers?: CelHandlers): Environment;
20
+ export declare function buildCelEnvironment(handlers?: Partial<CelHandlers>): Environment;
20
21
  //# sourceMappingURL=environment.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/cel/environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAClC;AAcD;;;;;;;;;;;;yDAYyD;AACzD,wBAAgB,mBAAmB,CAAC,QAAQ,GAAE,WAA2B,GAAG,WAAW,CAgBtF"}
1
+ {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/cel/environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAE/D,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAoBhD;;;;;;;;;;;;;;;yDAeyD;AACzD,wBAAgB,mBAAmB,CAAC,QAAQ,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAYpF"}
@@ -1,17 +1,27 @@
1
1
  import { Environment } from "@marcbachmann/cel-js";
2
2
  import { Stream } from "@telorun/sdk";
3
+ import { CEL_FUNCTIONS } from "./catalog.js";
3
4
  const stub = (name) => () => {
4
5
  throw new Error(`${name}() is not available in this environment. ` +
5
6
  `Construct StaticAnalyzer or Loader with celHandlers to enable it.`);
6
7
  };
7
8
  const STUB_HANDLERS = {
8
9
  sha256: stub("sha256"),
10
+ md5: stub("md5"),
11
+ sha1: stub("sha1"),
12
+ sha512: stub("sha512"),
13
+ hmac: stub("hmac"),
14
+ base64Encode: stub("base64Encode"),
15
+ base64Decode: stub("base64Decode"),
9
16
  json: stub("json"),
10
17
  };
11
- /** Build a CEL `Environment` with Telo's stdlib of functions. Always registers the
12
- * same function signatures (so `env.check()` succeeds for type-inference) the
13
- * handlers govern what the function does when called at runtime. Analyzer-only
14
- * callers can omit handlers; runtime callers (kernel) must supply real ones.
18
+ /** Build a CEL `Environment` with Telo's stdlib. Every function comes from the
19
+ * single-source catalog (`CEL_FUNCTIONS`), so registration and the documented
20
+ * surface (`telo cel functions`) can never drift. Always registers the same
21
+ * signatures (so `env.check()` succeeds for type-inference); the host-injected
22
+ * handlers govern what `hostBacked` functions do at runtime. Analyzer-only
23
+ * callers can omit handlers (the stubs throw if such a function is evaluated);
24
+ * runtime callers (kernel) supply real ones.
15
25
  *
16
26
  * Also registers the `Stream` object type, backed by the `Stream` class from
17
27
  * `@telorun/sdk`. CEL's type-checker rejects values whose constructor isn't
@@ -21,20 +31,16 @@ const STUB_HANDLERS = {
21
31
  * no fields, so terminal access (passing the value through CEL) succeeds but
22
32
  * member access raises a CEL error at runtime — matching the analyzer's
23
33
  * static check on `x-telo-stream`-marked properties. */
24
- export function buildCelEnvironment(handlers = STUB_HANDLERS) {
25
- return new Environment({ unlistedVariablesAreDyn: true, enableOptionalTypes: true })
26
- .registerFunction("join(list, string): string", (list, sep) => list.map(String).join(sep))
27
- .registerFunction("keys(map): list", (map) => {
28
- if (map instanceof Map)
29
- return [...map.keys()];
30
- return Object.keys(map);
31
- })
32
- .registerFunction("values(map): list", (map) => {
33
- if (map instanceof Map)
34
- return [...map.values()];
35
- return Object.values(map);
36
- })
37
- .registerFunction("sha256(string): string", (s) => handlers.sha256(s))
38
- .registerFunction("json(dyn): string", (value) => handlers.json(value))
39
- .registerType("Stream", Stream);
34
+ export function buildCelEnvironment(handlers = {}) {
35
+ const h = { ...STUB_HANDLERS, ...handlers };
36
+ let env = new Environment({ unlistedVariablesAreDyn: true, enableOptionalTypes: true });
37
+ for (const fn of CEL_FUNCTIONS) {
38
+ const impl = fn.build(h);
39
+ // `register` lists one cel-js signature per arity (overloaded functions);
40
+ // it falls back to `signature` for the single-arity common case.
41
+ for (const sig of fn.register ?? [fn.signature]) {
42
+ env = env.registerFunction(sig, impl);
43
+ }
44
+ }
45
+ return env.registerType("Stream", Stream);
40
46
  }
@@ -1,4 +1,10 @@
1
- import type { TemplatingEngine } from "../engine.js";
1
+ import type { AnalyzeEnv, EngineDiagnostic, TemplatingEngine } from "../engine.js";
2
+ /** Statically analyze one CEL expression against the effective context schema:
3
+ * parse → extract member-access chains → validate each chain → flag nullable
4
+ * access. Single source of truth shared by the `!cel` engine (one expression)
5
+ * and the `!sql` engine (one per `${{ }}` interpolation), so diagnostic wording
6
+ * can't drift between them. */
7
+ export declare function analyzeCelExpression(source: string, env: AnalyzeEnv): EngineDiagnostic[];
2
8
  /** The `!cel` engine. Treats the entire tagged scalar as a single CEL
3
9
  * expression — no `${{ }}` wrapping. Analysis runs the same chain validator
4
10
  * as the untagged path: parse → extract member-access chains → validate each
@@ -1 +1 @@
1
- {"version":3,"file":"cel.d.ts","sourceRoot":"","sources":["../../src/engines/cel.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEvE;;;kDAGkD;AAClD,eAAO,MAAM,SAAS,EAAE,gBA4CvB,CAAC"}
1
+ {"version":3,"file":"cel.d.ts","sourceRoot":"","sources":["../../src/engines/cel.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEnF;;;;gCAIgC;AAChC,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,gBAAgB,EAAE,CAmCxF;AAED;;;kDAGkD;AAClD,eAAO,MAAM,SAAS,EAAE,gBAWvB,CAAC"}
@@ -1,5 +1,42 @@
1
1
  import { extractAccessChains, findNullableAccessIssues, validateChainAgainstSchema, } from "../cel/analyze.js";
2
2
  import { compileExpression } from "../cel/compile.js";
3
+ /** Statically analyze one CEL expression against the effective context schema:
4
+ * parse → extract member-access chains → validate each chain → flag nullable
5
+ * access. Single source of truth shared by the `!cel` engine (one expression)
6
+ * and the `!sql` engine (one per `${{ }}` interpolation), so diagnostic wording
7
+ * can't drift between them. */
8
+ export function analyzeCelExpression(source, env) {
9
+ const out = [];
10
+ let parsed;
11
+ try {
12
+ parsed = env.celEnv.parse(source);
13
+ }
14
+ catch (e) {
15
+ out.push({
16
+ code: "CEL_SYNTAX_ERROR",
17
+ message: e instanceof Error ? e.message : String(e),
18
+ });
19
+ return out;
20
+ }
21
+ if (!env.contextSchema)
22
+ return out;
23
+ const chains = extractAccessChains(parsed.ast);
24
+ for (const chain of chains) {
25
+ const err = validateChainAgainstSchema(chain, env.contextSchema);
26
+ if (err)
27
+ out.push({ code: "CEL_UNKNOWN_FIELD", message: err });
28
+ }
29
+ for (const issue of findNullableAccessIssues(parsed.ast, env.contextSchema)) {
30
+ // Index access (member "[index]") attaches without a dot; a named field
31
+ // attaches with one — so the suggested CEL stays valid either way.
32
+ const access = issue.member === "[index]" ? issue.member : `.${issue.member}`;
33
+ out.push({
34
+ code: "CEL_NULLABLE_ACCESS",
35
+ message: `'${issue.path}' may be null — guard it (e.g. '${issue.path} != null && …' or '${issue.path} == null ? … : ${issue.path}${access}') before accessing '${access}'`,
36
+ });
37
+ }
38
+ return out;
39
+ }
3
40
  /** The `!cel` engine. Treats the entire tagged scalar as a single CEL
4
41
  * expression — no `${{ }}` wrapping. Analysis runs the same chain validator
5
42
  * as the untagged path: parse → extract member-access chains → validate each
@@ -11,35 +48,6 @@ export const celEngine = {
11
48
  return compileExpression(source, env.celEnv);
12
49
  },
13
50
  analyze(source, env) {
14
- const out = [];
15
- let parsed;
16
- try {
17
- parsed = env.celEnv.parse(source);
18
- }
19
- catch (e) {
20
- out.push({
21
- code: "CEL_SYNTAX_ERROR",
22
- message: e instanceof Error ? e.message : String(e),
23
- });
24
- return out;
25
- }
26
- if (!env.contextSchema)
27
- return out;
28
- const chains = extractAccessChains(parsed.ast);
29
- for (const chain of chains) {
30
- const err = validateChainAgainstSchema(chain, env.contextSchema);
31
- if (err)
32
- out.push({ code: "CEL_UNKNOWN_FIELD", message: err });
33
- }
34
- for (const issue of findNullableAccessIssues(parsed.ast, env.contextSchema)) {
35
- // Index access (member "[index]") attaches without a dot; a named field
36
- // attaches with one — so the suggested CEL stays valid either way.
37
- const access = issue.member === "[index]" ? issue.member : `.${issue.member}`;
38
- out.push({
39
- code: "CEL_NULLABLE_ACCESS",
40
- message: `'${issue.path}' may be null — guard it (e.g. '${issue.path} != null && …' or '${issue.path} == null ? … : ${issue.path}${access}') before accessing '${access}'`,
41
- });
42
- }
43
- return out;
51
+ return analyzeCelExpression(source, env);
44
52
  },
45
53
  };
@@ -0,0 +1,11 @@
1
+ import { isParameterizedSql, type ParameterizedSql } from "@telorun/sdk";
2
+ import type { TemplatingEngine } from "../engine.js";
3
+ export { isParameterizedSql, type ParameterizedSql };
4
+ /** The `!sql` engine. Treats the tagged scalar as a SQL string with `${{ }}`
5
+ * interpolations whose values are *bound*, not spliced. Unlike `!cel` (one bare
6
+ * expression) it keeps the literal text and each interpolation separate: at
7
+ * runtime `call()` returns a {@link ParameterizedSql} the consumer turns into a
8
+ * parameterized query. Generic expansion passes that object through untouched
9
+ * (it is the `call()` result), so it survives the step-level input expansion. */
10
+ export declare const sqlEngine: TemplatingEngine;
11
+ //# sourceMappingURL=sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/engines/sql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAsB,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAG7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,CAAC;AAErD;;;;;kFAKkF;AAClF,eAAO,MAAM,SAAS,EAAE,gBAqBvB,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { isParameterizedSql } from "@telorun/sdk";
2
+ import { analyzeCelExpression } from "./cel.js";
3
+ import { compileString, toParameterized, TEMPLATE_REGEX } from "../cel/compile.js";
4
+ export { isParameterizedSql };
5
+ /** The `!sql` engine. Treats the tagged scalar as a SQL string with `${{ }}`
6
+ * interpolations whose values are *bound*, not spliced. Unlike `!cel` (one bare
7
+ * expression) it keeps the literal text and each interpolation separate: at
8
+ * runtime `call()` returns a {@link ParameterizedSql} the consumer turns into a
9
+ * parameterized query. Generic expansion passes that object through untouched
10
+ * (it is the `call()` result), so it survives the step-level input expansion. */
11
+ export const sqlEngine = {
12
+ name: "sql",
13
+ language: "sql",
14
+ compile(source, env) {
15
+ const inner = compileString(source, env.celEnv);
16
+ return {
17
+ __compiled: true,
18
+ source,
19
+ call: (ctx) => {
20
+ const { fragments, values } = toParameterized(inner, ctx);
21
+ return { __teloParameterized: true, fragments, values };
22
+ },
23
+ };
24
+ },
25
+ analyze(source, env) {
26
+ // Each `${{ }}` interpolation is its own CEL expression; reuse the shared
27
+ // per-expression analyzer so diagnostics match the `!cel` engine exactly.
28
+ return expressionsOf(source).flatMap((expr) => analyzeCelExpression(expr, env));
29
+ },
30
+ };
31
+ /** Extract each `${{ expr }}` body from a `!sql` template source. */
32
+ function expressionsOf(source) {
33
+ const exprs = [];
34
+ for (const m of source.matchAll(TEMPLATE_REGEX)) {
35
+ exprs.push(m[1].trim());
36
+ }
37
+ return exprs;
38
+ }
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  export { buildCelEnvironment, type CelHandlers } from "./cel/environment.js";
2
- export { compileExpression, compileString, TEMPLATE_REGEX, EXACT_TEMPLATE_REGEX, } from "./cel/compile.js";
2
+ export { celFunctionCatalog, CEL_FUNCTIONS, type CelFunctionInfo, type CelFunctionDoc, type CelFunctionCategory, } from "./cel/catalog.js";
3
+ export { compileExpression, compileString, toParameterized, TEMPLATE_REGEX, EXACT_TEMPLATE_REGEX, } from "./cel/compile.js";
3
4
  export { extractAccessChains, findNullableAccessIssues, INDEX_SEGMENT, validateChainAgainstSchema, } from "./cel/analyze.js";
4
5
  export { walkCelExpressions } from "./cel/walk.js";
5
6
  export { celEngine } from "./engines/cel.js";
6
7
  export { literalEngine } from "./engines/literal.js";
7
8
  export { refEngine } from "./engines/ref.js";
9
+ export { sqlEngine, isParameterizedSql, type ParameterizedSql } from "./engines/sql.js";
8
10
  export { TemplatingEngineRegistry } from "./registry.js";
9
11
  export { builtinEngines, createDefaultRegistry, defaultRegistry } from "./builtins.js";
10
12
  export type { AnalyzeEnv, CompileEnv, EngineDiagnostic, TemplatingEngine, } from "./engine.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,aAAa,EACb,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACvF,YAAY,EACV,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GACzB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,cAAc,EACd,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,aAAa,EACb,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAExF,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACvF,YAAY,EACV,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  export { buildCelEnvironment } from "./cel/environment.js";
2
- export { compileExpression, compileString, TEMPLATE_REGEX, EXACT_TEMPLATE_REGEX, } from "./cel/compile.js";
2
+ export { celFunctionCatalog, CEL_FUNCTIONS, } from "./cel/catalog.js";
3
+ export { compileExpression, compileString, toParameterized, TEMPLATE_REGEX, EXACT_TEMPLATE_REGEX, } from "./cel/compile.js";
3
4
  export { extractAccessChains, findNullableAccessIssues, INDEX_SEGMENT, validateChainAgainstSchema, } from "./cel/analyze.js";
4
5
  export { walkCelExpressions } from "./cel/walk.js";
5
6
  export { celEngine } from "./engines/cel.js";
6
7
  export { literalEngine } from "./engines/literal.js";
7
8
  export { refEngine } from "./engines/ref.js";
9
+ export { sqlEngine, isParameterizedSql } from "./engines/sql.js";
8
10
  export { TemplatingEngineRegistry } from "./registry.js";
9
11
  export { builtinEngines, createDefaultRegistry, defaultRegistry } from "./builtins.js";
10
12
  export { isRefSentinel, isTaggedSentinel, makeTaggedSentinel } from "./sentinel.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/templating",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "Telo Templating - Engine registry and shared CEL core for Telo manifests.",
5
5
  "keywords": [
6
6
  "telo",
@@ -36,13 +36,15 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "@marcbachmann/cel-js": "^7.5.3",
39
+ "uuid": "^10.0.0",
39
40
  "yaml": "^2.8.3"
40
41
  },
41
42
  "devDependencies": {
42
43
  "@types/node": "^20.0.0",
44
+ "@types/uuid": "^10.0.0",
43
45
  "typescript": "^5.0.0",
44
46
  "vitest": "^2.1.8",
45
- "@telorun/sdk": "0.16.0"
47
+ "@telorun/sdk": "0.21.0"
46
48
  },
47
49
  "peerDependencies": {
48
50
  "@telorun/sdk": "*"
package/src/builtins.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { celEngine } from "./engines/cel.js";
2
2
  import { literalEngine } from "./engines/literal.js";
3
3
  import { refEngine } from "./engines/ref.js";
4
+ import { sqlEngine } from "./engines/sql.js";
4
5
  import { TemplatingEngineRegistry } from "./registry.js";
5
6
  import type { TemplatingEngine } from "./engine.js";
6
7
 
@@ -10,7 +11,12 @@ import type { TemplatingEngine } from "./engine.js";
10
11
  * agree on which engines exist. Per-host à-la-carte registration would let
11
12
  * a manifest validate clean in one host (e.g. `cel` only) and crash in
12
13
  * another (e.g. `cel + literal`); always ship the same set. */
13
- export const builtinEngines: readonly TemplatingEngine[] = [celEngine, literalEngine, refEngine];
14
+ export const builtinEngines: readonly TemplatingEngine[] = [
15
+ celEngine,
16
+ literalEngine,
17
+ refEngine,
18
+ sqlEngine,
19
+ ];
14
20
 
15
21
  export function createDefaultRegistry(): TemplatingEngineRegistry {
16
22
  const registry = new TemplatingEngineRegistry();