@telorun/analyzer 0.12.0 → 0.13.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 (67) hide show
  1. package/README.md +2 -2
  2. package/dist/analysis-registry.d.ts +12 -0
  3. package/dist/analysis-registry.d.ts.map +1 -1
  4. package/dist/analysis-registry.js +15 -0
  5. package/dist/analyzer.d.ts.map +1 -1
  6. package/dist/analyzer.js +131 -85
  7. package/dist/builtins.d.ts.map +1 -1
  8. package/dist/builtins.js +25 -0
  9. package/dist/cel-environment.d.ts +1 -1
  10. package/dist/cel-environment.d.ts.map +1 -1
  11. package/dist/cel-environment.js +40 -2
  12. package/dist/definition-registry.d.ts +12 -1
  13. package/dist/definition-registry.d.ts.map +1 -1
  14. package/dist/definition-registry.js +20 -1
  15. package/dist/dependency-graph.d.ts.map +1 -1
  16. package/dist/dependency-graph.js +41 -62
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -0
  20. package/dist/kernel-globals.d.ts +1 -1
  21. package/dist/kernel-globals.d.ts.map +1 -1
  22. package/dist/kernel-globals.js +19 -1
  23. package/dist/manifest-visitor.d.ts +109 -0
  24. package/dist/manifest-visitor.d.ts.map +1 -0
  25. package/dist/manifest-visitor.js +110 -0
  26. package/dist/reference-field-map.d.ts +1 -0
  27. package/dist/reference-field-map.d.ts.map +1 -1
  28. package/dist/reference-field-map.js +1 -1
  29. package/dist/schema-compat.d.ts +14 -0
  30. package/dist/schema-compat.d.ts.map +1 -1
  31. package/dist/schema-compat.js +38 -2
  32. package/dist/validate-cel-context.d.ts +14 -0
  33. package/dist/validate-cel-context.d.ts.map +1 -1
  34. package/dist/validate-cel-context.js +38 -0
  35. package/dist/validate-nested-inline.d.ts +30 -0
  36. package/dist/validate-nested-inline.d.ts.map +1 -0
  37. package/dist/validate-nested-inline.js +129 -0
  38. package/dist/validate-references.d.ts.map +1 -1
  39. package/dist/validate-references.js +117 -160
  40. package/dist/validate-unused-declarations.d.ts +25 -0
  41. package/dist/validate-unused-declarations.d.ts.map +1 -0
  42. package/dist/validate-unused-declarations.js +91 -0
  43. package/package.json +2 -2
  44. package/src/analysis-registry.ts +20 -0
  45. package/src/analyzer.ts +217 -158
  46. package/src/builtins.ts +25 -0
  47. package/src/cel-environment.ts +42 -1
  48. package/src/definition-registry.ts +20 -1
  49. package/src/dependency-graph.ts +37 -52
  50. package/src/index.ts +11 -0
  51. package/src/kernel-globals.ts +22 -1
  52. package/src/manifest-visitor.ts +251 -0
  53. package/src/reference-field-map.ts +1 -1
  54. package/src/schema-compat.ts +38 -2
  55. package/src/validate-cel-context.ts +50 -0
  56. package/src/validate-nested-inline.ts +158 -0
  57. package/src/validate-references.ts +168 -211
  58. package/src/validate-unused-declarations.ts +95 -0
  59. package/dist/adapters/http-adapter.d.ts +0 -10
  60. package/dist/adapters/http-adapter.d.ts.map +0 -1
  61. package/dist/adapters/http-adapter.js +0 -18
  62. package/dist/adapters/node-adapter.d.ts +0 -17
  63. package/dist/adapters/node-adapter.d.ts.map +0 -1
  64. package/dist/adapters/node-adapter.js +0 -71
  65. package/dist/adapters/registry-adapter.d.ts +0 -15
  66. package/dist/adapters/registry-adapter.d.ts.map +0 -1
  67. package/dist/adapters/registry-adapter.js +0 -53
package/README.md CHANGED
@@ -61,12 +61,12 @@ targets:
61
61
  kind: Telo.Import
62
62
  metadata:
63
63
  name: Http
64
- source: std/http-server@0.4.0
64
+ source: std/http-server@0.5.0
65
65
  ---
66
66
  kind: Telo.Import
67
67
  metadata:
68
68
  name: Sql
69
- source: std/sql@0.2.3
69
+ source: std/sql@0.3.0
70
70
  ---
71
71
  # SQLite database — swap driver/host/database for PostgreSQL with zero YAML changes
72
72
  kind: Sql.Connection
@@ -1,4 +1,5 @@
1
1
  import type { ResourceDefinition, ResourceManifest } from "@telorun/sdk";
2
+ import { type ManifestVisitor } from "./manifest-visitor.js";
2
3
  import type { AnalysisContext } from "./types.js";
3
4
  /**
4
5
  * Accumulates type and alias knowledge for a running kernel or analysis session.
@@ -22,6 +23,17 @@ export declare class AnalysisRegistry {
22
23
  * sub-schema (e.g. Server.notFoundHandler.returns[].content[mime].encoder).
23
24
  */
24
25
  iterateFieldEntries(resource: ResourceManifest, onRef: (fieldPath: string) => void, onScope: (fieldPath: string) => void): void;
