@telorun/analyzer 0.53.0 → 0.55.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 (76) hide show
  1. package/dist/analysis-registry.d.ts +20 -0
  2. package/dist/analysis-registry.d.ts.map +1 -1
  3. package/dist/analysis-registry.js +36 -3
  4. package/dist/analyzer.d.ts +3 -2
  5. package/dist/analyzer.d.ts.map +1 -1
  6. package/dist/analyzer.js +188 -26
  7. package/dist/builtins.d.ts.map +1 -1
  8. package/dist/builtins.js +32 -12
  9. package/dist/call-graph.d.ts +189 -0
  10. package/dist/call-graph.d.ts.map +1 -0
  11. package/dist/call-graph.js +617 -0
  12. package/dist/dependency-graph.d.ts +17 -7
  13. package/dist/dependency-graph.d.ts.map +1 -1
  14. package/dist/dependency-graph.js +36 -65
  15. package/dist/flatten-for-analyzer.d.ts +8 -0
  16. package/dist/flatten-for-analyzer.d.ts.map +1 -1
  17. package/dist/flatten-for-analyzer.js +32 -0
  18. package/dist/index.d.ts +14 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +7 -1
  21. package/dist/manifest-navigation.d.ts +32 -0
  22. package/dist/manifest-navigation.d.ts.map +1 -0
  23. package/dist/manifest-navigation.js +91 -0
  24. package/dist/manifest-visitor.js +1 -1
  25. package/dist/ref-slot.d.ts +125 -0
  26. package/dist/ref-slot.d.ts.map +1 -0
  27. package/dist/ref-slot.js +226 -0
  28. package/dist/reference-field-map.d.ts +15 -1
  29. package/dist/reference-field-map.d.ts.map +1 -1
  30. package/dist/reference-field-map.js +29 -35
  31. package/dist/resolve-schema-ref-kinds.d.ts +4 -0
  32. package/dist/resolve-schema-ref-kinds.d.ts.map +1 -1
  33. package/dist/resolve-schema-ref-kinds.js +31 -8
  34. package/dist/resolve-zone-requirements.d.ts +110 -0
  35. package/dist/resolve-zone-requirements.d.ts.map +1 -0
  36. package/dist/resolve-zone-requirements.js +541 -0
  37. package/dist/types.d.ts +8 -0
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/validate-observed-state.d.ts +14 -13
  40. package/dist/validate-observed-state.d.ts.map +1 -1
  41. package/dist/validate-observed-state.js +21 -88
  42. package/dist/validate-ref-slots.d.ts +48 -0
  43. package/dist/validate-ref-slots.d.ts.map +1 -0
  44. package/dist/validate-ref-slots.js +219 -0
  45. package/dist/validate-references.d.ts.map +1 -1
  46. package/dist/validate-references.js +8 -1
  47. package/dist/validate-zone-slots.d.ts +39 -0
  48. package/dist/validate-zone-slots.d.ts.map +1 -0
  49. package/dist/validate-zone-slots.js +114 -0
  50. package/dist/zone-module-documents.d.ts +27 -0
  51. package/dist/zone-module-documents.d.ts.map +1 -0
  52. package/dist/zone-module-documents.js +1 -0
  53. package/dist/zone-slot.d.ts +61 -0
  54. package/dist/zone-slot.d.ts.map +1 -0
  55. package/dist/zone-slot.js +91 -0
  56. package/package.json +3 -3
  57. package/src/analysis-registry.ts +36 -2
  58. package/src/analyzer.ts +206 -24
  59. package/src/builtins.ts +32 -12
  60. package/src/call-graph.ts +827 -0
  61. package/src/dependency-graph.ts +34 -68
  62. package/src/flatten-for-analyzer.ts +32 -0
  63. package/src/index.ts +47 -0
  64. package/src/manifest-navigation.ts +91 -0
  65. package/src/manifest-visitor.ts +1 -1
  66. package/src/ref-slot.ts +273 -0
  67. package/src/reference-field-map.ts +39 -36
  68. package/src/resolve-schema-ref-kinds.ts +34 -7
  69. package/src/resolve-zone-requirements.ts +781 -0
  70. package/src/types.ts +8 -0
  71. package/src/validate-observed-state.ts +26 -92
  72. package/src/validate-ref-slots.ts +293 -0
  73. package/src/validate-references.ts +8 -1
  74. package/src/validate-zone-slots.ts +175 -0
  75. package/src/zone-module-documents.ts +27 -0
  76. package/src/zone-slot.ts +116 -0
@@ -1,9 +1,22 @@
1
+ import { type RefSlot, type RefUse, type RefUseCases, readRefSlot } from "./ref-slot.js";
2
+
3
+ export { readRefSlot, isRefSlot, hasDeclaredUse } from "./ref-slot.js";
4
+ export type { RefSlot, RefUse, RefUseCases } from "./ref-slot.js";
5
+
1
6
  /** An entry for a field that carries one or more x-telo-ref constraints. */
