@telorun/analyzer 0.1.3 → 0.2.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 (65) hide show
  1. package/README.md +3 -3
  2. package/dist/analyzer.d.ts +6 -0
  3. package/dist/analyzer.d.ts.map +1 -1
  4. package/dist/analyzer.js +45 -25
  5. package/dist/builtins.d.ts.map +1 -1
  6. package/dist/builtins.js +56 -24
  7. package/dist/cel-environment.d.ts +12 -5
  8. package/dist/cel-environment.d.ts.map +1 -1
  9. package/dist/cel-environment.js +31 -17
  10. package/dist/definition-registry.d.ts +5 -5
  11. package/dist/definition-registry.d.ts.map +1 -1
  12. package/dist/definition-registry.js +10 -10
  13. package/dist/dependency-graph.d.ts.map +1 -1
  14. package/dist/dependency-graph.js +9 -2
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1 -1
  18. package/dist/kernel-globals.d.ts +6 -2
  19. package/dist/kernel-globals.d.ts.map +1 -1
  20. package/dist/kernel-globals.js +14 -8
  21. package/dist/manifest-loader.d.ts +9 -1
  22. package/dist/manifest-loader.d.ts.map +1 -1
  23. package/dist/manifest-loader.js +165 -11
  24. package/dist/module-kinds.d.ts +4 -0
  25. package/dist/module-kinds.d.ts.map +1 -0
  26. package/dist/module-kinds.js +4 -0
  27. package/dist/normalize-inline-resources.d.ts.map +1 -1
  28. package/dist/normalize-inline-resources.js +6 -1
  29. package/dist/precompile.d.ts +3 -2
  30. package/dist/precompile.d.ts.map +1 -1
  31. package/dist/precompile.js +13 -11
  32. package/dist/reference-field-map.d.ts +1 -1
  33. package/dist/resolve-throws-union.d.ts +30 -0
  34. package/dist/resolve-throws-union.d.ts.map +1 -0
  35. package/dist/resolve-throws-union.js +252 -0
  36. package/dist/types.d.ts +11 -0
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/validate-cel-context.js +1 -1
  39. package/dist/validate-references.d.ts.map +1 -1
  40. package/dist/validate-references.js +19 -12
  41. package/dist/validate-throws-coverage.d.ts +8 -0
  42. package/dist/validate-throws-coverage.d.ts.map +1 -0
  43. package/dist/validate-throws-coverage.js +461 -0
  44. package/package.json +3 -3
  45. package/src/analyzer.ts +60 -26
  46. package/src/builtins.ts +56 -24
  47. package/src/cel-environment.ts +40 -17
  48. package/src/definition-registry.ts +10 -10
  49. package/src/dependency-graph.ts +9 -2
  50. package/src/index.ts +2 -1
  51. package/src/kernel-globals.ts +19 -10
  52. package/src/manifest-loader.ts +202 -17
  53. package/src/module-kinds.ts +6 -0
  54. package/src/normalize-inline-resources.ts +6 -1
  55. package/src/precompile.ts +14 -11
  56. package/src/reference-field-map.ts +1 -1
  57. package/src/resolve-throws-union.ts +345 -0
  58. package/src/types.ts +13 -0
  59. package/src/validate-cel-context.ts +1 -1
  60. package/src/validate-references.ts +19 -12
  61. package/src/validate-throws-coverage.ts +565 -0
  62. package/dist/adapters/node-adapter.d.ts +0 -15
  63. package/dist/adapters/node-adapter.d.ts.map +0 -1
  64. package/dist/adapters/node-adapter.js +0 -33
  65. package/src/adapters/node-adapter.ts +0 -38
