@telorun/templating 0.15.0 → 0.17.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 +1 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/cel/catalog.ts"],"names":[],"mappings":"AAGA;;;0DAG0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAClE,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAClC;AA2CD,MAAM,MAAM,mBAAmB,GAC3B,YAAY,GACZ,MAAM,GACN,MAAM,GACN,QAAQ,GACR,MAAM,GACN,YAAY,GACZ,MAAM,GACN,UAAU,GACV,SAAS,GACT,MAAM,CAAC;AAEX;;;qBAGqB;AACrB,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;0EACsE;IACtE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;yDAIqD;IACrD,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;wBACoB;IACpB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;2EACuE;IACvE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;CACjE;AAED,wEAAwE;AACxE,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;AAwH5D,eAAO,MAAM,aAAa,EAAE,SAAS,cAAc,EAslBlD,CAAC;AAEF,oEAAoE;AACpE,wBAAgB,kBAAkB,IAAI,eAAe,EAAE,CAEtD"}
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/cel/catalog.ts"],"names":[],"mappings":"AAGA;;;0DAG0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAClE,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAClC;AA2CD,MAAM,MAAM,mBAAmB,GAC3B,YAAY,GACZ,MAAM,GACN,MAAM,GACN,QAAQ,GACR,MAAM,GACN,YAAY,GACZ,MAAM,GACN,UAAU,GACV,SAAS,GACT,MAAM,CAAC;AAEX;;;qBAGqB;AACrB,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;0EACsE;IACtE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;yDAIqD;IACrD,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;wBACoB;IACpB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;2EACuE;IACvE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;CACjE;AAED,wEAAwE;AACxE,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;AAwH5D,eAAO,MAAM,aAAa,EAAE,SAAS,cAAc,EA8lBlD,CAAC;AAEF,oEAAoE;AACpE,wBAAgB,kBAAkB,IAAI,eAAe,EAAE,CAEtD"}
@@ -352,17 +352,25 @@ export const CEL_FUNCTIONS = [
352
352
  // already treats `size()` that way, and the alternative teaches an author that
353
353
  // slicing bytes is a different operation from slicing a string.
354
354
  //
355
- // ONE `dyn` signature rather than a concrete overload per element type. The
356
- // concrete set is more precise, but cel-js refuses overloads that overlap, so
357
- // it cannot also carry the `dyn` case — and a step result whose producer
358
- // declares no output type IS `dyn`, which is the common receiver. With only the
359
- // concrete set the checker resolves such a call to whichever overload it tries
360
- // first and pins the result to that type, so slicing an untyped byte buffer
361
- // came back typed `string`. Precision here would be lost at the CEL boundary
362
- // anyway, where type arguments are erased.
355
+ // OVERLOADED BY PARAMETER TYPE, so a wrong receiver is rejected `slice(42, 0,
356
+ // 1)` matches nothing. What cannot ALSO be registered is a `dyn` parameter
357
+ // variant: cel-js refuses overloads that overlap, and a `dyn` first parameter
358
+ // overlaps all three. That is why the RETURN is `dyn` rather than the input's
359
+ // own type. A step result whose producer declares no output type is `dyn`, and
360
+ // with concrete returns the checker resolves such a call to whichever overload
361
+ // was registered first and pins the result to it so slicing an untyped byte
362
+ // buffer came back typed `string`, and a byte consumer downstream reported a
363
+ // mismatch against perfectly valid CEL. Precision on the way out would be lost
364
+ // at the CEL boundary anyway, where type arguments are erased; being wrong
365
+ // about an untyped receiver would not be.
363
366
  {
364
367
  name: "slice",
365
368
  signature: "slice(dyn, int, int): dyn",
369
+ register: [
370
+ "slice(bytes, int, int): dyn",
371
+ "slice(string, int, int): dyn",
372
+ "slice(list, int, int): dyn",
373
+ ],
366
374
  // Not "string": it is the one function over strings, bytes AND lists, and the
367
375
  // category is what groups it in the generated reference — filing it under
368
376
  // strings hides it from the byte and list readers who need it most.
package/dist/engine.d.ts CHANGED
@@ -19,6 +19,18 @@ export interface CompileEnv {
19
19
  export interface AnalyzeEnv {
20
20
  readonly celEnv: Environment;
21
21
  readonly contextSchema: Record<string, unknown> | null;
22
+ /**
23
+ * The caller vouches that `celEnv` declares EVERY name legal at this site, so
24
+ * a root identifier it does not know is undeclared rather than merely
25
+ * unmodelled.
26
+ *
27
+ * Opt-in because the environment is only that complete when someone built it
28
+ * for a specific site: the base environment declares CEL's own type names and
29
+ * nothing else, so checking roots against it would report `variables` itself
30
+ * as unknown. Absent means the check is skipped, which is what every caller
31
+ * did before it existed.
32
+ */
33
+ readonly rootsDeclared?: boolean;
22
34
  }
23
35
  /** A mechanically applicable repair for a diagnostic. `replacement` is the
24
36
  * **whole analyzed source**, corrected — never a fragment — so a consumer can
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD;;wEAEwE;AACxE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED;;;;;;;;;eASe;AACf,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACxD;AAED;;;;;;;;;;;;;0EAa0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;oDAEoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED;;;;yBAIyB;AACzB,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IACrC,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;oDAEgD;IAChD,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;iFAGiF;AACjF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAClD,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;4CAgB4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;wBAEwB;AACxB,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;;2EAKuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B;;;yDAGqD;IACrD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,aAAa,GAAG,OAAO,CAAC;IAElE;;iEAE6D;IAC7D,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,aAAa,CAAC;IAExD;;;;;;;;;;;;uBAYmB;IACnB,UAAU,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,eAAe,EAAE,CAAC;IAExD;;;;;;;;;;;;;;;;;2CAiBuC;IACvC,YAAY,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD;;wEAEwE;AACxE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED;;;;;;;;;eASe;AACf,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACvD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;;;;;;;;;;;0EAa0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;oDAEoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED;;;;yBAIyB;AACzB,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IACrC,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;oDAEgD;IAChD,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;iFAGiF;AACjF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAClD,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;4CAgB4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;wBAEwB;AACxB,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;;2EAKuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B;;;yDAGqD;IACrD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,aAAa,GAAG,OAAO,CAAC;IAElE;;iEAE6D;IAC7D,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,aAAa,CAAC;IAExD;;;;;;;;;;;;uBAYmB;IACnB,UAAU,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,eAAe,EAAE,CAAC;IAExD;;;;;;;;;;;;;;;;;2CAiBuC;IACvC,YAAY,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C"}
@@ -1 +1 @@
1
- {"version":3,"file":"cel.d.ts","sourceRoot":"","sources":["../../src/engines/cel.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAoB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAElG;;;;;;;;;;2BAU2B;AAC3B,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,aAAa,CAgFnF;AAYD;yCACyC;AACzC,eAAO,MAAM,SAAS,EAAE,gBAWvB,CAAC"}
1
+ {"version":3,"file":"cel.d.ts","sourceRoot":"","sources":["../../src/engines/cel.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAoB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAElG;;;;;;;;;;2BAU2B;AAC3B,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,aAAa,CAqGnF;AAYD;yCACyC;AACzC,eAAO,MAAM,SAAS,EAAE,gBAWvB,CAAC"}
@@ -69,6 +69,27 @@ export function analyzeCelExpression(source, env) {
69
69
  });
70
70
  }
71
71
  }
72
+ // An undeclared ROOT identifier. cel-js types an unknown name as `dyn` and
73
+ // accepts it, so `!cel "fff"` type-checked, reached the runtime and resolved
74
+ // to nothing — the one CEL mistake with no static report at all. Member
75
+ // access on a KNOWN root was already covered ("No such key"), which is why a
76
+ // typo one level in was caught and a typo at the root was not.
77
+ //
78
+ // Comprehension variables are not roots: `extractAccessChains` drops the
79
+ // names a `.all(x, …)` binds, so the check never sees them.
80
+ if (env.rootsDeclared) {
81
+ const reported = new Set();
82
+ for (const chain of extractAccessChains(parsed.ast)) {
83
+ const root = chain[0];
84
+ if (!root || reported.has(root) || env.celEnv.hasVariable(root))
85
+ continue;
86
+ reported.add(root);
87
+ out.push({
88
+ code: "CEL_UNKNOWN_IDENTIFIER",
89
+ message: `unknown identifier '${root}' — nothing by that name is in scope here.`,
90
+ });
91
+ }
92
+ }
72
93
  if (env.contextSchema) {
73
94
  const contextSchema = env.contextSchema;
74
95
  for (const chain of extractAccessChains(parsed.ast)) {
@@ -1 +1 @@
1
- {"version":3,"file":"literal.d.ts","sourceRoot":"","sources":["../../src/engines/literal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD;;+CAE+C;AAC/C,eAAO,MAAM,aAAa,EAAE,gBAU3B,CAAC"}
1
+ {"version":3,"file":"literal.d.ts","sourceRoot":"","sources":["../../src/engines/literal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD;;+CAE+C;AAC/C,eAAO,MAAM,aAAa,EAAE,gBAyB3B,CAAC"}
@@ -6,6 +6,20 @@ export const literalEngine = {
6
6
  compile(source) {
7
7
  return source;
8
8
  },
9
+ /**
10
+ * Opaque text is TEXT — the type is a constant of the tag, exactly as an
11
+ * embed's is, not a function of the slot.
12
+ *
13
+ * Declaring nothing put `!literal` in the other category, the one `!cel` and
14
+ * `!ref` belong to, and everything downstream then treated it as producing
15
+ * whatever the slot asked for. So the editor offered it at a boolean
16
+ * predicate — where opaque text cannot satisfy the slot and `Run.Choice` has
17
+ * a runtime error saying so — and the analyzer type-checked one against a
18
+ * slot-shaped placeholder instead of the string it will actually be.
19
+ */
20
+ producedType() {
21
+ return { type: "string" };
22
+ },
9
23
  analyze() {
10
24
  return { diagnostics: [], calls: [] };
11
25
  },
package/dist/index.d.ts CHANGED
@@ -14,5 +14,4 @@ export { builtinEngines, createDefaultRegistry, defaultRegistry, producedTypeOf,
14
14
  export type { AnalyzeEnv, AnalyzeResult, CallSite, CompileEnv, DiagnosticFix, EngineDiagnostic, EngineFileClaim, TemplatingEngine, } from "./engine.js";
15
15
  export { CEL_ENGINE, INCLUDE_BYTES_ENGINE, INCLUDE_ENGINE_NAMES, INCLUDE_TEXT_ENGINE, isIncludeSentinel, isRefSentinel, isTaggedSentinel, makeTaggedSentinel, plainChainOf, type TaggedSentinel, } from "./sentinel.js";
16
16
  export { buildCustomTags, defaultCustomTags } from "./yaml-tags.js";
17
- export { MANIFEST_SCHEMA_URI, ManifestRootSchema, ResourceRefSchema, } from "./manifest-schemas.js";
18
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,WAAW,GACjB,MAAM,sBAAsB,CAAC;AAC9B,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,UAAU,EAAE,iBAAiB,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAAE,kBAAkB,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAEpE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,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,EACL,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,UAAU,EACV,aAAa,EACb,QAAQ,EACR,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AACvB,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,EACL,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,WAAW,GACjB,MAAM,sBAAsB,CAAC;AAC9B,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,UAAU,EAAE,iBAAiB,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAAE,kBAAkB,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAEpE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,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,EACL,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,UAAU,EACV,aAAa,EACb,QAAQ,EACR,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -13,4 +13,3 @@ export { TemplatingEngineRegistry } from "./registry.js";
13
13
  export { builtinEngines, createDefaultRegistry, defaultRegistry, producedTypeOf, } from "./builtins.js";
14
14
  export { CEL_ENGINE, INCLUDE_BYTES_ENGINE, INCLUDE_ENGINE_NAMES, INCLUDE_TEXT_ENGINE, isIncludeSentinel, isRefSentinel, isTaggedSentinel, makeTaggedSentinel, plainChainOf, } from "./sentinel.js";
15
15
  export { buildCustomTags, defaultCustomTags } from "./yaml-tags.js";
16
- export { MANIFEST_SCHEMA_URI, ManifestRootSchema, ResourceRefSchema, } from "./manifest-schemas.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/templating",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "description": "Telo Templating - Engine registry and shared CEL core for Telo manifests.",
5
5
  "keywords": [
6
6
  "telo",
@@ -44,7 +44,7 @@
44
44
  "@types/node": "^20.0.0",
45
45
  "typescript": "^5.0.0",
46
46
  "vitest": "^2.1.8",
47
- "@telorun/sdk": "0.74.0"
47
+ "@telorun/sdk": "0.82.1"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@telorun/sdk": "*"
@@ -424,17 +424,25 @@ export const CEL_FUNCTIONS: readonly CelFunctionDoc[] = [
424
424
  // already treats `size()` that way, and the alternative teaches an author that
425
425
  // slicing bytes is a different operation from slicing a string.
426
426
  //
427
- // ONE `dyn` signature rather than a concrete overload per element type. The
428
- // concrete set is more precise, but cel-js refuses overloads that overlap, so
429
- // it cannot also carry the `dyn` case — and a step result whose producer
430
- // declares no output type IS `dyn`, which is the common receiver. With only the
431
- // concrete set the checker resolves such a call to whichever overload it tries
432
- // first and pins the result to that type, so slicing an untyped byte buffer
433
- // came back typed `string`. Precision here would be lost at the CEL boundary
434
- // anyway, where type arguments are erased.
427
+ // OVERLOADED BY PARAMETER TYPE, so a wrong receiver is rejected `slice(42, 0,
428
+ // 1)` matches nothing. What cannot ALSO be registered is a `dyn` parameter
429
+ // variant: cel-js refuses overloads that overlap, and a `dyn` first parameter
430
+ // overlaps all three. That is why the RETURN is `dyn` rather than the input's
431
+ // own type. A step result whose producer declares no output type is `dyn`, and
432
+ // with concrete returns the checker resolves such a call to whichever overload
433
+ // was registered first and pins the result to it so slicing an untyped byte
434
+ // buffer came back typed `string`, and a byte consumer downstream reported a
435
+ // mismatch against perfectly valid CEL. Precision on the way out would be lost
436
+ // at the CEL boundary anyway, where type arguments are erased; being wrong
437
+ // about an untyped receiver would not be.
435
438
  {
436
439
  name: "slice",
437
440
  signature: "slice(dyn, int, int): dyn",
441
+ register: [
442
+ "slice(bytes, int, int): dyn",
443
+ "slice(string, int, int): dyn",
444
+ "slice(list, int, int): dyn",
445
+ ],
438
446
  // Not "string": it is the one function over strings, bytes AND lists, and the
439
447
  // category is what groups it in the generated reference — filing it under
440
448
  // strings hides it from the byte and list readers who need it most.
package/src/engine.ts CHANGED
@@ -21,6 +21,18 @@ export interface CompileEnv {
21
21
  export interface AnalyzeEnv {
22
22
  readonly celEnv: Environment;
23
23
  readonly contextSchema: Record<string, unknown> | null;
24
+ /**
25
+ * The caller vouches that `celEnv` declares EVERY name legal at this site, so
26
+ * a root identifier it does not know is undeclared rather than merely
27
+ * unmodelled.
28
+ *
29
+ * Opt-in because the environment is only that complete when someone built it
30
+ * for a specific site: the base environment declares CEL's own type names and
31
+ * nothing else, so checking roots against it would report `variables` itself
32
+ * as unknown. Absent means the check is skipped, which is what every caller
33
+ * did before it existed.
34
+ */
35
+ readonly rootsDeclared?: boolean;
24
36
  }
25
37
 
26
38
  /** A mechanically applicable repair for a diagnostic. `replacement` is the
@@ -79,6 +79,27 @@ export function analyzeCelExpression(source: string, env: AnalyzeEnv): AnalyzeRe
79
79
  }
80
80
  }
81
81
 
82
+ // An undeclared ROOT identifier. cel-js types an unknown name as `dyn` and
83
+ // accepts it, so `!cel "fff"` type-checked, reached the runtime and resolved
84
+ // to nothing — the one CEL mistake with no static report at all. Member
85
+ // access on a KNOWN root was already covered ("No such key"), which is why a
86
+ // typo one level in was caught and a typo at the root was not.
87
+ //
88
+ // Comprehension variables are not roots: `extractAccessChains` drops the
89
+ // names a `.all(x, …)` binds, so the check never sees them.
90
+ if (env.rootsDeclared) {
91
+ const reported = new Set<string>();
92
+ for (const chain of extractAccessChains(parsed.ast)) {
93
+ const root = chain[0];
94
+ if (!root || reported.has(root) || env.celEnv.hasVariable(root)) continue;
95
+ reported.add(root);
96
+ out.push({
97
+ code: "CEL_UNKNOWN_IDENTIFIER",
98
+ message: `unknown identifier '${root}' — nothing by that name is in scope here.`,
99
+ });
100
+ }
101
+ }
102
+
82
103
  if (env.contextSchema) {
83
104
  const contextSchema = env.contextSchema as Record<string, any>;
84
105
  for (const chain of extractAccessChains(parsed.ast)) {
@@ -10,6 +10,21 @@ export const literalEngine: TemplatingEngine = {
10
10
  return source;
11
11
  },
12
12
 
13
+ /**
14
+ * Opaque text is TEXT — the type is a constant of the tag, exactly as an
15
+ * embed's is, not a function of the slot.
16
+ *
17
+ * Declaring nothing put `!literal` in the other category, the one `!cel` and
18
+ * `!ref` belong to, and everything downstream then treated it as producing
19
+ * whatever the slot asked for. So the editor offered it at a boolean
20
+ * predicate — where opaque text cannot satisfy the slot and `Run.Choice` has
21
+ * a runtime error saying so — and the analyzer type-checked one against a
22
+ * slot-shaped placeholder instead of the string it will actually be.
23
+ */
24
+ producedType() {
25
+ return { type: "string" };
26
+ },
27
+
13
28
  analyze() {
14
29
  return { diagnostics: [], calls: [] };
15
30
  },
package/src/index.ts CHANGED
@@ -69,8 +69,3 @@ export {
69
69
  type TaggedSentinel,
70
70
  } from "./sentinel.js";
71
71
  export { buildCustomTags, defaultCustomTags } from "./yaml-tags.js";
72
- export {
73
- MANIFEST_SCHEMA_URI,
74
- ManifestRootSchema,
75
- ResourceRefSchema,
76
- } from "./manifest-schemas.js";
@@ -1,122 +0,0 @@
1
- /** Shared JSON-Schema fragments referenced from every module's `telo.yaml`
2
- * via `$ref: "telo://manifest#/$defs/<Name>"`. Lives in the templating
3
- * package (alongside the YAML tag mechanism) because the analyzer cannot
4
- * depend on the kernel — both consume this package, so this is the only
5
- * layer where a single source of truth can sit. The kernel re-exports
6
- * these symbols from its own `manifest-schemas` surface for downstream
7
- * ergonomics. */
8
- /** Schema fragment for a resource-reference slot. The only form a manifest
9
- * author writes is the `!ref <name>` (or `!ref <Alias>.<name>`) YAML tag,
10
- * which parses to a `TaggedSentinel` (engine "ref") whose `source` is the
11
- * bare resource name. In practice module schemas mark a ref slot with a bare
12
- * `x-telo-ref` annotation (plus, where the slot only ever holds a reference,
13
- * `type: object` to reject a stray scalar); the analyzer's reference walker
14
- * reads `x-telo-ref` to look the name up against that constraint, independent
15
- * of this fragment. A slot opts into `$ref:
16
- * "telo://manifest#/$defs/ResourceRef"` only when it wants this exact
17
- * two-branch shape enforced at the AJV layer too — it is not required, and
18
- * slots that also accept an inline value (e.g. `inputType` / `outputType`)
19
- * deliberately do not use it (an inline JSON Schema has no `kind`).
20
- *
21
- * Two `anyOf` branches because the value's shape depends on the phase at
22
- * which it is validated:
23
- *
24
- * 1. The raw `!ref` sentinel — what survives to AJV when a cross-module
25
- * reference can't be resolved in standalone single-file analysis (the
26
- * imported module isn't loaded). `substituteCelFields` deliberately
27
- * keeps the sentinel so this branch matches.
28
- * 2. A resolved reference object — `{kind, name, alias?}` substituted in
29
- * place of a sentinel (or an inline definition `{kind, ...config}`
30
- * reached through a local `$ref` that escapes extraction). Both the
31
- * kernel and the analyzer validate ref slots *after* sentinel
32
- * resolution, so this is the shape AJV usually sees.
33
- *
34
- * The object-form `{kind, name}` reference a user could once type directly
35
- * is gone: a plain object at a ref slot is only ever an inline definition
36
- * or the resolver's own substitution, never an author-written reference.
37
- * That removal is enforced by the analyzer (it rejects an author-written
38
- * `{kind, name}` before normalization), not by this schema — branch 2
39
- * cannot distinguish an author's `{kind, name}` from the resolver's. */
40
- export declare const ResourceRefSchema: {
41
- title: string;
42
- anyOf: ({
43
- type: string;
44
- required: string[];
45
- properties: {
46
- __tagged: {
47
- const: boolean;
48
- };
49
- engine: {
50
- const: string;
51
- };
52
- source: {
53
- type: string;
54
- minLength: number;
55
- };
56
- kind?: undefined;
57
- };
58
- additionalProperties: boolean;
59
- } | {
60
- type: string;
61
- required: string[];
62
- properties: {
63
- kind: {
64
- type: string;
65
- };
66
- __tagged?: undefined;
67
- engine?: undefined;
68
- source?: undefined;
69
- };
70
- additionalProperties: boolean;
71
- })[];
72
- };
73
- /** Stable URI under which the shared manifest root schema is registered
74
- * with module-side AJV instances. Module YAMLs reach the fragments via
75
- * `$ref: "telo://manifest#/$defs/<Name>"`. The URI is the contract;
76
- * the symbol names intentionally omit a host-specific prefix since the
77
- * fragments live in `@telorun/templating` (the only layer both kernel
78
- * and analyzer depend on). */
79
- export declare const MANIFEST_SCHEMA_URI = "telo://manifest";
80
- /** Root schema registered with AJV under `MANIFEST_SCHEMA_URI`. Carries
81
- * `$defs` only — it isn't validated against directly. Adding a new
82
- * shared fragment means putting it under `$defs` here and `$ref`-ing
83
- * it from module schemas. */
84
- export declare const ManifestRootSchema: {
85
- $id: string;
86
- $defs: {
87
- ResourceRef: {
88
- title: string;
89
- anyOf: ({
90
- type: string;
91
- required: string[];
92
- properties: {
93
- __tagged: {
94
- const: boolean;
95
- };
96
- engine: {
97
- const: string;
98
- };
99
- source: {
100
- type: string;
101
- minLength: number;
102
- };
103
- kind?: undefined;
104
- };
105
- additionalProperties: boolean;
106
- } | {
107
- type: string;
108
- required: string[];
109
- properties: {
110
- kind: {
111
- type: string;
112
- };
113
- __tagged?: undefined;
114
- engine?: undefined;
115
- source?: undefined;
116
- };
117
- additionalProperties: boolean;
118
- })[];
119
- };
120
- };
121
- };
122
- //# sourceMappingURL=manifest-schemas.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"manifest-schemas.d.ts","sourceRoot":"","sources":["../src/manifest-schemas.ts"],"names":[],"mappings":"AAAA;;;;;;kBAMkB;AAElB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yEA+ByE;AACzE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoB7B,CAAC;AAGF;;;;;+BAK+B;AAC/B,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD;;;8BAG8B;AAC9B,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAK9B,CAAC"}
@@ -1,77 +0,0 @@
1
- /** Shared JSON-Schema fragments referenced from every module's `telo.yaml`
2
- * via `$ref: "telo://manifest#/$defs/<Name>"`. Lives in the templating
3
- * package (alongside the YAML tag mechanism) because the analyzer cannot
4
- * depend on the kernel — both consume this package, so this is the only
5
- * layer where a single source of truth can sit. The kernel re-exports
6
- * these symbols from its own `manifest-schemas` surface for downstream
7
- * ergonomics. */
8
- /** Schema fragment for a resource-reference slot. The only form a manifest
9
- * author writes is the `!ref <name>` (or `!ref <Alias>.<name>`) YAML tag,
10
- * which parses to a `TaggedSentinel` (engine "ref") whose `source` is the
11
- * bare resource name. In practice module schemas mark a ref slot with a bare
12
- * `x-telo-ref` annotation (plus, where the slot only ever holds a reference,
13
- * `type: object` to reject a stray scalar); the analyzer's reference walker
14
- * reads `x-telo-ref` to look the name up against that constraint, independent
15
- * of this fragment. A slot opts into `$ref:
16
- * "telo://manifest#/$defs/ResourceRef"` only when it wants this exact
17
- * two-branch shape enforced at the AJV layer too — it is not required, and
18
- * slots that also accept an inline value (e.g. `inputType` / `outputType`)
19
- * deliberately do not use it (an inline JSON Schema has no `kind`).
20
- *
21
- * Two `anyOf` branches because the value's shape depends on the phase at
22
- * which it is validated:
23
- *
24
- * 1. The raw `!ref` sentinel — what survives to AJV when a cross-module
25
- * reference can't be resolved in standalone single-file analysis (the
26
- * imported module isn't loaded). `substituteCelFields` deliberately
27
- * keeps the sentinel so this branch matches.
28
- * 2. A resolved reference object — `{kind, name, alias?}` substituted in
29
- * place of a sentinel (or an inline definition `{kind, ...config}`
30
- * reached through a local `$ref` that escapes extraction). Both the
31
- * kernel and the analyzer validate ref slots *after* sentinel
32
- * resolution, so this is the shape AJV usually sees.
33
- *
34
- * The object-form `{kind, name}` reference a user could once type directly
35
- * is gone: a plain object at a ref slot is only ever an inline definition
36
- * or the resolver's own substitution, never an author-written reference.
37
- * That removal is enforced by the analyzer (it rejects an author-written
38
- * `{kind, name}` before normalization), not by this schema — branch 2
39
- * cannot distinguish an author's `{kind, name}` from the resolver's. */
40
- export const ResourceRefSchema = {
41
- title: "Resource reference",
42
- anyOf: [
43
- {
44
- type: "object",
45
- required: ["__tagged", "engine", "source"],
46
- properties: {
47
- __tagged: { const: true },
48
- engine: { const: "ref" },
49
- source: { type: "string", minLength: 1 },
50
- },
51
- additionalProperties: false,
52
- },
53
- {
54
- type: "object",
55
- required: ["kind"],
56
- properties: { kind: { type: "string" } },
57
- additionalProperties: true,
58
- },
59
- ],
60
- };
61
- /** Stable URI under which the shared manifest root schema is registered
62
- * with module-side AJV instances. Module YAMLs reach the fragments via
63
- * `$ref: "telo://manifest#/$defs/<Name>"`. The URI is the contract;
64
- * the symbol names intentionally omit a host-specific prefix since the
65
- * fragments live in `@telorun/templating` (the only layer both kernel
66
- * and analyzer depend on). */
67
- export const MANIFEST_SCHEMA_URI = "telo://manifest";
68
- /** Root schema registered with AJV under `MANIFEST_SCHEMA_URI`. Carries
69
- * `$defs` only — it isn't validated against directly. Adding a new
70
- * shared fragment means putting it under `$defs` here and `$ref`-ing
71
- * it from module schemas. */
72
- export const ManifestRootSchema = {
73
- $id: MANIFEST_SCHEMA_URI,
74
- $defs: {
75
- ResourceRef: ResourceRefSchema,
76
- },
77
- };
@@ -1,81 +0,0 @@
1
- /** Shared JSON-Schema fragments referenced from every module's `telo.yaml`
2
- * via `$ref: "telo://manifest#/$defs/<Name>"`. Lives in the templating
3
- * package (alongside the YAML tag mechanism) because the analyzer cannot
4
- * depend on the kernel — both consume this package, so this is the only
5
- * layer where a single source of truth can sit. The kernel re-exports
6
- * these symbols from its own `manifest-schemas` surface for downstream
7
- * ergonomics. */
8
-
9
- /** Schema fragment for a resource-reference slot. The only form a manifest
10
- * author writes is the `!ref <name>` (or `!ref <Alias>.<name>`) YAML tag,
11
- * which parses to a `TaggedSentinel` (engine "ref") whose `source` is the
12
- * bare resource name. In practice module schemas mark a ref slot with a bare
13
- * `x-telo-ref` annotation (plus, where the slot only ever holds a reference,
14
- * `type: object` to reject a stray scalar); the analyzer's reference walker
15
- * reads `x-telo-ref` to look the name up against that constraint, independent
16
- * of this fragment. A slot opts into `$ref:
17
- * "telo://manifest#/$defs/ResourceRef"` only when it wants this exact
18
- * two-branch shape enforced at the AJV layer too — it is not required, and
19
- * slots that also accept an inline value (e.g. `inputType` / `outputType`)
20
- * deliberately do not use it (an inline JSON Schema has no `kind`).
21
- *
22
- * Two `anyOf` branches because the value's shape depends on the phase at
23
- * which it is validated:
24
- *
25
- * 1. The raw `!ref` sentinel — what survives to AJV when a cross-module
26
- * reference can't be resolved in standalone single-file analysis (the
27
- * imported module isn't loaded). `substituteCelFields` deliberately
28
- * keeps the sentinel so this branch matches.
29
- * 2. A resolved reference object — `{kind, name, alias?}` substituted in
30
- * place of a sentinel (or an inline definition `{kind, ...config}`
31
- * reached through a local `$ref` that escapes extraction). Both the
32
- * kernel and the analyzer validate ref slots *after* sentinel
33
- * resolution, so this is the shape AJV usually sees.
34
- *
35
- * The object-form `{kind, name}` reference a user could once type directly
36
- * is gone: a plain object at a ref slot is only ever an inline definition
37
- * or the resolver's own substitution, never an author-written reference.
38
- * That removal is enforced by the analyzer (it rejects an author-written
39
- * `{kind, name}` before normalization), not by this schema — branch 2
40
- * cannot distinguish an author's `{kind, name}` from the resolver's. */
41
- export const ResourceRefSchema = {
42
- title: "Resource reference",
43
- anyOf: [
44
- {
45
- type: "object",
46
- required: ["__tagged", "engine", "source"],
47
- properties: {
48
- __tagged: { const: true },
49
- engine: { const: "ref" },
50
- source: { type: "string", minLength: 1 },
51
- },
52
- additionalProperties: false,
53
- },
54
- {
55
- type: "object",
56
- required: ["kind"],
57
- properties: { kind: { type: "string" } },
58
- additionalProperties: true,
59
- },
60
- ],
61
- };
62
-
63
-
64
- /** Stable URI under which the shared manifest root schema is registered
65
- * with module-side AJV instances. Module YAMLs reach the fragments via
66
- * `$ref: "telo://manifest#/$defs/<Name>"`. The URI is the contract;
67
- * the symbol names intentionally omit a host-specific prefix since the
68
- * fragments live in `@telorun/templating` (the only layer both kernel
69
- * and analyzer depend on). */
70
- export const MANIFEST_SCHEMA_URI = "telo://manifest";
71
-
72
- /** Root schema registered with AJV under `MANIFEST_SCHEMA_URI`. Carries
73
- * `$defs` only — it isn't validated against directly. Adding a new
74
- * shared fragment means putting it under `$defs` here and `$ref`-ing
75
- * it from module schemas. */
76
- export const ManifestRootSchema = {
77
- $id: MANIFEST_SCHEMA_URI,
78
- $defs: {
79
- ResourceRef: ResourceRefSchema,
80
- },
81
- };