2
7
  export interface RefFieldEntry {
3
8
  /** One or more canonical kind keys ("<module>.<Kind>"), or the deprecated
4
9
  * identity form ("<namespace>/<module>#<Kind>") for a legacy published module.
5
- * Multiple entries arise from anyOf branches. */
10
+ * Multiple entries arise from a `kind:` list or from anyOf branches. */
6
11
  refs: string[];
12
+ /** What the declaring resource does with the target — see {@link RefUse}.
13
+ * Empty for a slot still on the bare-string form. */
14
+ uses: RefUse[];
15
+ /** Set when the use is selected by a sibling config field. */
16
+ useCases?: RefUseCases;
17
+ /** JSON Pointer (relative to the object enclosing the slot) naming the field
18
+ * carrying this call's arguments. */
19
+ inputs?: string;
7
20
  /** True when the field path traversed through at least one array (path contains "[]"). */
8
21
  isArray: boolean;
9
22
  /** x-telo-context schema declared on this ref slot, if any. Describes the CEL invocation
@@ -161,26 +174,11 @@ export function buildReferenceFieldMap(schema: Record<string, any>): ReferenceFi
161
174
  return map;
162
175
  }
163
176
 
164
- /** `x-telo-inline` declared on any `anyOf` branch marks the whole slot as
165
- * inline-accepting, matching how {@link collectRefs} unions branch refs. */
166
- function collectInlineFlag(node: Record<string, any>): boolean {
167
- if (!Array.isArray(node.anyOf)) return false;
168
- return node.anyOf.some((branch: Record<string, any>) => branch?.["x-telo-inline"] === true);
169
- }
170
-
177
+ /** The accepted kinds a node declares, unioned across a `kind:` list and across
178
+ * `anyOf` branches. Thin wrapper over {@link readRefSlot} kept because
179
+ * several passes want only the kind set. */
171
180
  export function collectRefs(node: Record<string, any>): string[] {
172
- const refs: string[] = [];
173
- if (typeof node["x-telo-ref"] === "string") {
174
- refs.push(node["x-telo-ref"]);
175
- }
176
- if (Array.isArray(node.anyOf)) {
177
- for (const branch of node.anyOf) {
178
- if (branch && typeof branch["x-telo-ref"] === "string") {
179
- refs.push(branch["x-telo-ref"]);
180
- }
181
- }
182
- }
183
- return refs;
181
+ return readRefSlot(node)?.kinds ?? [];
184
182
  }
185
183
 
186
184
  /** Traverses an arbitrary JSON Schema starting at the given path prefix. Used to
@@ -203,17 +201,16 @@ function traverseNode(
203
201
  root?: Record<string, any>,
204
202
  visitedRefs: Set<string> = new Set(),
205
203
  ): void {
206
- // Local `$ref` is intentionally NOT followed. Descending into shared
207
- // `$defs` (notably `Run.Sequence`'s `step` definition) would surface
208
- // ref slots like `steps[].invoke` that Phase 5 then injects live
209
- // instances into; today's `Run.Sequence` controller calls
210
- // `instance.invoke()` directly when handed an instance, bypassing
211
- // the kernel's `runInvoke` emit-Invoked path. The walker fix and the
212
- // dispatcher fix need to land together see the follow-up in
213
- // [kernel/nodejs/plans/reference-syntax-unification.md] and the
214
- // stopgap in `resource-context.ts:ensureKindRef`. `visitedRefs`
215
- // stays as a parameter so the recursive calls below thread the right
216
- // signature; turning the descent back on is a single-branch change.
204
+ // Local `$ref` is intentionally NOT followed. This map is the kernel's
205
+ // Phase-5 injection surface: descending into shared `$defs` (notably
206
+ // `Run.Sequence`'s `step` definition) would make every step's `invoke` an
207
+ // injection site, and step slots resolve at dispatch — injecting there is
208
+ // unwanted regardless of tracing (the original dispatcher-bypass blocker
209
+ // has since shipped via the `REF_IDENTITY` stamp). Static analysis is NOT
210
+ // limited by this stop: the call graph (`call-graph.ts`) reads step slots
211
+ // from the item schema itself and scans the value tree, so it sees what
212
+ // this map deliberately hides. `visitedRefs` stays as a parameter so the
213
+ // recursive calls below thread the right signature.
217
214
  if (typeof node?.$ref === "string") return;
218
215
  // Scope slot — record and stop; do not recurse into scope contents
219
216
  if ("x-telo-scope" in node) {
@@ -227,12 +224,18 @@ function traverseNode(
227
224
  return;
228
225
  }
229
226
 
230
- // Reference slot (direct or via anyOf)
231
- const refs = collectRefs(node);
232
- if (refs.length > 0) {
233
- const entry: RefFieldEntry = { refs, isArray: path.includes("[]") };
227
+ // Reference slot (direct, via a `kind:` list, or via anyOf)
228
+ const slot = readRefSlot(node);
229
+ if (slot && slot.kinds.length > 0) {
230
+ const entry: RefFieldEntry = {
231
+ refs: slot.kinds,
232
+ uses: slot.uses,
233
+ isArray: path.includes("[]"),
234
+ };
235
+ if (slot.useCases) entry.useCases = slot.useCases;
236
+ if (slot.inputs !== undefined) entry.inputs = slot.inputs;
234
237
  if (node["x-telo-context"]) entry.context = node["x-telo-context"] as Record<string, any>;
235
- if (node["x-telo-inline"] === true || collectInlineFlag(node)) entry.inline = true;
238
+ if (slot.inline) entry.inline = true;
236
239
  map.set(path, entry);
237
240
  // A node can mix item-level ref branches (a bare string / `{kind, name}`)
238
241
  // with object branches that carry their OWN nested refs — e.g. Application
@@ -1,5 +1,7 @@
1
1
  import type { ResourceManifest } from "@telorun/sdk";
2
2
  import type { AliasResolver } from "./alias-resolver.js";
3
+ import { rewriteRefSlotKinds } from "./ref-slot.js";
4
+ import { hasRequiresZone, rewriteRequiresZoneKind } from "./zone-slot.js";
3
5
 
4
6
  const REF_ANNOTATION = "x-telo-ref";
5
7
 
@@ -24,6 +26,10 @@ export interface RefConstraintIssue {
24
26
  /** The `Telo.Definition` / `Telo.Abstract` doc that declares the slot. */
25
27
  manifest: ResourceManifest;
26
28
  reason: RefConstraintReason;
29
+ /** Which annotation carried the unresolved name: an `x-telo-ref` constraint
30
+ * (the default) or an `x-telo-requires-zone` provider kind — the caller
31
+ * reports the latter as ZONE_PROVIDER_UNRESOLVED. */
32
+ annotation?: "ref" | "zone";
27
33
  /** For `gated`: the target module and the kinds it does export. */
28
34
  gate?: { module: string; exported: string[] };
29
35
  /** Aliases the declaring scope does know — the "did you mean" material for
@@ -69,9 +75,9 @@ export function resolveSchemaRefKinds(
69
75
  ): RefConstraintIssue[] {
70
76
  const issues: RefConstraintIssue[] = [];
71
77
 
72
- const record = (ref: string, path: string): void => {
78
+ const record = (ref: string, path: string, annotation: "ref" | "zone" = "ref"): void => {
73
79
  if (isLegacyRefIdentity(ref)) {
74
- issues.push({ ref, path, manifest: definition, reason: "legacy" });
80
+ issues.push({ ref, path, manifest: definition, reason: "legacy", annotation });
75
81
  return;
76
82
  }
77
83
  const result = resolver.resolveKindResult(ref);
@@ -83,6 +89,7 @@ export function resolveSchemaRefKinds(
83
89
  path,
84
90
  manifest: definition,
85
91
  reason: "gated",
92
+ annotation,
86
93
  gate: { module: result.module, exported: result.exported },
87
94
  }
88
95
  : {
@@ -90,6 +97,7 @@ export function resolveSchemaRefKinds(
90
97
  path,
91
98
  manifest: definition,
92
99
  reason: "unknown",
100
+ annotation,
93
101
  knownAliases: resolver.knownAliases(),
94
102
  },
95
103
  );
@@ -102,11 +110,30 @@ export function resolveSchemaRefKinds(
102
110
  return;
103
111
  }
104
112
  const obj = value as Record<string, unknown>;
105
- const ref = obj[REF_ANNOTATION];
106
- if (typeof ref === "string" && ref) {
107
- const result = isLegacyRefIdentity(ref) ? null : resolver.resolveKindResult(ref);
108
- if (result?.status === "ok") obj[REF_ANNOTATION] = result.kind;
109
- else record(ref, path);
113
+ if (obj[REF_ANNOTATION] !== undefined) {
114
+ // Shape knowledge stays in `ref-slot.ts`: this pass states only the
115
+ // alias→canonical rule and what to report when it does not apply.
116
+ rewriteRefSlotKinds(obj, (ref) => {
117
+ const result = isLegacyRefIdentity(ref) ? null : resolver.resolveKindResult(ref);
118
+ if (result?.status === "ok") return result.kind;
119
+ record(ref, path);
120
+ return undefined;
121
+ });
122
+ }
123
+ if (hasRequiresZone(obj)) {
124
+ // A zone requirement names its provider kind through the identical
125
+ // alias-qualified grammar, resolved in the same declaring scope — one
126
+ // walk canonicalizes both, so the kernel's `requireZone` and the zone
127
+ // projection never see an alias. An unresolved name is reported like an
128
+ // unresolved ref constraint (the caller maps it to
129
+ // ZONE_PROVIDER_UNRESOLVED); the legacy identity form is not accepted
130
+ // here — the annotation postdates its removal.
131
+ rewriteRequiresZoneKind(obj, (zone) => {
132
+ const result = resolver.resolveKindResult(zone);
133
+ if (result.status === "ok") return result.kind;
134
+ record(zone, `${path}.x-telo-requires-zone`, "zone");
135
+ return undefined;
136
+ });
110
137
  }
111
138
  for (const key of Object.keys(obj)) {
112
139
  walk(obj[key], path ? `${path}.${key}` : key);