@@ -0,0 +1,252 @@
1
+ export function createResolveCtx(allManifests, defs, aliases) {
2
+ return {
3
+ allManifests,
4
+ defs,
5
+ aliases,
6
+ memo: new Map(),
7
+ inProgress: new Set(),
8
+ };
9
+ }
10
+ function emptyUnion() {
11
+ return { codes: new Map(), unbounded: false };
12
+ }
13
+ function unionInto(target, src) {
14
+ for (const [code, meta] of src.codes) {
15
+ if (!target.codes.has(code))
16
+ target.codes.set(code, meta);
17
+ }
18
+ if (src.unbounded)
19
+ target.unbounded = true;
20
+ }
21
+ function definitionFor(kind, defs, aliases) {
22
+ const resolved = aliases.resolveKind(kind);
23
+ return defs.resolve(kind) ?? (resolved ? defs.resolve(resolved) : undefined);
24
+ }
25
+ function codesFromDefinition(definition) {
26
+ const out = new Map();
27
+ const raw = definition.throws?.codes ?? {};
28
+ for (const [code, meta] of Object.entries(raw)) {
29
+ out.set(code, { data: meta.data });
30
+ }
31
+ return out;
32
+ }
33
+ /** Resolve the effective throw union for a named manifest. The result combines
34
+ * explicit `throws.codes`, `throws.inherit: true` dataflow (step-context
35
+ * traversal with try/catch subtraction), and unbounded markers for
36
+ * unresolvable passthrough call sites. Cycles short-circuit to an empty
37
+ * result so resolution always terminates. */
38
+ export function resolveThrowsUnion(manifest, ctx) {
39
+ const name = manifest.metadata?.name;
40
+ if (name) {
41
+ const cached = ctx.memo.get(name);
42
+ if (cached)
43
+ return cached;
44
+ if (ctx.inProgress.has(name))
45
+ return emptyUnion();
46
+ }
47
+ const definition = definitionFor(manifest.kind, ctx.defs, ctx.aliases);
48
+ if (!definition) {
49
+ const u = { codes: new Map(), unbounded: true };
50
+ if (name)
51
+ ctx.memo.set(name, u);
52
+ return u;
53
+ }
54
+ const throws = definition.throws;
55
+ if (!throws) {
56
+ const u = emptyUnion();
57
+ if (name)
58
+ ctx.memo.set(name, u);
59
+ return u;
60
+ }
61
+ if (name)
62
+ ctx.inProgress.add(name);
63
+ try {
64
+ const result = { codes: new Map(), unbounded: false };
65
+ for (const [code, meta] of codesFromDefinition(definition)) {
66
+ result.codes.set(code, meta);
67
+ }
68
+ if (throws.passthrough) {
69
+ // Definition-level passthrough can't be resolved without a call site.
70
+ // resolveStepInvokeThrows handles passthrough call sites directly.
71
+ result.unbounded = true;
72
+ }
73
+ if (throws.inherit) {
74
+ const inherited = resolveInherited(manifest, definition, ctx);
75
+ unionInto(result, inherited);
76
+ }
77
+ if (name)
78
+ ctx.memo.set(name, result);
79
+ return result;
80
+ }
81
+ finally {
82
+ if (name)
83
+ ctx.inProgress.delete(name);
84
+ }
85
+ }
86
+ function resolveInherited(manifest, definition, ctx) {
87
+ const result = { codes: new Map(), unbounded: false };
88
+ const props = definition.schema?.properties;
89
+ if (!props)
90
+ return result;
91
+ for (const [fieldName, fieldSchema] of Object.entries(props)) {
92
+ const stepCtx = fieldSchema["x-telo-step-context"];
93
+ if (!stepCtx?.invoke)
94
+ continue;
95
+ const steps = manifest[fieldName];
96
+ if (!Array.isArray(steps))
97
+ continue;
98
+ unionInto(result, collectStepArrayThrows(steps, stepCtx.invoke, undefined, ctx));
99
+ }
100
+ return result;
101
+ }
102
+ function collectStepArrayThrows(steps, invokeField, enclosingTryCodes, ctx) {
103
+ const result = emptyUnion();
104
+ for (const step of steps) {
105
+ if (!step || typeof step !== "object")
106
+ continue;
107
+ unionInto(result, collectStepThrows(step, invokeField, enclosingTryCodes, ctx));
108
+ }
109
+ return result;
110
+ }
111
+ /** Walk one step, dispatching by shape. Generic for any Run.Sequence-style
112
+ * composer: the step keys it recognises (`try` / `catch` / `finally` / `then`
113
+ * / `else` / `elseif` / `do` / `cases` / `default`) are the same set already
114
+ * traversed by the analyzer's `x-telo-step-context` schema builder, so future
115
+ * composers that reuse those shape conventions work without changes here. */
116
+ function collectStepThrows(step, invokeField, enclosingTryCodes, ctx) {
117
+ if (step[invokeField]) {
118
+ return resolveStepInvokeThrows(step, invokeField, enclosingTryCodes, ctx);
119
+ }
120
+ if (step.throw && typeof step.throw === "object") {
121
+ return resolveThrowStepCode(step.throw, enclosingTryCodes);
122
+ }
123
+ if (Array.isArray(step.try)) {
124
+ const tryUnion = collectStepArrayThrows(step.try, invokeField, enclosingTryCodes, ctx);
125
+ let propagated;
126
+ if (Array.isArray(step.catch)) {
127
+ // Catch absorbs the try block's codes; the catch's own throws propagate
128
+ // out instead. Sequence-specific subtraction — the plan explicitly
129
+ // anchors this to Run.Sequence's try/catch schema shape.
130
+ const tryCodes = new Set(tryUnion.codes.keys());
131
+ propagated = collectStepArrayThrows(step.catch, invokeField, tryCodes, ctx);
132
+ // Unbounded in the try block still signals the caller to expect
133
+ // arbitrary codes to flow through the catch (e.g. via passthrough).
134
+ if (tryUnion.unbounded)
135
+ propagated.unbounded = true;
136
+ }
137
+ else {
138
+ propagated = cloneUnion(tryUnion);
139
+ }
140
+ if (Array.isArray(step.finally)) {
141
+ unionInto(propagated, collectStepArrayThrows(step.finally, invokeField, enclosingTryCodes, ctx));
142
+ }
143
+ return propagated;
144
+ }
145
+ if (Array.isArray(step.then)) {
146
+ const result = emptyUnion();
147
+ unionInto(result, collectStepArrayThrows(step.then, invokeField, enclosingTryCodes, ctx));
148
+ if (Array.isArray(step.else)) {
149
+ unionInto(result, collectStepArrayThrows(step.else, invokeField, enclosingTryCodes, ctx));
150
+ }
151
+ if (Array.isArray(step.elseif)) {
152
+ for (const branch of step.elseif) {
153
+ if (Array.isArray(branch?.then)) {
154
+ unionInto(result, collectStepArrayThrows(branch.then, invokeField, enclosingTryCodes, ctx));
155
+ }
156
+ }
157
+ }
158
+ return result;
159
+ }
160
+ if (Array.isArray(step.do)) {
161
+ return collectStepArrayThrows(step.do, invokeField, enclosingTryCodes, ctx);
162
+ }
163
+ if (step.cases && typeof step.cases === "object") {
164
+ const result = emptyUnion();
165
+ for (const arr of Object.values(step.cases)) {
166
+ if (Array.isArray(arr)) {
167
+ unionInto(result, collectStepArrayThrows(arr, invokeField, enclosingTryCodes, ctx));
168
+ }
169
+ }
170
+ if (Array.isArray(step.default)) {
171
+ unionInto(result, collectStepArrayThrows(step.default, invokeField, enclosingTryCodes, ctx));
172
+ }
173
+ return result;
174
+ }
175
+ return emptyUnion();
176
+ }
177
+ function cloneUnion(u) {
178
+ const out = emptyUnion();
179
+ for (const [c, m] of u.codes)
180
+ out.codes.set(c, m);
181
+ out.unbounded = u.unbounded;
182
+ return out;
183
+ }
184
+ function resolveStepInvokeThrows(step, invokeField, enclosingTryCodes, ctx) {
185
+ const invokeRef = step[invokeField];
186
+ if (!invokeRef || typeof invokeRef !== "object")
187
+ return emptyUnion();
188
+ const invokedKind = invokeRef.kind;
189
+ if (!invokedKind)
190
+ return emptyUnion();
191
+ const definition = definitionFor(invokedKind, ctx.defs, ctx.aliases);
192
+ if (!definition)
193
+ return { codes: new Map(), unbounded: true };
194
+ if (definition.throws?.passthrough) {
195
+ return resolvePassthroughAtCallSite(step, enclosingTryCodes);
196
+ }
197
+ // Named manifest: resolve the full chain (covers transitive inherit).
198
+ const invokeName = invokeRef.name;
199
+ if (invokeName) {
200
+ const target = ctx.allManifests.find((m) => m.metadata?.name === invokeName &&
201
+ (m.kind === invokedKind ||
202
+ ctx.aliases.resolveKind(m.kind) === invokedKind ||
203
+ m.kind === ctx.aliases.resolveKind(invokedKind)));
204
+ if (target)
205
+ return resolveThrowsUnion(target, ctx);
206
+ }
207
+ // Fall back to the definition's own explicit codes. Mark unbounded when the
208
+ // definition depends on call-site or transitive resolution we couldn't
209
+ // perform (no specific target manifest to recurse into).
210
+ const codes = codesFromDefinition(definition);
211
+ const unbounded = definition.throws?.inherit === true || definition.throws?.passthrough === true;
212
+ return { codes, unbounded };
213
+ }
214
+ /** Resolve a passthrough-style invocable at a specific call site. Recognised forms
215
+ * (see "passthrough: true" in the plan):
216
+ * - constant literal (no template) → `{ <literal> }`
217
+ * - `${{ 'FOO' }}` constant expression → `{ FOO }`
218
+ * - `${{ error.code }}` inside a catch → enclosing try's propagated union
219
+ * Anything else is unbounded; the analyzer flags it downstream. */
220
+ function resolvePassthroughAtCallSite(step, enclosingTryCodes) {
221
+ return resolveCodeExpression(step.inputs?.code, enclosingTryCodes);
222
+ }
223
+ /** Resolve the `code:` of a `throw:` step to a throws union. Uses the same
224
+ * recognised forms as passthrough call sites. */
225
+ function resolveThrowStepCode(throwSpec, enclosingTryCodes) {
226
+ return resolveCodeExpression(throwSpec.code, enclosingTryCodes);
227
+ }
228
+ function resolveCodeExpression(codeInput, enclosingTryCodes) {
229
+ if (typeof codeInput !== "string" || codeInput.length === 0) {
230
+ return { codes: new Map(), unbounded: true };
231
+ }
232
+ const match = codeInput.match(/^\s*\$\{\{\s*([\s\S]+?)\s*\}\}\s*$/);
233
+ if (!match) {
234
+ return { codes: new Map([[codeInput, {}]]), unbounded: false };
235
+ }
236
+ const expr = match[1].trim();
237
+ const litMatch = expr.match(/^'([^']+)'$|^"([^"]+)"$/);
238
+ if (litMatch) {
239
+ const code = litMatch[1] ?? litMatch[2];
240
+ return { codes: new Map([[code, {}]]), unbounded: false };
241
+ }
242
+ if (expr === "error.code") {
243
+ if (enclosingTryCodes) {
244
+ const codes = new Map();
245
+ for (const c of enclosingTryCodes)
246
+ codes.set(c, {});
247
+ return { codes, unbounded: false };
248
+ }
249
+ return { codes: new Map(), unbounded: true };
250
+ }
251
+ return { codes: new Map(), unbounded: true };
252
+ }
package/dist/types.d.ts CHANGED
@@ -42,6 +42,14 @@ export interface ManifestAdapter {
42
42
  source: string;
43
43
  }>;
