@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
@@ -1,5 +1,5 @@
1
1
  import { isRefSentinel } from "@telorun/templating";
2
- import { isRefEntry, isScopeEntry, resolveFieldValues } from "./reference-field-map.js";
2
+ import { visitManifest } from "./manifest-visitor.js";
3
3
  import { DEPENDENCY_GRAPH_SKIP_KINDS as SYSTEM_KINDS } from "./system-kinds.js";
4
4
  const nodeKey = (kind, name) => `${kind}\0${name}`;
5
5
  /**
@@ -33,68 +33,47 @@ export function buildDependencyGraph(resources, registry, aliases, aliasesByModu
33
33
  const deps = new Map();
34
34
  for (const key of nodes.keys())
35
35
  deps.set(key, new Set());
36
- for (const r of resources) {
37
- if (!r.metadata?.name || !r.kind || SYSTEM_KINDS.has(r.kind))
38
- continue;
39
- const sourceKey = nodeKey(r.kind, r.metadata.name);
40
- // Use the expanded map so refs nested behind x-telo-schema-from contribute
41
- // edges to the DAG. Without these, a parent (e.g. Http.Server) can init
42
- // before its extracted encoder and Phase 5 injection fires against a
43
- // not-yet-created dependency.
44
- const fieldMap = aliases && aliasesByModule
45
- ? registry.expandedFieldMapForResource(r, aliases, aliasesByModule)
46
- : registry.getFieldMapForKind(r.kind, aliases);
47
- if (!fieldMap)
48
- continue;
49
- // Collect names of resources declared inside scope fields — these are initialized
50
- // on-demand at runtime, not at boot, so edges pointing to them are excluded from the DAG.
51
- const scopedNames = new Set();
52
- for (const [scopeFieldPath, entry] of fieldMap) {
53
- if (!isScopeEntry(entry))
54
- continue;
55
- const scopeVal = r[scopeFieldPath];
56
- if (!Array.isArray(scopeVal))
57
- continue;
58
- for (const item of scopeVal) {
59
- const name = item?.metadata?.name;
60
- if (typeof name === "string")
61
- scopedNames.add(name);
36
+ // Names of resources declared inside the *current* resource's scope fields —
37
+ // initialized on-demand at runtime, not at boot, so edges pointing to them
38
+ // are excluded. Scoping is per-source-resource: an edge A → B is dropped only
39
+ // when B is declared inside A's own scope (the visitor's ScopeBoundary fires
40
+ // before that resource's RefSites, so this is set before any edge is added).
41
+ let scopedNames = new Set();
42
+ // Expanded map so refs nested behind x-telo-schema-from contribute edges to
43
+ // the DAG. Without these, a parent (e.g. Http.Server) can init before its
44
+ // extracted encoder and Phase 5 injection fires against a not-yet-created
45
+ // dependency.
46
+ visitManifest(resources, registry, {
47
+ onScope: (e) => {
48
+ scopedNames = e.enclosedNames;
49
+ },
50
+ onRef: (e) => {
51
+ const sourceKey = nodeKey(e.source.kind, e.source.metadata.name);
52
+ const val = e.value;
53
+ // `!ref <name>` sentinel — look up the target's kind from the name
54
+ // (resources are unique by name) so the edge carries the concrete kind,
55
+ // matching the {kind, name} edge shape below.
56
+ if (isRefSentinel(val)) {
57
+ const refName = val.source;
58
+ if (scopedNames.has(refName))
59
+ return;
60
+ const node = nodesByName.get(refName);
61
+ if (node)
62
+ deps.get(sourceKey).add(nodeKey(node.kind, node.name));
63
+ return;
62
64
  }
63
- }
64
- for (const [fieldPath, entry] of fieldMap) {
65
- if (!isRefEntry(entry))
66
- continue;
67
- for (const val of resolveFieldValues(r, fieldPath)) {
68
- if (!val)
69
- continue;
70
- // `!ref <name>` sentinel — look up the target's kind from the
71
- // name (resources are unique by name) so the edge carries the
72
- // concrete kind, matching the {kind, name} edge shape below.
73
- if (isRefSentinel(val)) {
74
- const refName = val.source;
75
- if (scopedNames.has(refName))
76
- continue;
77
- const node = nodesByName.get(refName);
78
- if (node) {
79
- deps.get(sourceKey).add(nodeKey(node.kind, node.name));
80
- }
81
- continue;
82
- }
83
- if (typeof val !== "object")
84
- continue;
85
- const ref = val;
86
- if (!ref.kind || !ref.name)
87
- continue;
88
- // Edges to scoped resources are runtime deps, not boot-time deps — exclude from DAG
89
- if (scopedNames.has(ref.name))
90
- continue;
91
- const targetKey = nodeKey(ref.kind, ref.name);
92
- if (nodes.has(targetKey)) {
93
- deps.get(sourceKey).add(targetKey);
94
- }
95
- }
96
- }
97
- }
65
+ if (typeof val !== "object")
66
+ return;
67
+ const ref = val;
68
+ if (!ref.kind || !ref.name)
69
+ return;
70
+ if (scopedNames.has(ref.name))
71
+ return;
72
+ const targetKey = nodeKey(ref.kind, ref.name);
73
+ if (nodes.has(targetKey))
74
+ deps.get(sourceKey).add(targetKey);
75
+ },
76
+ }, { aliases, aliasesByModule, skipKinds: SYSTEM_KINDS, expand: true });
98
77
  // --- Kahn's topological sort ---
99
78
  // in-degree[X] = number of X's dependencies (size of deps[X])
100
79
  // reverse[dep] = set of nodes that depend on dep (for degree decrement)
package/dist/index.d.ts CHANGED
@@ -2,6 +2,8 @@ export { AnalysisRegistry } from "./analysis-registry.js";
2
2
  export { StaticAnalyzer } from "./analyzer.js";
3
3
  export type { GraphLoadError, ImportEdge, LoadedFile, LoadedGraph, LoadedModule, ParseError, } from "./loaded-types.js";
4
4
  export { flattenForAnalyzer, flattenLoadedModule } from "./flatten-for-analyzer.js";
5
+ export { visitManifest } from "./manifest-visitor.js";
6
+ export type { CelSiteEvent, ManifestVisitor, RefSiteEvent, ResourceEnterEvent, ResourceExitEvent, ScopeBoundaryEvent, SchemaFromSiteEvent, VisitOptions, } from "./manifest-visitor.js";
5
7
  export { Loader } from "./manifest-loader.js";
6
8
  export { isModuleKind, MODULE_KINDS } from "./module-kinds.js";
7
9
  export type { ModuleKind } from "./module-kinds.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EACR,cAAc,EACd,UAAU,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,UAAU,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EACH,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC3E,YAAY,EACR,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,QAAQ,EACR,aAAa,EACb,KAAK,EACR,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EACR,cAAc,EACd,UAAU,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,UAAU,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,YAAY,EACR,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EACH,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC3E,YAAY,EACR,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,QAAQ,EACR,aAAa,EACb,KAAK,EACR,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export { AnalysisRegistry } from "./analysis-registry.js";
2
2
  export { StaticAnalyzer } from "./analyzer.js";
3
3
  export { flattenForAnalyzer, flattenLoadedModule } from "./flatten-for-analyzer.js";
4
+ export { visitManifest } from "./manifest-visitor.js";
4
5
  export { Loader } from "./manifest-loader.js";
5
6
  export { isModuleKind, MODULE_KINDS } from "./module-kinds.js";
6
7
  export { parseLoadedFile } from "./parse-loaded-file.js";
@@ -10,7 +10,7 @@ import type { ResourceManifest } from "@telorun/sdk";
10
10
  * There is no `imports` namespace at runtime — import snapshots are stored
11
11
  * under `resources.<alias>`.
12
12
  */