26
+ /**
27
+ * Walks a manifest's annotation sites (refs, scopes, schema-from, CEL) via
28
+ * the shared manifest visitor, bound to this registry's definitions and
29
+ * aliases. The public seam for hosts (editor overview graph, tooling) that
30
+ * need the same site discovery the analyzer's own passes use, without
31
+ * reaching into the internal DefinitionRegistry.
32
+ */
33
+ visitManifest(resources: ResourceManifest[], visitor: ManifestVisitor, opts?: {
34
+ skipKinds?: ReadonlySet<string>;
35
+ expand?: boolean;
36
+ }): void;
25
37
  /**
26
38
  * Returns the built-in kernel definitions. The underlying DefinitionRegistry already
27
39
  * seeds these on construction; this method exposes them so callers (e.g. the kernel's
@@ -1 +1 @@
1
- {"version":3,"file":"analysis-registry.d.ts","sourceRoot":"","sources":["../src/analysis-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAMzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;GAIG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;IAEpE,kBAAkB,CAAC,GAAG,EAAE,kBAAkB,GAAG,IAAI;IAIjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIpE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAIpE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C;;;;;;;OAOG;IACH,mBAAmB,CACjB,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,EAClC,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GACnC,IAAI;IAkBP;;;;OAIG;IACH,kBAAkB,IAAI,kBAAkB,EAAE;IAI1C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAM/D,QAAQ,IAAI,MAAM,EAAE;IAIpB;mEAC+D;IAC/D,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAIxC;;;gCAG4B;IAC5B,oBAAoB,IAAI,MAAM,EAAE;IAIhC;wEACoE;IACpE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIhD;;;;;gEAK4D;IAC5D,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;IA+B7D,qFAAqF;IACrF,QAAQ,IAAI,eAAe;CAG5B"}
1
+ {"version":3,"file":"analysis-registry.d.ts","sourceRoot":"","sources":["../src/analysis-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKzE,OAAO,EAAqC,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEhG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;GAIG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;IAEpE,kBAAkB,CAAC,GAAG,EAAE,kBAAkB,GAAG,IAAI;IAIjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIpE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAIpE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C;;;;;;;OAOG;IACH,mBAAmB,CACjB,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,EAClC,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GACnC,IAAI;IAkBP;;;;;;OAMG;IACH,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,EACxB,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAC3D,IAAI;IAQP;;;;OAIG;IACH,kBAAkB,IAAI,kBAAkB,EAAE;IAI1C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAM/D,QAAQ,IAAI,MAAM,EAAE;IAIpB;mEAC+D;IAC/D,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAIxC;;;gCAG4B;IAC5B,oBAAoB,IAAI,MAAM,EAAE;IAIhC;wEACoE;IACpE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIhD;;;;;gEAK4D;IAC5D,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;IA+B7D,qFAAqF;IACrF,QAAQ,IAAI,eAAe;CAG5B"}
@@ -2,6 +2,7 @@ import { AliasResolver } from "./alias-resolver.js";
2
2
  import { KERNEL_BUILTINS } from "./builtins.js";
3
3
  import { DefinitionRegistry } from "./definition-registry.js";
4
4
  import { computeSuggestKind, computeValidUserFacingKinds } from "./kind-suggest.js";
5
+ import { visitManifest as runVisitManifest } from "./manifest-visitor.js";
5
6
  import { isRefEntry, isScopeEntry } from "./reference-field-map.js";
6
7
  /**
7
8
  * Accumulates type and alias knowledge for a running kernel or analysis session.
@@ -46,6 +47,20 @@ export class AnalysisRegistry {
46
47
  }
47
48
  }
48
49
  }
50
+ /**
51
+ * Walks a manifest's annotation sites (refs, scopes, schema-from, CEL) via
52
+ * the shared manifest visitor, bound to this registry's definitions and
53
+ * aliases. The public seam for hosts (editor overview graph, tooling) that
54
+ * need the same site discovery the analyzer's own passes use, without
55
+ * reaching into the internal DefinitionRegistry.
56
+ */
57
+ visitManifest(resources, visitor, opts) {
58
+ runVisitManifest(resources, this.defs, visitor, {
59
+ aliases: this.aliases,
60
+ aliasesByModule: this.aliasesByModule,
61
+ ...opts,
62
+ });
63
+ }
49
64
  /**
50
65
  * Returns the built-in kernel definitions. The underlying DefinitionRegistry already
51
66
  * seeds these on construction; this method exposes them so callers (e.g. the kernel's
@@ -1 +1 @@
1
- {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAgB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAgf/F,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,OAAO,GAAE,qBAA0B;IAI/C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IA8dvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAMvB,SAAS,CAAC,SAAS,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,EAAE;IAexF,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,GACzB;QAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAsB5F"}
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAiB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA+c/F,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,OAAO,GAAE,qBAA0B;IAI/C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAyjBvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAMvB,SAAS,CAAC,SAAS,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,EAAE;IAexF,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,GACzB;QAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAsB5F"}
package/dist/analyzer.js CHANGED
@@ -1,10 +1,11 @@
1
- import { defaultRegistry, walkCelExpressions } from "@telorun/templating";
1
+ import { defaultRegistry, isTaggedSentinel } from "@telorun/templating";
2
2
  import { AliasResolver } from "./alias-resolver.js";
3
3
  import { buildCelEnvironment, buildTypedCelEnvironment, } from "./cel-environment.js";
4
4
  import { DefinitionRegistry } from "./definition-registry.js";
5
5
  import { buildDependencyGraph, formatCycle } from "./dependency-graph.js";
6
6
  import { buildKernelGlobalsSchema, mergeKernelGlobalsIntoContext } from "./kernel-globals.js";
7
7
  import { computeSuggestKind } from "./kind-suggest.js";
8
+ import { visitManifest } from "./manifest-visitor.js";
8
9
  import { isModuleKind } from "./module-kinds.js";
9
10
  import { normalizeInlineResources } from "./normalize-inline-resources.js";
10
11
  import { REF_VALIDATION_SKIP_KINDS } from "./system-kinds.js";
@@ -12,10 +13,12 @@ import { resolveRefSentinels } from "./resolve-ref-sentinels.js";
12
13
  import { rewriteSyntheticOrigins } from "./rewrite-synthetic-origins.js";
13
14
  import { celTypeSatisfiesJsonSchema, substituteCelFields, validateAgainstSchema, } from "./schema-compat.js";
14
15
  import { DiagnosticSeverity } from "./types.js";
15
- import { getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
16
+ import { extractContextsFromSchema, getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
16
17
  import { validateExtends } from "./validate-extends.js";
18
+ import { validateNestedInlineResources } from "./validate-nested-inline.js";
17
19
  import { validateProviderCoherence } from "./validate-provider-coherence.js";
18
20
  import { validateReferences } from "./validate-references.js";
21
+ import { validateUnusedDeclarations } from "./validate-unused-declarations.js";
19
22
  import { validateThrowsCoverage } from "./validate-throws-coverage.js";
20
23
  const SELF_PREFIX = "Self.";
21
24
  /**
@@ -141,44 +144,6 @@ function manifestRootForResolver(m, defs, aliases, allManifests) {
141
144
  ...(inputs ? { inputType: inputs } : {}),
142
145
  };
143
146
  }
144
- /**
145
- * Walk a JSON Schema tree and collect all `x-telo-context` annotations,
146
- * returning them as `{ scope, schema }` pairs using JSONPath-style scopes —
147
- * the same format the analyzer uses for CEL context validation.
148
- *
149
- * Result is sorted by scope specificity (longer scope first) so that the
150
- * per-expression resolver's first-match-wins logic picks the most-specific
151
- * context. Without this, a broader ancestor scope (e.g. `$.resources[*]`)
152
- * could shadow a narrower descendant scope whose activation differs.
153
- */
154
- function extractContextsFromSchema(schema, path = "$") {
155
- const all = collectContexts(schema, path);
156
- return all.sort((a, b) => b.scope.length - a.scope.length);
157
- }
158
- function collectContexts(schema, path) {
159
- if (!schema || typeof schema !== "object")
160
- return [];
161
- const results = [];
162
- if (schema["x-telo-context"]) {
163
- results.push({ scope: path, schema: schema["x-telo-context"] });
164
- }
165
- if (schema.properties) {
166
- for (const [key, value] of Object.entries(schema.properties)) {
167
- results.push(...collectContexts(value, `${path}.${key}`));
168
- }
169
- }
170
- if (schema.items && typeof schema.items === "object") {
171
- results.push(...collectContexts(schema.items, `${path}[*]`));
172
- }
173
- for (const key of ["oneOf", "anyOf", "allOf"]) {
174
- if (Array.isArray(schema[key])) {
175
- for (const subschema of schema[key]) {
176
- results.push(...collectContexts(subschema, path));
177
- }
178
- }
179
- }
180
- return results;
181
- }
182
147
  /** Resolve a local `$ref` (only `#/$defs/<name>` form) against the root schema.
183
148
  * Non-refs and unresolved refs pass through unchanged. */