44
44
  resolveRelative(base: string, relative: string): string;
45
+ /** Expand glob patterns relative to a base source. Returns sources in the same
46
+ * format as read().source — suitable to pass back into read() / resolveRelative().
47
+ * Optional — only filesystem-capable adapters implement this. */
48
+ expandGlob?(base: string, patterns: string[]): Promise<string[]>;
49
+ /** Walk parent directories from fileUrl looking for the nearest telo.yaml.
50
+ * Returns the source in the same format as read().source, or null if none found.
51
+ * Optional — only filesystem-capable adapters implement this. */
52
+ resolveOwnerOf?(fileUrl: string): Promise<string | null>;
45
53
  }
46
54
  export interface LoadOptions {
47
55
  /** When true, each YAML document is passed through the CEL precompiler before being
@@ -59,6 +67,9 @@ export interface LoaderInitOptions {
59
67
  includeRegistryAdapter?: boolean;
60
68
  /** Base URL used by built-in RegistryAdapter when enabled. */
61
69
  registryUrl?: string;
70
+ /** Handlers for CEL stdlib functions (e.g. `sha256`). Analyzer-only callers may
71
+ * omit this and get throwing stubs; runtime callers (kernel) must supply real impls. */
72
+ celHandlers?: import("./cel-environment.js").CelHandlers;
62
73
  }