13
- export declare const KERNEL_GLOBAL_NAMES: readonly ["variables", "secrets", "resources", "env"];
13
+ export declare const KERNEL_GLOBAL_NAMES: readonly ["variables", "secrets", "resources", "ports", "env"];
14
14
  /**
15
15
  * Build a typed JSON Schema describing the kernel globals available in the
16
16
  * given manifest set. Used to merge into `x-telo-context` schemas so that
@@ -1 +1 @@
1
- {"version":3,"file":"kernel-globals.d.ts","sourceRoot":"","sources":["../src/kernel-globals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,uDAAwD,CAAC;AASzF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,gBAAgB,EAAE,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAiCrB;AAwBD;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACjC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CASrB"}
1
+ {"version":3,"file":"kernel-globals.d.ts","sourceRoot":"","sources":["../src/kernel-globals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,gEAAiE,CAAC;AASlG;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,gBAAgB,EAAE,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAkCrB;AA4CD;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACjC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CASrB"}
@@ -10,7 +10,7 @@ import { residualEntrySchemaMap } from "./residual-schema.js";
10
10
  * There is no `imports` namespace at runtime — import snapshots are stored
11
11
  * under `resources.<alias>`.
12
12
  */
13
- export const KERNEL_GLOBAL_NAMES = ["variables", "secrets", "resources", "env"];
13
+ export const KERNEL_GLOBAL_NAMES = ["variables", "secrets", "resources", "ports", "env"];
14
14
  const SYSTEM_KINDS = new Set([
15
15
  "Telo.Definition",
16
16
  "Telo.Application",
@@ -55,10 +55,28 @@ export function buildKernelGlobalsSchema(manifests) {
55
55
  properties: resourceProps,
56
56
  additionalProperties: false,
57
57
  },
58
+ ports: buildPortsSchema(moduleManifest?.ports),
58
59
  env: { type: "object", additionalProperties: true },
59
60
  },
60
61
  };