184
149
  function resolveLocalRef(schema, root) {
@@ -346,19 +311,30 @@ const CEL_PURE_RE = /^\s*\$\{\{[^}]*\}\}\s*$/;
346
311
  const CEL_EXPR_RE = /\$\{\{\s*([^}]+?)\s*\}\}/;
347
312
  /** Recursively walk `data`+`schema` together, type-checking every pure CEL template
348
313
  * string via `env.check()`. Returns `SchemaIssue[]` for any type mismatches found. */
349
- function collectCelTypeIssues(data, schema, path, definition, manifest, baseTypedEnv, rootEnv) {
314
+ function collectCelTypeIssues(data, schema, path, definition, manifest, baseTypedEnv, rootEnv, rootModuleManifest) {
350
315
  const issues = [];
351
- if (typeof data === "string" && CEL_PURE_RE.test(data)) {
352
- const exprMatch = data.match(CEL_EXPR_RE);
353
- if (exprMatch) {
354
- const expr = exprMatch[1].trim();
316
+ // A pure CEL value type-checks the same regardless of surface form: a
317
+ // `${{ }}` string and a `!cel`-tagged sentinel must behave identically.
318
+ let celExpr;
319
+ if (isTaggedSentinel(data)) {
320
+ // Non-CEL engines (e.g. `!literal`) are analyzed by their own engine pass.
321
+ if (data.engine !== "cel")
322
+ return issues;
323
+ celExpr = data.source;
324
+ }
325
+ else if (typeof data === "string" && CEL_PURE_RE.test(data)) {
326
+ celExpr = data.match(CEL_EXPR_RE)?.[1]?.trim();
327
+ }
328
+ if (celExpr !== undefined) {
329
+ {
330
+ const expr = celExpr;
355
331
  // Merge x-telo-context variables for this path if applicable
356
332
  let typedEnv = baseTypedEnv;
357
333
  if (definition.schema) {
358
334
  for (const ctx of extractContextsFromSchema(definition.schema)) {
359
335
  if (!pathMatchesScope(path, ctx.scope))
360
336
  continue;
361
- typedEnv = buildTypedCelEnvironment(rootEnv, manifest, ctx.schema);
337
+ typedEnv = buildTypedCelEnvironment(rootEnv, manifest, ctx.schema, rootModuleManifest);
362
338
  break;
363
339
  }
364
340
  }
@@ -381,8 +357,9 @@ function collectCelTypeIssues(data, schema, path, definition, manifest, baseType
381
357
  else if (checkResult?.valid && checkResult.type && schema) {
382
358
  const celType = checkResult.type.split("<")[0];
383
359
  if (!celTypeSatisfiesJsonSchema(celType, schema)) {
360
+ const expected = schema["x-telo-type"] ?? schema.type ?? "unknown";
384
361
  issues.push({
385
- message: `CEL returns '${checkResult.type}' but field expects '${schema.type ?? "unknown"}'`,
362
+ message: `CEL returns '${checkResult.type}' but field expects '${expected}'`,
386
363
  path,
387
364
  });
388
365
  }
@@ -393,13 +370,13 @@ function collectCelTypeIssues(data, schema, path, definition, manifest, baseType
393
370
  if (Array.isArray(data)) {
394
371
  const itemSchema = (schema.items ?? {});
395
372
  for (let i = 0; i < data.length; i++) {
396
- issues.push(...collectCelTypeIssues(data[i], itemSchema, `${path}[${i}]`, definition, manifest, baseTypedEnv, rootEnv));
373
+ issues.push(...collectCelTypeIssues(data[i], itemSchema, `${path}[${i}]`, definition, manifest, baseTypedEnv, rootEnv, rootModuleManifest));
397
374
  }
398
375
  }
399
376
  else if (data !== null && typeof data === "object") {
400
377
  const props = (schema.properties ?? {});
401
378
  for (const [k, v] of Object.entries(data)) {
402
- issues.push(...collectCelTypeIssues(v, (props[k] ?? {}), path ? `${path}.${k}` : k, definition, manifest, baseTypedEnv, rootEnv));
379
+ issues.push(...collectCelTypeIssues(v, (props[k] ?? {}), path ? `${path}.${k}` : k, definition, manifest, baseTypedEnv, rootEnv, rootModuleManifest));
403
380
  }
404
381
  }
405
382
  return issues;
@@ -563,6 +540,34 @@ export class StaticAnalyzer {
563
540
  byName.set(m.metadata.name, m);
564
541
  }
565
542
  }
543
+ // Fail loud on definition schemas AJV cannot compile. `validateAgainstSchema`
544
+ // and `validateWithRefs` swallow compile failures (returning no issues),
545
+ // which would silently skip schema validation for every resource of that
546
+ // kind — surface the broken schema once, anchored on the definition itself.
547
+ for (const m of allManifests) {
548
+ if (m.kind !== "Telo.Definition" && m.kind !== "Telo.Abstract")
549
+ continue;
550
+ const schema = m.schema;
551
+ if (!schema || typeof schema !== "object")
552
+ continue;
553
+ const name = m.metadata?.name;
554
+ if (!name)
555
+ continue;
556
+ const compileError = defs.schemaCompileError(schema);
557
+ if (compileError) {
558
+ diagnostics.push({
559
+ severity: DiagnosticSeverity.Error,
560
+ code: "SCHEMA_COMPILE_ERROR",
561
+ source: SOURCE,
562
+ message: `${m.kind}/${name}: definition schema failed to compile: ${compileError}`,
563
+ data: {
564
+ resource: { kind: m.kind, name },
565
+ filePath: m.metadata?.source,
566
+ path: "schema",
567
+ },
568
+ });
569
+ }
570
+ }
566
571
  // Library env: rejection — `env:` on a Library `variables` / `secrets`
567
572
  // entry is forbidden. The Library entry schema is otherwise open so that
568
573
  // any JSON Schema property schema is valid; this targeted check produces
@@ -596,6 +601,11 @@ export class StaticAnalyzer {
596
601
  // Build typed kernel globals schema so x-telo-context chain validation
597
602
  // recognises variables, secrets, resources, env automatically
598
603
  const kernelGlobals = buildKernelGlobalsSchema(allManifests);
604
+ // The module doc (Application/Library) carries the Application-only `ports`
605
+ // namespace; threaded into per-resource CEL typing so `${{ ports.X }}`
606
+ // resolves its nominal brand cross-doc.
607
+ const moduleManifest = allManifests.find((mm) => mm.kind === "Telo.Application") ??
608
+ allManifests.find((mm) => mm.kind === "Telo.Library");
599
609
  // Validate each non-definition, non-system resource
600
610
  for (const m of allManifests) {
601
611
  const filePath = m.metadata?.source;
@@ -649,8 +659,8 @@ export class StaticAnalyzer {
649
659
  }
650
660
  : definition.schema;
651
661
  // Phase 1: CEL type checking — walk data+schema together, check env.check() return types
652
- const baseTypedEnv = buildTypedCelEnvironment(this.celEnv, m);
653
- const celIssues = collectCelTypeIssues(m, schema, "", definition, m, baseTypedEnv, this.celEnv);
662
+ const baseTypedEnv = buildTypedCelEnvironment(this.celEnv, m, undefined, moduleManifest);
663
+ const celIssues = collectCelTypeIssues(m, schema, "", definition, m, baseTypedEnv, this.celEnv, moduleManifest);
654
664
  // Phase 2+3: AJV on substituted data — CEL fields replaced with typed placeholders
655
665
  const ajvIssues = validateAgainstSchema(substituteCelFields(m, schema), schema);
656
666
  const issues = [...celIssues, ...ajvIssues];
@@ -664,6 +674,33 @@ export class StaticAnalyzer {
664
674
  });
665
675
  }
666
676
  }
677
+ // Validate inline resources nested inside this resource's body (e.g. a
678
+ // Run.Sequence step's `invoke: { kind, ...config }`). These sit at
679
+ // x-telo-ref slots reached only through local `$ref`s, which the
680
+ // reference field map intentionally does not follow, so they escape both
681
+ // inline-extraction and the per-resource schema check above.
682
+ if (definition.schema) {
683
+ // Resolve inline kinds in the parent resource's scope: direct kind
684
+ // first, then the parent module's own aliases (for resources declared
685
+ // inside an imported module), then the root aliases. Mirrors how the
686
+ // analyzer resolves kinds elsewhere so module-scoped aliases don't
687
+ // produce false UNDEFINED_KIND diagnostics.
688
+ const ownModule = m.metadata?.module;
689
+ const scopeResolver = ownModule && !rootModules.has(ownModule) ? aliasesByModule.get(ownModule) : undefined;
690
+ diagnostics.push(...validateNestedInlineResources(m, definition.schema, (kind) => {
691
+ const direct = defs.resolve(kind);
692
+ if (direct)
693
+ return direct;
694
+ const viaScope = scopeResolver?.resolveKind(kind);
695
+ if (viaScope) {
696
+ const scoped = defs.resolve(viaScope);
697
+ if (scoped)
698
+ return scoped;
699
+ }
700
+ const viaRoot = aliases.resolveKind(kind);
701
+ return viaRoot ? defs.resolve(viaRoot) : undefined;
702
+ }));
703
+ }
667
704
  // (Invocation context compatibility check is handled via x-telo-context in the CEL pass below)
668
705
  }
669
706
  // Template-body structural validations: check that template entry-points produce
@@ -735,42 +772,39 @@ export class StaticAnalyzer {
735
772
  }
736
773
  }
737
774
  }
738
- // Validate CEL syntax and context variable access in all manifests
739
- for (const m of allManifests) {
740
- const resource = { kind: m.kind, name: m.metadata?.name };
741
- const filePath = m.metadata?.source;
742
- const resolvedKind = aliases.resolveKind(m.kind);
743
- const mDefinition = defs.resolve(m.kind) ?? (resolvedKind ? defs.resolve(resolvedKind) : undefined);
744
- // Pre-compute step context for manifests with x-telo-step-context
745
- const stepContextSchema = mDefinition?.schema
746
- ? buildStepContextSchema(m, mDefinition.schema, allManifests, defs, aliases)
747
- : undefined;
748
- walkCelExpressions(m, "", (expr, path, engineName) => {
749
- // Resolve the effective context for this expression's path. The
750
- // engine receives a single closed schema and validates member-access
751
- // chains against it; per-path resolution (step context, x-telo-context,
752
- // kernel-globals merge) stays on the analyzer side because it depends
753
- // on analyzer-internal state (definitions, aliases).
754
- const contexts = mDefinition?.schema ? extractContextsFromSchema(mDefinition.schema) : [];
755
- const invocationContext = m.metadata?.xTeloInvocationContext;
756
- let matchedContext;
757
- let matchedScope;
758
- for (const ctx of contexts) {
759
- if (pathMatchesScope(path, ctx.scope)) {
760
- matchedContext = ctx.schema;
761
- matchedScope = ctx.scope;
762
- break;
763
- }
764
- }
765
- if (!matchedContext)
766
- matchedContext = invocationContext;
767
- if (stepContextSchema) {
775
+ // Validate CEL syntax and context variable access in all manifests. The
776
+ // walker discovers every compiled CEL node by scanning the value tree and
777
+ // hands back the `x-telo-context` schema matched at the enclosing path; the
778
+ // per-path resolution (step context, kernel-globals merge, x-telo-context-*
779
+ // annotation resolution) stays here because it depends on analyzer-internal
780
+ // state (definitions, aliases, the typed CEL env).
781
+ // Per-resource state computed at enter and read by that resource's CEL
782
+ // sites. The manifest / resource / filePath come straight off each CelSite's
783
+ // `source` (no need to capture them); only the derived step / invocation
784
+ // context — which require analyzer state to build — are stashed here.
785
+ let celStepContextSchema;
786
+ let celInvocationContext;
787
+ visitManifest(allManifests, defs, {
788
+ onResourceEnter: (e) => {
789
+ const m = e.source;
790
+ celInvocationContext = m.metadata?.xTeloInvocationContext;
791
+ celStepContextSchema = e.definition?.schema
792
+ ? buildStepContextSchema(m, e.definition.schema, allManifests, defs, aliases)
793
+ : undefined;
794
+ },
795
+ onCel: (e) => {
796
+ const m = e.source;
797
+ const resource = { kind: m.kind, name: m.metadata?.name };
798
+ const filePath = m.metadata?.source;
799
+ const { expr, path, engineName, matchedScope } = e;
800
+ let matchedContext = e.contextSchema ?? celInvocationContext;
801
+ if (celStepContextSchema) {
768
802
  const base = matchedContext ?? { type: "object", properties: {}, additionalProperties: true };
769
803
  matchedContext = {
770
804
  ...base,
771
805
  properties: {
772
806
  ...(base.properties ?? {}),
773
- steps: stepContextSchema,
807
+ steps: celStepContextSchema,
774
808
  },
775
809
  };
776
810
  }
@@ -789,8 +823,18 @@ export class StaticAnalyzer {
789
823
  effectiveContext = mergeKernelGlobalsIntoContext(resolvedContext, kernelGlobals);
790
824
  }
791
825
  const engine = defaultRegistry().get(engineName);
792
- if (!engine)
826
+ if (!engine) {
827
+ // No registered engine owns this tag — the expression would go
828
+ // entirely unanalyzed. Surface it rather than skipping silently.
829
+ diagnostics.push({
830
+ severity: DiagnosticSeverity.Error,
831
+ code: "UNKNOWN_ENGINE",
832
+ source: SOURCE,
833
+ message: `${m.kind}/${resource.name}: no templating engine registered for '!${engineName}' at '${path}'.`,
834
+ data: { resource, filePath, path },
835
+ });
793
836
  return;
837
+ }
794
838
  const findings = engine.analyze(expr, { celEnv: this.celEnv, contextSchema: effectiveContext });
795
839
  for (const f of findings) {
796
840
  if (f.code === "CEL_SYNTAX_ERROR") {
@@ -824,8 +868,8 @@ export class StaticAnalyzer {
824
868
  });
825
869
  }
826
870
  }
827
- });
828
- }
871
+ },
872
+ }, { aliases });
829
873
  // Validate resource references (Phase 3)
830
874
  diagnostics.push(...validateReferences(allManifests, { aliases, definitions: defs, aliasesByModule }));
831
875
  // Validate `extends` fields and flag legacy `capability: <UserAbstract>` overload.
@@ -834,6 +878,8 @@ export class StaticAnalyzer {
834
878
  diagnostics.push(...validateProviderCoherence(allManifests, defs, aliases));
835
879
  // Validate throws: declarations and catches: coverage (rules 1, 2, 4, 7)
836
880
  diagnostics.push(...validateThrowsCoverage(allManifests, defs, aliases, this.celEnv));
881
+ // Warn about declared variables / secrets / ports that no CEL references.
882
+ diagnostics.push(...validateUnusedDeclarations(allManifests, this.celEnv));
837
883
  // Reroute diagnostics on synthetic (inline-extracted) resources back to
838
884
  // the chain root so position-index lookups land on the parent doc.
839
885
  return rewriteSyntheticOrigins(diagnostics, allManifests);
@@ -1 +1 @@
1
- {"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,EAiU/C,CAAC"}
1
+ {"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,EA0V/C,CAAC"}
package/dist/builtins.js CHANGED
@@ -277,6 +277,31 @@ export const KERNEL_BUILTINS = [
277
277
  },
278
278
  },
279
279
  },
280
+ // Inbound ports the Application listens on. A name-keyed map mirroring
281
+ // `variables`: each entry binds a host env var (`env:`) that supplies a
282
+ // port integer (implicitly typed `integer`, 1–65535), with an optional
283
+ // `default:` used when the env var is unset. `protocol:` (default `tcp`)
284
+ // selects the transport — the runner reads this list to know the
285
+ // exposed ports before launch, and the analyzer brands the resolved
286
+ // `ports.<name>` value (tcp → TcpPort, udp → UdpPort) for static wiring
287
+ // checks. Application-only. See kernel/nodejs/src/application-env.ts.
288
+ ports: {
289
+ type: "object",
290
+ additionalProperties: {
291
+ type: "object",
292
+ required: ["env"],
293
+ properties: {
294
+ env: { type: "string" },
295
+ protocol: {
296
+ type: "string",
297
+ enum: ["tcp", "udp"],
298
+ default: "tcp",
299
+ },
300
+ default: { type: "integer", minimum: 1, maximum: 65535 },
301
+ },
302
+ additionalProperties: false,
303
+ },
304
+ },
280
305
  },
281
306
  required: ["metadata"],
282
307
  additionalProperties: false,
@@ -12,5 +12,5 @@ export type { CelHandlers } from "@telorun/templating";
12
12
  *
13
13
  * NOTE: The set of kernel globals registered here must match `KERNEL_GLOBAL_NAMES`
14
14
  * in kernel-globals.ts, which is used for chain-access validation. */
15
- export declare function buildTypedCelEnvironment(baseEnv: Environment, manifest: ResourceManifest, extraContextSchema?: Record<string, any> | null): Environment;
15
+ export declare function buildTypedCelEnvironment(baseEnv: Environment, manifest: ResourceManifest, extraContextSchema?: Record<string, any> | null, rootModuleManifest?: ResourceManifest): Environment;
16
16
  //# sourceMappingURL=cel-environment.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cel-environment.d.ts","sourceRoot":"","sources":["../src/cel-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;uEASuE;AACvE,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAC9C,WAAW,CAyCb"}
1
+ {"version":3,"file":"cel-environment.d.ts","sourceRoot":"","sources":["../src/cel-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAUrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;uEASuE;AACvE,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAI/C,kBAAkB,CAAC,EAAE,gBAAgB,GACpC,WAAW,CAuEb"}
@@ -1,4 +1,10 @@
1
- import { jsonSchemaToCelType } from "./schema-compat.js";
1
+ import { jsonSchemaToCelType, VALUE_BRAND_BASE } from "./schema-compat.js";
2
+ /** Transport protocol on a `ports` entry → the nominal CEL brand its resolved
3
+ * value carries. Mirrors the `protocol` enum in the Application schema. */
4
+ const PORT_PROTOCOL_BRAND = {
5
+ tcp: "TcpPort",
6
+ udp: "UdpPort",
7
+ };
2
8
  export { buildCelEnvironment } from "@telorun/templating";
3
9
  /** Clone `baseEnv` and register typed variable declarations so that
4
10
  * `env.check(expr)` can infer return types for expressions referencing known variables.
@@ -10,9 +16,21 @@ export { buildCelEnvironment } from "@telorun/templating";
10
16
  *
11
17
  * NOTE: The set of kernel globals registered here must match `KERNEL_GLOBAL_NAMES`
12
18
  * in kernel-globals.ts, which is used for chain-access validation. */
13
- export function buildTypedCelEnvironment(baseEnv, manifest, extraContextSchema) {
19
+ export function buildTypedCelEnvironment(baseEnv, manifest, extraContextSchema,
20
+ // The `ports` namespace is Application-only and lives on the module doc, not
21
+ // on the resource being analyzed. When validating a resource, the caller
22
+ // passes the module manifest here so `${{ ports.X }}` types cross-doc.
23
+ rootModuleManifest) {
14
24
  try {
15
25
  const env = baseEnv.clone();
26
+ // Register nominal value brands (TcpPort/UdpPort/…) on the *clone* so the
27
+ // type-checker can distinguish structurally-identical values. The base env
28
+ // (shared with the kernel runtime) is untouched — a branded value flows as
29
+ // a plain integer at runtime, so only static checking needs these. cel-js
30
+ // auto-generates a field-less wrapper class; no runtime constructor needed.
31
+ for (const brand of Object.keys(VALUE_BRAND_BASE)) {
32
+ env.registerType(brand, { fields: {} });
33
+ }
16
34
  // Build typed ObjectSchema from manifest.variables if it looks like a schema map
17
35
  const vars = manifest.variables;
18
36
  if (vars !== null && typeof vars === "object" && !Array.isArray(vars)) {
@@ -31,6 +49,26 @@ export function buildTypedCelEnvironment(baseEnv, manifest, extraContextSchema)
31
49
  else {
32
50
  env.registerVariable("variables", "map");
33
51
  }
52
+ // `ports` namespace: each entry types as the brand its `protocol` selects
53
+ // (tcp → TcpPort, udp → UdpPort), so `${{ ports.http }}` carries a nominal
54
+ // type that consuming fields can check against.
55
+ const portsManifest = (rootModuleManifest ?? manifest).ports;
56
+ if (portsManifest !== null && typeof portsManifest === "object" && !Array.isArray(portsManifest)) {
57
+ const portEntries = Object.entries(portsManifest).filter(([, v]) => v !== null && typeof v === "object" && !Array.isArray(v));
58
+ if (portEntries.length > 0) {
59
+ const schema = {};
60
+ for (const [k, v] of portEntries) {
61
+ schema[k] = PORT_PROTOCOL_BRAND[v.protocol ?? "tcp"] ?? "int";
62
+ }
63
+ env.registerVariable({ name: "ports", schema });
64
+ }
65
+ else {
66
+ env.registerVariable("ports", "map");
67
+ }
68
+ }
69
+ else {
70
+ env.registerVariable("ports", "map");
71
+ }
34
72
  env.registerVariable("secrets", "map");
35
73
  env.registerVariable("resources", "map");
36
74
  env.registerVariable("env", "map");
@@ -31,8 +31,19 @@ export declare class DefinitionRegistry {
31
31
  * Returns undefined when the module identity is not yet registered. */
32
32
  computeId(moduleName: string, typeName: string): string | undefined;
33
33
  /** Validates data against a schema using this registry's AJV instance, which has all
34
- * registered definition schemas loaded — enabling cross-module $ref resolution. */
34
+ * registered definition schemas loaded — enabling cross-module $ref resolution.
35
+ * A compile failure returns `[]` here; it is surfaced loudly (once, on the
36
+ * owning definition) by `schemaCompileError` via the analyzer's
37
+ * definition-schema compile check, so resources are never silently skipped. */
35
38
  validateWithRefs(data: unknown, schema: Record<string, any>): string[];
39
+ /** Returns the AJV compile error for `schema`, or `undefined` when it compiles.
40
+ * Compiles on this registry's instance, which has every loaded module schema
41
+ * plus the manifest root registered, so local `#/$defs`, `telo://manifest`,
42
+ * and cross-module `$ref`s all resolve. Used to fail loud on a definition
43
+ * schema that AJV cannot compile — otherwise `validateAgainstSchema` /
44
+ * `validateWithRefs` would swallow the failure and silently skip every
45
+ * resource of that kind. */
46
+ schemaCompileError(schema: Record<string, any>): string | undefined;
36
47
  private tryRegisterSchema;
37
48
  /** Resolves an x-telo-ref string to a canonical registry kind key.
38
49
  * Splits on "#", looks up the left side in the identity table, and returns
@@ -1 +1 @@
1
- {"version":3,"file":"definition-registry.d.ts","sourceRoot":"","sources":["../src/definition-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAC;AAGlC,+EAA+E;AAC/E,qBAAa,kBAAkB;;IAK7B;;sFAEkF;IAClF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IAEzD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;IAClE,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D;6DACyD;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD;;0EAEsE;IACtE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA6B;IAEhE,QAAQ,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI;IAiC9C,OAAO,CAAC,aAAa;IASrB;;;yFAGqF;IACrF,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IA+B1E;4EACwE;IACxE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMnE;wFACoF;IACpF,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE;IAWtE,OAAO,CAAC,iBAAiB;IAczB;;;;;;;;4FAQwF;IACxF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAUhD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAIrD,+FAA+F;IAC/F,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIxD,gGAAgG;IAChG,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,GACvD,iBAAiB,GAAG,SAAS;IAOhC;;;;;;;;qFAQiF;IACjF,2BAA2B,CACzB,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,aAAa,EACtB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC1C,iBAAiB,GAAG,SAAS;IAuBhC,OAAO,CAAC,uBAAuB;IA+B/B;;qEAEiE;IACjE,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAgBxD,KAAK,IAAI,MAAM,EAAE;CAGlB"}
1
+ {"version":3,"file":"definition-registry.d.ts","sourceRoot":"","sources":["../src/definition-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAC;AAGlC,+EAA+E;AAC/E,qBAAa,kBAAkB;;IAK7B;;sFAEkF;IAClF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IAEzD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;IAClE,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D;6DACyD;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD;;0EAEsE;IACtE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA6B;IAEhE,QAAQ,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI;IAiC9C,OAAO,CAAC,aAAa;IASrB;;;yFAGqF;IACrF,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IA+B1E;4EACwE;IACxE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMnE;;;;oFAIgF;IAChF,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE;IAWtE;;;;;;iCAM6B;IAC7B,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,GAAG,SAAS;IASnE,OAAO,CAAC,iBAAiB;IAczB;;;;;;;;4FAQwF;IACxF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAUhD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAIrD,+FAA+F;IAC/F,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIxD,gGAAgG;IAChG,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,GACvD,iBAAiB,GAAG,SAAS;IAOhC;;;;;;;;qFAQiF;IACjF,2BAA2B,CACzB,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,aAAa,EACtB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC1C,iBAAiB,GAAG,SAAS;IAuBhC,OAAO,CAAC,uBAAuB;IA+B/B;;qEAEiE;IACjE,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAgBxD,KAAK,IAAI,MAAM,EAAE;CAGlB"}
@@ -105,7 +105,10 @@ export class DefinitionRegistry {
105
105
  return `${identity}/${typeName}`;
106
106
  }
107
107
  /** Validates data against a schema using this registry's AJV instance, which has all
108
- * registered definition schemas loaded — enabling cross-module $ref resolution. */
108
+ * registered definition schemas loaded — enabling cross-module $ref resolution.
109
+ * A compile failure returns `[]` here; it is surfaced loudly (once, on the
110
+ * owning definition) by `schemaCompileError` via the analyzer's
111
+ * definition-schema compile check, so resources are never silently skipped. */
109
112
  validateWithRefs(data, schema) {
110
113
  let validate;
111
114
  try {
@@ -118,6 +121,22 @@ export class DefinitionRegistry {
118
121
  return [];
119
122
  return (validate.errors ?? []).map(formatSingleError);
120
123
  }
124
+ /** Returns the AJV compile error for `schema`, or `undefined` when it compiles.
125
+ * Compiles on this registry's instance, which has every loaded module schema
126
+ * plus the manifest root registered, so local `#/$defs`, `telo://manifest`,
127
+ * and cross-module `$ref`s all resolve. Used to fail loud on a definition
128
+ * schema that AJV cannot compile — otherwise `validateAgainstSchema` /
129
+ * `validateWithRefs` would swallow the failure and silently skip every
130
+ * resource of that kind. */
131
+ schemaCompileError(schema) {
132
+ try {
133
+ this.ajv.compile(schema);
134
+ return undefined;
135
+ }
136
+ catch (err) {
137
+ return err instanceof Error ? err.message : String(err);
138
+ }
139
+ }
121
140
  tryRegisterSchema(moduleName, typeName, schema) {
122
141
  const id = this.computeId(moduleName, typeName);
123
142
  if (!id || this.registeredSchemaIds.has(id))
@@ -1 +1 @@
1
- {"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../src/dependency-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAInE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B;kDAC8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACpC;oFACgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACrC;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,eAAe,CAkHjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,MAAM,CAOtE"}
1
+ {"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../src/dependency-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAInE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B;kDAC8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACpC;oFACgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACrC;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,eAAe,CAmGjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,MAAM,CAOtE"}