63
74
  export interface AnalysisOptions {
64
75
  strictContexts?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;qHACqH;AACrH,eAAO,MAAM,kBAAkB;;;;;CAKrB,CAAC;AACX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC;AAE9F,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,cAAc,CAAC;AAErD,MAAM,WAAW,QAAQ;IACvB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,QAAQ,CAAC;CACf;AAED;;oDAEoD;AACpD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE/C;6EAC6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;CACzD;AAED,MAAM,WAAW,WAAW;IAC1B;;;+EAG2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,aAAa,CAAC,EAAE,eAAe,EAAE,CAAC;IAClC,sDAAsD;IACtD,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,0DAA0D;IAC1D,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;gEAKgE;AAChE,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,OAAO,qBAAqB,EAAE,aAAa,CAAC;IACtD,WAAW,CAAC,EAAE,OAAO,0BAA0B,EAAE,kBAAkB,CAAC;CACrE"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;qHACqH;AACrH,eAAO,MAAM,kBAAkB;;;;;CAKrB,CAAC;AACX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC;AAE9F,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,cAAc,CAAC;AAErD,MAAM,WAAW,QAAQ;IACvB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,QAAQ,CAAC;CACf;AAED;;oDAEoD;AACpD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE/C;6EAC6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAExD;;sEAEkE;IAClE,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEjE;;sEAEkE;IAClE,cAAc,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,WAAW;IAC1B;;;+EAG2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,aAAa,CAAC,EAAE,eAAe,EAAE,CAAC;IAClC,sDAAsD;IACtD,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,0DAA0D;IAC1D,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;6FACyF;IACzF,WAAW,CAAC,EAAE,OAAO,sBAAsB,EAAE,WAAW,CAAC;CAC1D;AAED,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;gEAKgE;AAChE,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,OAAO,qBAAqB,EAAE,aAAa,CAAC;IACtD,WAAW,CAAC,EAAE,OAAO,0BAA0B,EAAE,kBAAkB,CAAC;CACrE"}