61
62
  }
63
+ /** Build the closed `ports` chain-access schema: each declared port is an
64
+ * integer, so `ports.<name>` resolves and `ports.typo` (or member access past
65
+ * a port, like `ports.http.foo`) is flagged. Falls back to an open map when
66
+ * the module declares no ports. */
67
+ function buildPortsSchema(ports) {
68
+ if (!ports || typeof ports !== "object" || Array.isArray(ports)) {
69
+ return { type: "object", additionalProperties: true };
70
+ }
71
+ const props = {};
72
+ for (const name of Object.keys(ports)) {
73
+ props[name] = { type: "integer" };
74
+ }
75
+ if (Object.keys(props).length === 0) {
76
+ return { type: "object", additionalProperties: true };
77
+ }
78
+ return { type: "object", properties: props, additionalProperties: false };
79
+ }
62
80
  /** Wrap a JSON Schema property map (like `Telo.Application.variables`) into a
63
81
  * closed object schema suitable for chain-access validation. For Application
64
82
  * entries the per-entry shape carries kernel-specific keys (`env`, `default`)
@@ -0,0 +1,109 @@
1
+ import type { ResourceDefinition, ResourceManifest } from "@telorun/sdk";
2
+ import type { AliasResolver } from "./alias-resolver.js";
3
+ import type { DefinitionRegistry } from "./definition-registry.js";
4
+ import { type RefFieldEntry, type SchemaFromFieldEntry } from "./reference-field-map.js";
5
+ /**
6
+ * One descent surface over a manifest's resources, emitting the annotation
7
+ * sites every analyzer pass needs. It replaces the iteration scaffolding that
8
+ * `validate-references`, `dependency-graph`, and the analyzer's CEL walk each
9
+ * reimplemented (field-map fetch, scope collection, ref/schema-from iteration,
10
+ * CEL expression walk + context matching).
11
+ *
12
+ * Two discovery mechanics ride one per-resource pass:
13
+ *
14
+ * - **Path-driven** — ref / scope / schema-from sites come from the resource's
15
+ * per-kind field map (`RefSite`, `ScopeBoundary`, `SchemaFromSite`). This is
16
+ * map iteration resolved against the resource value, not a node-by-node tree
17
+ * descent; the field map already unifies all three annotation types.
18
+ * - **Value-tree-driven** — compiled `${{...}}` / `!cel` nodes are found by
19
+ * scanning the resource value tree (`CelSite`). CEL can sit in any string
20
+ * field, including ones the field map never lists, so its discovery is
21
+ * fundamentally not path-driven; the field map only supplies the matched
22
+ * `x-telo-context` schema at the enclosing path.
23
+ *
24
+ * Handlers are optional (Babel-style): the walker computes and emits only what
25
+ * the visitor subscribes to, and skips the work behind absent handlers.
26
+ *
27
+ * **Scope is per-resource.** `ScopeBoundary` is emitted once per resource at
28
+ * enter time, before that resource's `RefSite`s, carrying both the source
29
+ * enclosure prefixes (for refs written *inside* a scope) and the enclosed
30
+ * resource-name set (for consumers that drop edges to scoped targets). No
31
+ * cross-resource ordering or global enclosed-name union is implied — every
32
+ * consumer's scope decision is local to the resource being visited, matching
33
+ * the semantics each pass had before this walker existed.
34
+ */
35
+ export interface ResourceEnterEvent {
36
+ source: ResourceManifest;
37
+ /** Resolved definition for the resource's kind, or undefined when unknown. */
38
+ definition?: ResourceDefinition;
39
+ }
40
+ export interface ResourceExitEvent {
41
+ source: ResourceManifest;
42
+ }
43
+ export interface ScopeBoundaryEvent {
44
+ source: ResourceManifest;
45
+ /** Dot-form prefixes of every `x-telo-scope` field on this resource. */
46
+ scopePrefixes: string[];
47
+ /** Scope-field JSON Pointer → manifests declared within that scope. */
48
+ manifestsByPointer: Map<string, ResourceManifest[]>;
49
+ /** Names of every resource declared inside this resource's scopes. Used by
50
+ * the dependency graph to drop boot edges to scoped (on-demand) targets. */
51
+ enclosedNames: Set<string>;
52
+ }
53
+ export interface RefSiteEvent {
54
+ source: ResourceManifest;
55
+ /** Field-map path with `[]` / `{}` markers (e.g. `steps[].invoke`). */
56
+ fieldPath: string;
57
+ /** Concrete path with `[N]` / map keys, matching `buildPositionIndex` keys. */
58
+ concretePath: string;
59
+ /** The ref value at this concrete site (sentinel, string, or `{kind,name}`). */
60
+ value: unknown;
61
+ /** The ref constraint (`refs[]`, `isArray`, optional `context`). */
62
+ entry: RefFieldEntry;
63
+ /** True when `fieldPath` falls within one of this resource's scope prefixes —
64
+ * source enclosure, used to scope a ref's candidate set. */
65
+ inScope: boolean;
66
+ /** Scope manifests visible to this ref path (non-empty only when `inScope`). */
67
+ visibleScopeManifests: ResourceManifest[];
68
+ }
69
+ export interface SchemaFromSiteEvent {
70
+ source: ResourceManifest;
71
+ /** Field-map path of the `x-telo-schema-from` slot. */
72
+ fieldPath: string;
73
+ entry: SchemaFromFieldEntry;
74
+ }
75
+ export interface CelSiteEvent {
76
+ source: ResourceManifest;
77
+ /** Concrete dotted path of the expression (from `walkCelExpressions`). */
78
+ path: string;
79
+ /** The CEL source expression. */
80
+ expr: string;
81
+ /** Engine that owns the expression (`cel`, `literal`, …). */
82
+ engineName: string;
83
+ /** Raw `x-telo-context` schema matched at the enclosing path, if any. The
84
+ * consumer resolves `x-telo-context-*` annotations and merges its own
85
+ * globals — the walker only does the path → context match. */
86
+ contextSchema?: Record<string, any>;
87
+ /** Scope of the matched context (e.g. `$.routes[*].handler`), if matched. */
88
+ matchedScope?: string;
89
+ }
90
+ export interface ManifestVisitor {
91
+ onResourceEnter?(e: ResourceEnterEvent): void;
92
+ onScope?(e: ScopeBoundaryEvent): void;
93
+ onRef?(e: RefSiteEvent): void;
94
+ onSchemaFrom?(e: SchemaFromSiteEvent): void;
95
+ onCel?(e: CelSiteEvent): void;
96
+ onResourceExit?(e: ResourceExitEvent): void;
97
+ }
98
+ export interface VisitOptions {
99
+ aliases?: AliasResolver;
100
+ aliasesByModule?: Map<string, AliasResolver>;
101
+ /** Resource kinds to skip entirely (kind blueprints, import metadata, …). */
102
+ skipKinds?: ReadonlySet<string>;
103
+ /** When true, ref / scope sites come from the schema-from-expanded field map
104
+ * so refs nested behind `x-telo-schema-from` are surfaced. `SchemaFromSite`
105
+ * events are always emitted from the base map regardless of this flag. */
106
+ expand?: boolean;
107
+ }
108
+ export declare function visitManifest(resources: ResourceManifest[], registry: DefinitionRegistry, visitor: ManifestVisitor, options?: VisitOptions): void;
109
+ //# sourceMappingURL=manifest-visitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-visitor.d.ts","sourceRoot":"","sources":["../src/manifest-visitor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ import { walkCelExpressions } from "@telorun/templating";
2
+ import { isRefEntry, isSchemaFromEntry, isScopeEntry, resolveFieldEntries, resolveFieldValues, } from "./reference-field-map.js";
3
+ import { extractContextsFromSchema, pathMatchesScope } from "./validate-cel-context.js";
4
+ const scopePrefixOf = (pointer) => pointer.replace(/^\//, "").replace(/\//g, ".");
5
+ const pathUnderPrefix = (fieldPath, prefix) => fieldPath === prefix ||
6
+ fieldPath.startsWith(prefix + ".") ||
7
+ fieldPath.startsWith(prefix + "[");
8
+ export function visitManifest(resources, registry, visitor, options = {}) {
9
+ const { aliases, aliasesByModule, skipKinds, expand } = options;
10
+ const wantsRefs = !!visitor.onRef;
11
+ const wantsScope = !!visitor.onScope;
12
+ const wantsSchemaFrom = !!visitor.onSchemaFrom;
13
+ const wantsCel = !!visitor.onCel;
14
+ for (const r of resources) {
15
+ if (!r.metadata?.name || !r.kind)
16
+ continue;
17
+ if (skipKinds?.has(r.kind))
18
+ continue;
19
+ const resolvedKind = aliases?.resolveKind(r.kind);
20
+ const definition = registry.resolve(r.kind) ??
21
+ (resolvedKind ? registry.resolve(resolvedKind) : undefined);
22
+ visitor.onResourceEnter?.({ source: r, definition });
23
+ if (wantsRefs || wantsScope || wantsSchemaFrom) {
24
+ const baseMap = aliases
25
+ ? registry.getFieldMapForKind(r.kind, aliases)
26
+ : registry.getFieldMap(r.kind);
27
+ // Expanded map drives ref/scope sites when requested; schema-from sites
28
+ // come from the base map (expansion replaces them with nested refs).
29
+ const refScopeMap = expand && aliases && aliasesByModule
30
+ ? registry.expandedFieldMapForResource(r, aliases, aliasesByModule)
31
+ : baseMap;
32
+ if (refScopeMap && (wantsRefs || wantsScope)) {
33
+ const manifestsByPointer = new Map();
34
+ for (const [fieldPath, entry] of refScopeMap) {
35
+ if (!isScopeEntry(entry))
36
+ continue;
37
+ const raw = resolveFieldValues(r, fieldPath)
38
+ .flatMap((v) => (Array.isArray(v) ? v : [v]))
39
+ .filter((v) => !!v && typeof v === "object");
40
+ const pointers = Array.isArray(entry.scope) ? entry.scope : [entry.scope];
41
+ for (const pointer of pointers)
42
+ manifestsByPointer.set(pointer, raw);
43
+ }
44
+ const scopePrefixes = Array.from(manifestsByPointer.keys()).map(scopePrefixOf);
45
+ if (wantsScope) {
46
+ const enclosedNames = new Set();
47
+ for (const manifests of manifestsByPointer.values()) {
48
+ for (const m of manifests) {
49
+ const name = m.metadata?.name;
50
+ if (typeof name === "string")
51
+ enclosedNames.add(name);
52
+ }
53
+ }
54
+ visitor.onScope({ source: r, scopePrefixes, manifestsByPointer, enclosedNames });
55
+ }
56
+ if (wantsRefs) {
57
+ for (const [fieldPath, entry] of refScopeMap) {
58
+ if (!isRefEntry(entry))
59
+ continue;
60
+ const inScope = scopePrefixes.some((prefix) => pathUnderPrefix(fieldPath, prefix));
61
+ const visibleScopeManifests = [];
62
+ if (inScope) {
63
+ for (const [pointer, manifests] of manifestsByPointer) {
64
+ if (pathUnderPrefix(fieldPath, scopePrefixOf(pointer))) {
65
+ visibleScopeManifests.push(...manifests);
66
+ }
67
+ }
68
+ }
69
+ for (const { value, path: concretePath } of resolveFieldEntries(r, fieldPath)) {
70
+ if (!value)
71
+ continue;
72
+ visitor.onRef({
73
+ source: r,
74
+ fieldPath,
75
+ concretePath,
76
+ value,
77
+ entry,
78
+ inScope,
79
+ visibleScopeManifests,
80
+ });
81
+ }
82
+ }
83
+ }
84
+ }
85
+ if (wantsSchemaFrom && baseMap) {
86
+ for (const [fieldPath, entry] of baseMap) {
87
+ if (!isSchemaFromEntry(entry))
88
+ continue;
89
+ visitor.onSchemaFrom({ source: r, fieldPath, entry });
90
+ }
91
+ }
92
+ }
93
+ if (wantsCel) {
94
+ const contexts = definition?.schema ? extractContextsFromSchema(definition.schema) : [];
95
+ walkCelExpressions(r, "", (expr, path, engineName) => {
96
+ let contextSchema;
97
+ let matchedScope;
98
+ for (const ctx of contexts) {
99
+ if (pathMatchesScope(path, ctx.scope)) {
100
+ contextSchema = ctx.schema;
101
+ matchedScope = ctx.scope;
102
+ break;
103
+ }
104
+ }
105
+ visitor.onCel({ source: r, path, expr, engineName, contextSchema, matchedScope });
106
+ });
107
+ }
108
+ visitor.onResourceExit?.({ source: r });
109
+ }
110
+ }
@@ -78,6 +78,7 @@ export declare function resolveFieldValues(obj: unknown, path: string): unknown[
78
78
  * - A node with `properties` → recurse into each property
79
79
  */
80
80
  export declare function buildReferenceFieldMap(schema: Record<string, any>): ReferenceFieldMap;
81
+ export declare function collectRefs(node: Record<string, any>): string[];
81
82
  /** Traverses an arbitrary JSON Schema starting at the given path prefix. Used to
82
83
  * expand x-telo-schema-from sub-schemas into nested ref/scope entries so Phase 2
83
84
  * inline normalization and Phase 5 injection see slots that the local field map
@@ -1 +1 @@
1
- {"version":3,"file":"reference-field-map.d.ts","sourceRoot":"","sources":["../src/reference-field-map.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B;sDACkD;IAClD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,0FAA0F;IAC1F,OAAO,EAAE,OAAO,CAAC;IACjB;8DAC0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B;2CACuC;IACvC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED;8CAC8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC;;qFAEiF;IACjF,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAEnF;0FAC0F;AAC1F,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE3D,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,aAAa,CAEvE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,eAAe,CAE3E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,oBAAoB,CAErF;AAED,oGAAoG;AACpG,eAAO,MAAM,cAAc,aAAwC,CAAC;AAEpE;;;;;;;;;2EAS2E;AAC3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAItE;AAED;;;;sEAIsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;wDAUwD;AACxD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAoCpF;AAED;+DAC+D;AAC/D,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAExE;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,iBAAiB,CAQrF;AAiBD;;;8CAG8C;AAC9C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,UAAU,EAAE,MAAM,GACjB,iBAAiB,CAInB"}
1
+ {"version":3,"file":"reference-field-map.d.ts","sourceRoot":"","sources":["../src/reference-field-map.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B;sDACkD;IAClD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,0FAA0F;IAC1F,OAAO,EAAE,OAAO,CAAC;IACjB;8DAC0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B;2CACuC;IACvC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED;8CAC8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC;;qFAEiF;IACjF,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAEnF;0FAC0F;AAC1F,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE3D,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,aAAa,CAEvE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,eAAe,CAE3E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,oBAAoB,CAErF;AAED,oGAAoG;AACpG,eAAO,MAAM,cAAc,aAAwC,CAAC;AAEpE;;;;;;;;;2EAS2E;AAC3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAItE;AAED;;;;sEAIsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;wDAUwD;AACxD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAoCpF;AAED;+DAC+D;AAC/D,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAExE;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,iBAAiB,CAQrF;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE,CAa/D;AAED;;;8CAG8C;AAC9C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,UAAU,EAAE,MAAM,GACjB,iBAAiB,CAInB"}
@@ -103,7 +103,7 @@ export function buildReferenceFieldMap(schema) {
103
103
  }
104
104
  return map;
105
105
  }
106
- function collectRefs(node) {
106
+ export function collectRefs(node) {
107
107
  const refs = [];
108
108
  if (typeof node["x-telo-ref"] === "string") {
109
109
  refs.push(node["x-telo-ref"]);
@@ -35,12 +35,26 @@ export declare function navigateJsonPointer(schema: unknown, pointer: string): u
35
35
  * Stops and returns the current node when a union type (`anyOf`/`oneOf`) is reached.
36
36
  * Returns `undefined` if any segment cannot be resolved. */
37
37
  export declare function navigateSchemaToExprPath(schema: Record<string, any>, path: string): Record<string, any> | undefined;
38
+ /**
39
+ * Recognized `x-telo-type` value brands and the CEL primitive each refines.
40
+ * A brand is a nominal type the analyzer registers (see cel-environment.ts) so
41
+ * structurally-identical values (a `TcpPort` and a `UdpPort` are both integers)
42
+ * stay distinct for static wiring checks. Brands carry no runtime effect — the
43
+ * value flows as its base type. Add new brands here (e.g. `Url: "string"`).
44
+ */
45
+ export declare const VALUE_BRAND_BASE: Record<string, string>;
46
+ /** Read a recognized `x-telo-type` brand off a schema, or undefined. */
47
+ export declare function brandOfSchema(schema: Record<string, any> | undefined): string | undefined;
38
48
  /** Map a JSON Schema type annotation to a CEL type string. */
39
49
  export declare function jsonSchemaToCelType(schema: Record<string, any> | undefined): string;
40
50
  /** Check whether a CEL return type is compatible with a JSON Schema type constraint. */
41
51
  export declare function celTypeSatisfiesJsonSchema(celType: string, schema: Record<string, any>): boolean;
42
52
  /** Return a literal placeholder value of the correct schema type for AJV. */
43
53
  export declare function celPlaceholderForSchema(schema: Record<string, any>): unknown;
54
+ /** Resolve a `$ref` (only `#/$defs/...` form) against the root schema. */
55
+ export declare function resolveRef(schema: Record<string, any>, root: Record<string, any>): Record<string, any>;
56
+ /** Collect property schemas from top-level `properties` and all `oneOf`/`anyOf` sub-schemas. */
57
+ export declare function collectProperties(schema: Record<string, any>): Record<string, any>;
44
58
  /** Deep-clone `data`, replacing every pure CEL template string (`${{ expr }}`) with a
45
59
  * schema-appropriate placeholder so AJV can validate non-CEL fields without false positives. */
46
60
  export declare function substituteCelFields(data: unknown, schema: Record<string, any>, rootSchema?: Record<string, any>): unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"schema-compat.d.ts","sourceRoot":"","sources":["../src/schema-compat.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,GAAG,KAA0C,CAAC;AAEpD;;;;;;;mCAOmC;AACnC,wBAAgB,SAAS,IAAI,YAAY,CAAC,OAAO,GAAG,CAAC,CAOpD;AAKD,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;oEAEoE;AACpE,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC1B,mBAAmB,CAIrB;AAiDD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAelD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGxE;AAuBD,mFAAmF;AACnF,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,0GAA0G;AAC1G,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,WAAW,EAAE,CAe/F;AAED;qFACqF;AACrF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQ7E;AAED;;;;6DAI6D;AAC7D,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,EAAE,MAAM,GACX,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAsBjC;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GAAG,MAAM,CAuBnF;AAED,wFAAwF;AACxF,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAqBhG;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAiB5E;AA2BD;iGACiG;AACjG,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC/B,OAAO,CAqCT"}
1
+ {"version":3,"file":"schema-compat.d.ts","sourceRoot":"","sources":["../src/schema-compat.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,GAAG,KAA0C,CAAC;AAEpD;;;;;;;mCAOmC;AACnC,wBAAgB,SAAS,IAAI,YAAY,CAAC,OAAO,GAAG,CAAC,CAOpD;AAKD,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;oEAEoE;AACpE,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC1B,mBAAmB,CAIrB;AAiDD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAelD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGxE;AAuBD,mFAAmF;AACnF,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,0GAA0G;AAC1G,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,WAAW,EAAE,CAmB/F;AAED;qFACqF;AACrF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQ7E;AAED;;;;6DAI6D;AAC7D,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,EAAE,MAAM,GACX,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAsBjC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGnD,CAAC;AAEF,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGzF;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GAAG,MAAM,CAyBnF;AAED,wFAAwF;AACxF,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAiChG;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAiB5E;AAID,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAOtG;AAED,gGAAgG;AAChG,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAUlF;AAED;iGACiG;AACjG,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC/B,OAAO,CAqCT"}
@@ -109,6 +109,10 @@ export function validateAgainstSchema(data, schema) {
109
109
  compiledSchemaValidators.set(schema, validate);
110
110
  }
111
111
  catch {
112
+ // A schema that won't compile is reported loudly (once, on the owning
113
+ // definition) by the analyzer's definition-schema compile check
114
+ // (`DefinitionRegistry.schemaCompileError`), so returning `[]` here does
115
+ // not silently accept resources of that kind.
112
116
  return [];
113
117
  }
114
118
  }
@@ -168,10 +172,29 @@ export function navigateSchemaToExprPath(schema, path) {
168
172
  }
169
173
  return current;
170
174
  }
175
+ /**
176
+ * Recognized `x-telo-type` value brands and the CEL primitive each refines.
177
+ * A brand is a nominal type the analyzer registers (see cel-environment.ts) so
178
+ * structurally-identical values (a `TcpPort` and a `UdpPort` are both integers)
179
+ * stay distinct for static wiring checks. Brands carry no runtime effect — the
180
+ * value flows as its base type. Add new brands here (e.g. `Url: "string"`).
181
+ */
182
+ export const VALUE_BRAND_BASE = {
183
+ TcpPort: "int",
184
+ UdpPort: "int",
185
+ };
186
+ /** Read a recognized `x-telo-type` brand off a schema, or undefined. */
187
+ export function brandOfSchema(schema) {
188
+ const brand = schema?.["x-telo-type"];
189
+ return typeof brand === "string" && brand in VALUE_BRAND_BASE ? brand : undefined;
190
+ }
171
191
  /** Map a JSON Schema type annotation to a CEL type string. */
172
192
  export function jsonSchemaToCelType(schema) {
173
193
  if (!schema || typeof schema !== "object")
174
194
  return "dyn";
195
+ const brand = brandOfSchema(schema);
196
+ if (brand)
197
+ return brand;
175
198
  if (schema.anyOf || schema.oneOf || schema.allOf)
176
199
  return "dyn";
177
200
  if (Array.isArray(schema.type))
@@ -202,6 +225,19 @@ export function jsonSchemaToCelType(schema) {
202
225
  export function celTypeSatisfiesJsonSchema(celType, schema) {
203
226
  if (celType === "dyn")
204
227
  return true;
228
+ // Nominal value brands: when the expression's type is a recognized brand,
229
+ // a branded consuming field must match exactly (a UdpPort wired into a
230
+ // TcpPort-branded field is the error we want). An unbranded field accepts
231
+ // the brand as its base type — gradual typing, so a TcpPort flows freely
232
+ // into a plain integer field. (A plain integer into a branded field is also
233
+ // allowed: only a *conflicting* brand is rejected.)
234
+ const sourceBase = VALUE_BRAND_BASE[celType];
235
+ if (sourceBase) {
236
+ const fieldBrand = brandOfSchema(schema);
237
+ if (fieldBrand)
238
+ return fieldBrand === celType;
239
+ celType = sourceBase;
240
+ }
205
241
  if (!schema.type && !schema.anyOf && !schema.oneOf && !schema.allOf)
206
242
  return true;
207
243
  if (schema.anyOf || schema.oneOf || schema.allOf)
@@ -247,7 +283,7 @@ export function celPlaceholderForSchema(schema) {
247
283
  }
248
284
  const CEL_PURE_RE = /^\s*\$\{\{[^}]*\}\}\s*$/;
249
285
  /** Resolve a `$ref` (only `#/$defs/...` form) against the root schema. */
250
- function resolveRef(schema, root) {
286
+ export function resolveRef(schema, root) {
251
287
  if (schema.$ref && typeof schema.$ref === "string" && schema.$ref.startsWith("#/$defs/")) {
252
288
  const defName = schema.$ref.slice("#/$defs/".length);
253
289
  const resolved = root.$defs?.[defName];
@@ -257,7 +293,7 @@ function resolveRef(schema, root) {
257
293
  return schema;
258
294
  }
259
295
  /** Collect property schemas from top-level `properties` and all `oneOf`/`anyOf` sub-schemas. */
260
- function collectProperties(schema) {
296
+ export function collectProperties(schema) {
261
297
  const props = { ...(schema.properties ?? {}) };
262
298
  for (const sub of schema.oneOf ?? schema.anyOf ?? []) {
263
299
  if (sub && typeof sub === "object" && sub.properties) {
@@ -83,4 +83,18 @@ export declare function resolveContextAnnotations(schema: Record<string, any>, m
83
83
  * e.g. exprPath="routes[0].inputs.q", scope="$.routes[*].inputs" → manifest.routes[0]
84
84
  */
85
85
  export declare function getManifestItem(exprPath: string, scope: string, manifest: Record<string, any>): Record<string, any>;
86
+ /**
87
+ * Walk a JSON Schema tree and collect all `x-telo-context` annotations,
88
+ * returning them as `{ scope, schema }` pairs using JSONPath-style scopes —
89
+ * the same format the analyzer uses for CEL context validation.
90
+ *
91
+ * Result is sorted by scope specificity (longer scope first) so that the
92
+ * per-expression resolver's first-match-wins logic picks the most-specific
93
+ * context. Without this, a broader ancestor scope (e.g. `$.resources[*]`)
94
+ * could shadow a narrower descendant scope whose activation differs.
95
+ */
96
+ export declare function extractContextsFromSchema(schema: Record<string, any>, path?: string): Array<{
97
+ scope: string;
98
+ schema: Record<string, any>;
99
+ }>;
86
100
  //# sourceMappingURL=validate-cel-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-cel-context.d.ts","sourceRoot":"","sources":["../src/validate-cel-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEtF,MAAM,WAAW,kBAAkB;IACjC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC;;kDAE8C;IAC9C,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;KACxD,CAAC;IACF,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KAC/C,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAClC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA6BjC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,IAAI,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAChD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAqGrB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAQrB"}
1
+ {"version":3,"file":"validate-cel-context.d.ts","sourceRoot":"","sources":["../src/validate-cel-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEtF,MAAM,WAAW,kBAAkB;IACjC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC;;kDAE8C;IAC9C,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;KACxD,CAAC;IACF,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KAC/C,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAClC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA6BjC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,IAAI,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAChD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAqGrB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAQrB;AAWD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,SAAM,GACT,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAAC,CAGvD"}