@@ -8,7 +8,7 @@ export function resolveTypeFieldToSchema(value, allManifests) {
8
8
  if (!value)
9
9
  return undefined;
10
10
  if (typeof value === "string") {
11
- // Named type reference — find a Kernel.Type resource by name
11
+ // Named type reference — find a Telo.Type resource by name
12
12
  const typeManifest = allManifests.find((m) => m.metadata?.name === value &&
13
13
  typeof m.kind === "string" &&
14
14
  /\bType\b/.test(m.kind) &&
@@ -1 +1 @@
1
- {"version":3,"file":"validate-references.d.ts","sourceRoot":"","sources":["../src/validate-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA6C/F;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,GACvB,kBAAkB,EAAE,CAgQtB"}
1
+ {"version":3,"file":"validate-references.d.ts","sourceRoot":"","sources":["../src/validate-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAkD/F;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,GACvB,kBAAkB,EAAE,CAkQtB"}
@@ -2,7 +2,12 @@ import { isRefEntry, isScopeEntry, isSchemaFromEntry, isInlineResource, resolveF
2
2
  import { navigateJsonPointer } from "./schema-compat.js";
3
3
  import { DiagnosticSeverity } from "./types.js";
4
4
  const SOURCE = "telo-analyzer";
5
- const SYSTEM_KINDS = new Set(["Kernel.Definition", "Kernel.Abstract"]);
5
+ /** Kinds skipped by reference validation. Telo.Application and Telo.Library
6
+ * are intentionally not here: Application has `targets` with x-telo-ref that
7
+ * must be validated, and Library has no ref-bearing fields so flows through
8
+ * harmlessly. Telo.Import is also not here for the same reason — its
9
+ * `source` field isn't x-telo-ref, so nothing gets checked. */
10
+ const SYSTEM_KINDS = new Set(["Telo.Definition", "Telo.Abstract"]);
6
11
  /**
7
12
  * Checks whether `kind` satisfies the ref constraint in `entry`.
8
13
  * Returns an empty array when valid, or mismatch error strings when not.
@@ -19,7 +24,7 @@ function checkKind(kind, entry, registry, aliases) {
19
24
  const targetDef = registry.resolve(targetKind);
20
25
  if (!targetDef)
21
26
  return [];
22
- if (targetDef.kind === "Kernel.Abstract") {
27
+ if (targetDef.kind === "Telo.Abstract") {
23
28
  const implementing = registry.getByExtends(targetKind);
24
29
  if (implementing.length === 0)
25
30
  return []; // partial context — no implementations loaded yet
@@ -59,7 +64,7 @@ export function validateReferences(resources, context) {
59
64
  if (!aliases || !registry)
60
65
  return diagnostics;
61
66
  // Build outer resource lookup by name for resolution check.
62
- // Exclude system kinds (Kernel.Definition) — they are type blueprints, not instances,
67
+ // Exclude system kinds (Telo.Definition) — they are type blueprints, not instances,
63
68
  // and their names (e.g. "Server", "Job") would shadow user-defined resource instances.
64
69
  const byName = new Map();
65
70
  for (const r of resources) {
@@ -74,6 +79,7 @@ export function validateReferences(resources, context) {
74
79
  continue;
75
80
  const resourceLabel = `${r.kind}/${r.metadata.name}`;
76
81
  const resourceData = { kind: r.kind, name: r.metadata.name };
82
+ const filePath = r.metadata?.source;
77
83
  // Collect scope visibility prefixes (JSON Pointer → dot prefix) and their manifests.
78
84
  // scope field path → flat array of ResourceManifest declared in that scope.
79
85
  const scopeManifestsByPointer = new Map();
@@ -131,7 +137,7 @@ export function validateReferences(resources, context) {
131
137
  code: "UNRESOLVED_REFERENCE",
132
138
  source: SOURCE,
133
139
  message: `${resourceLabel}: reference at '${fieldPath}' → resource '${val}' not found`,
134
- data: { resource: resourceData, path: fieldPath },
140
+ data: { resource: resourceData, filePath, path: fieldPath },
135
141
  });
136
142
  continue;
137
143
  }
@@ -142,7 +148,7 @@ export function validateReferences(resources, context) {
142
148
  code: "REFERENCE_KIND_MISMATCH",
143
149
  source: SOURCE,
144
150
  message: `${resourceLabel}: reference at '${fieldPath}' → ${kindErrors.join("; ")}`,
145
- data: { resource: resourceData, path: fieldPath },
151
+ data: { resource: resourceData, filePath, path: fieldPath },
146
152
  });
147
153
  }
148
154
  continue;
@@ -160,7 +166,7 @@ export function validateReferences(resources, context) {
160
166
  code: "INVALID_REFERENCE",
161
167
  source: SOURCE,
162
168
  message: `${resourceLabel}: reference at '${fieldPath}' must have string 'kind' and 'name' fields`,
163
- data: { resource: resourceData, path: fieldPath },
169
+ data: { resource: resourceData, filePath, path: fieldPath },
164
170
  });
165
171
  continue;
166
172
  }
@@ -172,7 +178,7 @@ export function validateReferences(resources, context) {
172
178
  code: "REFERENCE_KIND_MISMATCH",
173
179
  source: SOURCE,
174
180
  message: `${resourceLabel}: reference at '${fieldPath}' → ${kindErrors.join("; ")}`,
175
- data: { resource: resourceData, path: fieldPath },
181
+ data: { resource: resourceData, filePath, path: fieldPath },
176
182
  });
177
183
  }
178
184
  // 3. Resolution check — resource with this name must exist.
@@ -184,7 +190,7 @@ export function validateReferences(resources, context) {
184
190
  code: "UNRESOLVED_REFERENCE",
185
191
  source: SOURCE,
186
192
  message: `${resourceLabel}: reference at '${fieldPath}' → resource '${refVal.name}' not found`,
187
- data: { resource: resourceData, path: fieldPath },
193
+ data: { resource: resourceData, filePath, path: fieldPath },
188
194
  });
189
195
  }
190
196
  }
@@ -202,6 +208,7 @@ export function validateReferences(resources, context) {
202
208
  continue;
203
209
  const resourceLabel = `${r.kind}/${r.metadata.name}`;
204
210
  const resourceData = { kind: r.kind, name: r.metadata.name };
211
+ const filePath = r.metadata?.source;
205
212
  for (const [fieldPath, entry] of fieldMap) {
206
213
  if (!isSchemaFromEntry(entry))
207
214
  continue;
@@ -215,7 +222,7 @@ export function validateReferences(resources, context) {
215
222
  code: "INVALID_SCHEMA_FROM",
216
223
  source: SOURCE,
217
224
  message: `${resourceLabel}: x-telo-schema-from "${schemaFrom}" must contain at least one "/" to separate anchor from JSON Pointer`,
218
- data: { resource: resourceData, path: fieldPath },
225
+ data: { resource: resourceData, filePath, path: fieldPath },
219
226
  });
220
227
  continue;
221
228
  }
@@ -255,7 +262,7 @@ export function validateReferences(resources, context) {
255
262
  code: "SCHEMA_FROM_MISSING_PATH",
256
263
  source: SOURCE,
257
264
  message: `${resourceLabel}: x-telo-schema-from at '${fieldPath}' → kind '${refVal.kind}' has no schema`,
258
- data: { resource: resourceData, path: fieldPath },
265
+ data: { resource: resourceData, filePath, path: fieldPath },
259
266
  });
260
267
  continue;
261
268
  }
@@ -266,7 +273,7 @@ export function validateReferences(resources, context) {
266
273
  code: "SCHEMA_FROM_MISSING_PATH",
267
274
  source: SOURCE,
268
275
  message: `${resourceLabel}: x-telo-schema-from at '${fieldPath}' → kind '${refVal.kind}' has no schema path '${jsonPointer}'`,
269
- data: { resource: resourceData, path: fieldPath },
276
+ data: { resource: resourceData, filePath, path: fieldPath },
270
277
  });
271
278
  continue;
272
279
  }
@@ -277,7 +284,7 @@ export function validateReferences(resources, context) {
277
284
  code: "DEPENDENT_SCHEMA_MISMATCH",
278
285
  source: SOURCE,
279
286
  message: `${resourceLabel}: '${fieldPath}' does not match schema from '${refVal.kind}${jsonPointer}': ${issue}`,
280
- data: { resource: resourceData, path: fieldPath },
287
+ data: { resource: resourceData, filePath, path: fieldPath },
281
288
  });
282
289
  }
283
290
  }
@@ -0,0 +1,8 @@
1
+ import type { Environment } from "@marcbachmann/cel-js";
2
+ import type { ResourceManifest } from "@telorun/sdk";
3
+ import type { AliasResolver } from "./alias-resolver.js";
4
+ import type { DefinitionRegistry } from "./definition-registry.js";
5
+ import { type AnalysisDiagnostic } from "./types.js";
6
+ /** Entry point — invoked once per analyze() run. */
7
+ export declare function validateThrowsCoverage(manifests: ResourceManifest[], defs: DefinitionRegistry, aliases: AliasResolver, env: Environment): AnalysisDiagnostic[];
8
+ //# sourceMappingURL=validate-throws-coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-throws-coverage.d.ts","sourceRoot":"","sources":["../src/validate-throws-coverage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA4dzE,oDAAoD;AACpD,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,WAAW,GACf,kBAAkB,EAAE,CAwCtB"}