@telorun/analyzer 0.1.4 → 0.2.1

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 (64) 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 +52 -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 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1 -0
  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 +1 -0
  22. package/dist/manifest-loader.d.ts.map +1 -1
  23. package/dist/manifest-loader.js +36 -14
  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 +3 -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 +2 -2
  45. package/src/analyzer.ts +60 -26
  46. package/src/builtins.ts +52 -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 -0
  51. package/src/kernel-globals.ts +19 -10
  52. package/src/manifest-loader.ts +40 -14
  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 +3 -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 -17
  63. package/dist/adapters/node-adapter.d.ts.map +0 -1
  64. package/dist/adapters/node-adapter.js +0 -71
package/src/analyzer.ts CHANGED
@@ -1,10 +1,16 @@
1
1
  import type { ResourceDefinition, ResourceManifest } from "@telorun/sdk";
2
+ import type { Environment } from "@marcbachmann/cel-js";
2
3
  import { AliasResolver } from "./alias-resolver.js";
3
4
  import { AnalysisRegistry } from "./analysis-registry.js";
4
- import { buildTypedCelEnvironment, celEnvironment } from "./cel-environment.js";
5
+ import {
6
+ buildCelEnvironment,
7
+ buildTypedCelEnvironment,
8
+ type CelHandlers,
9
+ } from "./cel-environment.js";
5
10
  import { DefinitionRegistry } from "./definition-registry.js";
6
11
  import { buildDependencyGraph, formatCycle } from "./dependency-graph.js";
7
12
  import { buildKernelGlobalsSchema, mergeKernelGlobalsIntoContext } from "./kernel-globals.js";
13
+ import { isModuleKind } from "./module-kinds.js";
8
14
  import { normalizeInlineResources } from "./normalize-inline-resources.js";
9
15
  import {
10
16
  celTypeSatisfiesJsonSchema,
@@ -22,6 +28,7 @@ import {
22
28
  validateChainAgainstSchema,
23
29
  } from "./validate-cel-context.js";
24
30
  import { validateReferences } from "./validate-references.js";
31
+ import { validateThrowsCoverage } from "./validate-throws-coverage.js";
25
32
 
26
33
  const TEMPLATE_REGEX = /\$\{\{\s*([^}]+?)\s*\}\}/g;
27
34
 
@@ -174,7 +181,8 @@ function collectCelTypeIssues(
174
181
  path: string,
175
182
  definition: { schema?: Record<string, any> },
176
183
  manifest: ResourceManifest,
177
- baseEnv: ReturnType<typeof buildTypedCelEnvironment>,
184
+ baseTypedEnv: Environment,
185
+ rootEnv: Environment,
178
186
  ): SchemaIssue[] {
179
187
  const issues: SchemaIssue[] = [];
180
188
 
@@ -184,11 +192,11 @@ function collectCelTypeIssues(
184
192
  const expr = exprMatch[1].trim();
185
193
 
186
194
  // Merge x-telo-context variables for this path if applicable
187
- let typedEnv = baseEnv;
195
+ let typedEnv = baseTypedEnv;
188
196
  if (definition.schema) {
189
197
  for (const ctx of extractContextsFromSchema(definition.schema)) {
190
198
  if (!pathMatchesScope(path, ctx.scope)) continue;
191
- typedEnv = buildTypedCelEnvironment(manifest, ctx.schema);
199
+ typedEnv = buildTypedCelEnvironment(rootEnv, manifest, ctx.schema);
192
200
  break;
193
201
  }
194
202
  }
@@ -200,7 +208,15 @@ function collectCelTypeIssues(
200
208
  /* degrade gracefully */
201
209
  }
202
210
 
203
- if (checkResult?.valid && checkResult.type && schema) {
211
+ if (checkResult?.valid === false && checkResult.error) {
212
+ // env.check() rejected the expression itself — e.g. wrong method, wrong
213
+ // argument types, wrong operator overload. Surface the first line of the
214
+ // error message; the tail is a source-code caret diagram we don't need.
215
+ const message = String((checkResult.error as { message?: string }).message ?? checkResult.error)
216
+ .split("\n")[0]
217
+ .trim();
218
+ issues.push({ message: `CEL type error: ${message}`, path });
219
+ } else if (checkResult?.valid && checkResult.type && schema) {
204
220
  const celType = checkResult.type.split("<")[0]!;
205
221
  if (!celTypeSatisfiesJsonSchema(celType, schema)) {
206
222
  issues.push({
@@ -223,7 +239,8 @@ function collectCelTypeIssues(
223
239
  `${path}[${i}]`,
224
240
  definition,
225
241
  manifest,
226
- baseEnv,
242
+ baseTypedEnv,
243
+ rootEnv,
227
244
  ),
228
245
  );
229
246
  }
@@ -237,7 +254,8 @@ function collectCelTypeIssues(
237
254
  path ? `${path}.${k}` : k,
238
255
  definition,
239
256
  manifest,
240
- baseEnv,
257
+ baseTypedEnv,
258
+ rootEnv,
241
259
  ),
242
260
  );
243
261
  }
@@ -246,7 +264,17 @@ function collectCelTypeIssues(
246
264
  return issues;
247
265
  }
248
266
 
267
+ export interface StaticAnalyzerOptions {
268
+ celHandlers?: CelHandlers;
269
+ }
270
+
249
271
  export class StaticAnalyzer {
272
+ private readonly celEnv: Environment;
273
+
274
+ constructor(options: StaticAnalyzerOptions = {}) {
275
+ this.celEnv = buildCelEnvironment(options.celHandlers);
276
+ }
277
+
250
278
  analyze(
251
279
  manifests: ResourceManifest[],
252
280
  options?: AnalysisOptions,
@@ -262,17 +290,18 @@ export class StaticAnalyzer {
262
290
  const defs = ctx?.definitions ?? new DefinitionRegistry();
263
291
 
264
292
  // Register module identities and aliases.
265
- // The root Kernel.Module provides its own identity; imported modules surface their
266
- // identity via resolvedModuleName/resolvedNamespace stamped onto the Kernel.Import
267
- // by the loader (so we don't need to include imported Kernel.Module manifests in
268
- // the analysis set, avoiding false reference errors in the parent context).
293
+ // The root module doc (Telo.Application or Telo.Library) provides its own
294
+ // identity; imported modules surface their identity via resolvedModuleName/
295
+ // resolvedNamespace stamped onto the Telo.Import by the loader (so we don't
296
+ // need to include imported module manifests in the analysis set, avoiding false
297
+ // reference errors in the parent context).
269
298
  for (const m of manifests) {
270
- if (m.kind === "Kernel.Module") {
299
+ if (isModuleKind(m.kind)) {
271
300
  const namespace = ((m.metadata as any).namespace as string | undefined) ?? null;
272
301
  const moduleName = m.metadata.name as string;
273
302
  if (moduleName) defs.registerModuleIdentity(namespace, moduleName);
274
303
  }
275
- if (m.kind === "Kernel.Import") {
304
+ if (m.kind === "Telo.Import") {
276
305
  const alias = m.metadata.name as string;
277
306
  const source = (m as any).source as string | undefined;
278
307
  const exportedKinds: string[] = (m as any).exports?.kinds ?? [];
@@ -292,11 +321,11 @@ export class StaticAnalyzer {
292
321
  }
293
322
  }
294
323
 
295
- // Register definitions from Kernel.Definition resources.
324
+ // Register definitions from Telo.Definition resources.
296
325
  // Normalize alias-prefixed `capability` to canonical form so extendedBy lookup works
297
326
  // (e.g. "Workflow.Backend" → "workflow.Backend" when "Workflow" is a known alias).
298
327
  for (const m of manifests) {
299
- if (m.kind === "Kernel.Definition") {
328
+ if (m.kind === "Telo.Definition") {
300
329
  const def = m as unknown as ResourceDefinition;
301
330
  const resolvedCapability = def.capability
302
331
  ? (aliases.resolveKind(def.capability) ?? def.capability)
@@ -324,17 +353,18 @@ export class StaticAnalyzer {
324
353
 
325
354
  // Validate each non-definition, non-system resource
326
355
  for (const m of allManifests) {
356
+ const filePath = (m.metadata as { source?: string } | undefined)?.source;
327
357
  if (!m.kind || !m.metadata?.name) {
328
358
  diagnostics.push({
329
359
  severity: DiagnosticSeverity.Error,
330
360
  code: "MISSING_KIND_OR_NAME",
331
361
  source: SOURCE,
332
362
  message: "Resource is missing required 'kind' or 'metadata.name' field.",
333
- data: { path: !m.kind ? "kind" : "metadata.name" },
363
+ data: { filePath, path: !m.kind ? "kind" : "metadata.name" },
334
364
  });
335
365
  continue;
336
366
  }
337
- if (m.kind === "Kernel.Definition" || m.kind === "Kernel.Abstract") {
367
+ if (m.kind === "Telo.Definition" || m.kind === "Telo.Abstract") {
338
368
  continue;
339
369
  }
340
370
 
@@ -357,8 +387,8 @@ export class StaticAnalyzer {
357
387
  severity: DiagnosticSeverity.Error,
358
388
  code: "UNDEFINED_KIND",
359
389
  source: SOURCE,
360
- message: `No Kernel.Definition found for kind '${m.kind}'.${hint}`,
361
- data: { resource, path: "kind" },
390
+ message: `No Telo.Definition found for kind '${m.kind}'.${hint}`,
391
+ data: { resource, filePath, path: "kind" },
362
392
  });
363
393
  continue;
364
394
  }
@@ -379,8 +409,8 @@ export class StaticAnalyzer {
379
409
  }
380
410
  : definition.schema;
381
411
  // Phase 1: CEL type checking — walk data+schema together, check env.check() return types
382
- const baseEnv = buildTypedCelEnvironment(m);
383
- const celIssues = collectCelTypeIssues(m, schema, "", definition, m, baseEnv);
412
+ const baseTypedEnv = buildTypedCelEnvironment(this.celEnv, m);
413
+ const celIssues = collectCelTypeIssues(m, schema, "", definition, m, baseTypedEnv, this.celEnv);
384
414
  // Phase 2+3: AJV on substituted data — CEL fields replaced with typed placeholders
385
415
  const ajvIssues = validateAgainstSchema(substituteCelFields(m, schema), schema);
386
416
  const issues = [...celIssues, ...ajvIssues];
@@ -390,7 +420,7 @@ export class StaticAnalyzer {
390
420
  code: "SCHEMA_VIOLATION",
391
421
  source: SOURCE,
392
422
  message: `${m.kind}/${resource.name}: ${issue.message}`,
393
- data: { resource, path: issue.path },
423
+ data: { resource, filePath, path: issue.path },
394
424
  });
395
425
  }
396
426
  }
@@ -401,6 +431,7 @@ export class StaticAnalyzer {
401
431
  // Validate CEL syntax and context variable access in all manifests
402
432
  for (const m of allManifests) {
403
433
  const resource = { kind: m.kind, name: m.metadata?.name as string };
434
+ const filePath = (m.metadata as { source?: string } | undefined)?.source;
404
435
 
405
436
  const resolvedKind = aliases.resolveKind(m.kind);
406
437
  const mDefinition =
@@ -416,16 +447,16 @@ export class StaticAnalyzer {
416
447
  : undefined;
417
448
 
418
449
  walkCelExpressions(m, "", (expr, path) => {
419
- let parsed: ReturnType<typeof celEnvironment.parse> | undefined;
450
+ let parsed: ReturnType<typeof this.celEnv.parse> | undefined;
420
451
  try {
421
- parsed = celEnvironment.parse(expr);
452
+ parsed = this.celEnv.parse(expr);
422
453
  } catch (e) {
423
454
  diagnostics.push({
424
455
  severity: DiagnosticSeverity.Error,
425
456
  code: "CEL_SYNTAX_ERROR",
426
457
  source: SOURCE,
427
458
  message: `CEL syntax error at ${path}: ${e instanceof Error ? e.message : String(e)}`,
428
- data: { resource, path },
459
+ data: { resource, filePath, path },
429
460
  });
430
461
  return;
431
462
  }
@@ -483,7 +514,7 @@ export class StaticAnalyzer {
483
514
  code: "CEL_UNKNOWN_FIELD",
484
515
  source: SOURCE,
485
516
  message: `${m.kind}/${resource.name}: CEL at '${path}': ${err}`,
486
- data: { resource, path },
517
+ data: { resource, filePath, path },
487
518
  });
488
519
  }
489
520
  });
@@ -492,6 +523,9 @@ export class StaticAnalyzer {
492
523
  // Validate resource references (Phase 3)
493
524
  diagnostics.push(...validateReferences(allManifests, { aliases, definitions: defs }));
494
525
 
526
+ // Validate throws: declarations and catches: coverage (rules 1, 2, 4, 7)
527
+ diagnostics.push(...validateThrowsCoverage(allManifests, defs, aliases, this.celEnv));
528
+
495
529
  return diagnostics;
496
530
  }
497
531
 
package/src/builtins.ts CHANGED
@@ -1,21 +1,21 @@
1
1
  import type { ResourceDefinition } from "@telorun/sdk";
2
2
 
3
3
  export const KERNEL_BUILTINS: ResourceDefinition[] = [
4
- { kind: "Kernel.Abstract", metadata: { name: "Template", module: "Kernel" } },
5
- { kind: "Kernel.Abstract", metadata: { name: "Runnable", module: "Kernel" } },
6
- { kind: "Kernel.Abstract", metadata: { name: "Service", module: "Kernel" } },
7
- { kind: "Kernel.Abstract", metadata: { name: "Invocable", module: "Kernel" } },
8
- { kind: "Kernel.Abstract", metadata: { name: "Mount", module: "Kernel" } },
9
- { kind: "Kernel.Abstract", metadata: { name: "Type", module: "Kernel" } },
4
+ { kind: "Telo.Abstract", metadata: { name: "Template", module: "Telo" } },
5
+ { kind: "Telo.Abstract", metadata: { name: "Runnable", module: "Telo" } },
6
+ { kind: "Telo.Abstract", metadata: { name: "Service", module: "Telo" } },
7
+ { kind: "Telo.Abstract", metadata: { name: "Invocable", module: "Telo" } },
8
+ { kind: "Telo.Abstract", metadata: { name: "Mount", module: "Telo" } },
9
+ { kind: "Telo.Abstract", metadata: { name: "Type", module: "Telo" } },
10
10
  {
11
- kind: "Kernel.Abstract",
12
- metadata: { name: "Provider", module: "Kernel" },
11
+ kind: "Telo.Abstract",
12
+ metadata: { name: "Provider", module: "Telo" },
13
13
  schema: { "x-telo-eval": "compile" },
14
14
  },
15
15
  {
16
- kind: "Kernel.Definition",
17
- metadata: { name: "Abstract", module: "Kernel" },
18
- capability: "Kernel.Template",
16
+ kind: "Telo.Definition",
17
+ metadata: { name: "Abstract", module: "Telo" },
18
+ capability: "Telo.Template",
19
19
  schema: {
20
20
  type: "object",
21
21
  properties: {
@@ -33,15 +33,15 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
33
33
  },
34
34
  },
35
35
  {
36
- kind: "Kernel.Definition",
37
- metadata: { name: "Definition", module: "Kernel" },
38
- capability: "Kernel.Template",
36
+ kind: "Telo.Definition",
37
+ metadata: { name: "Definition", module: "Telo" },
38
+ capability: "Telo.Template",
39
39
  schema: { type: "object" },
40
40
  },
41
41
  {
42
- kind: "Kernel.Definition",
43
- metadata: { name: "Import", module: "Kernel" },
44
- capability: "Kernel.Template",
42
+ kind: "Telo.Definition",
43
+ metadata: { name: "Import", module: "Telo" },
44
+ capability: "Telo.Template",
45
45
  schema: {
46
46
  type: "object",
47
47
  properties: {
@@ -61,9 +61,9 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
61
61
  },
62
62
  },
63
63
  {
64
- kind: "Kernel.Definition",
65
- metadata: { name: "Module", module: "Kernel" },
66
- capability: "Kernel.Template",
64
+ kind: "Telo.Definition",
65
+ metadata: { name: "Application", module: "Telo" },
66
+ capability: "Telo.Template",
67
67
  schema: {
68
68
  type: "object",
69
69
  properties: {
@@ -85,14 +85,12 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
85
85
  default: "shared",
86
86
  },
87
87
  keepAlive: { type: "boolean", default: false },
88
- variables: { type: "object" },
89
- secrets: { type: "object" },
90
88
  targets: {
91
89
  type: "array",
92
90
  items: {
93
91
  anyOf: [
94
- { type: "string", "x-telo-ref": "kernel#Runnable" },
95
- { type: "string", "x-telo-ref": "kernel#Service" },
92
+ { type: "string", "x-telo-ref": "telo#Runnable" },
93
+ { type: "string", "x-telo-ref": "telo#Service" },
96
94
  ],
97
95
  },
98
96
  },
@@ -100,6 +98,36 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
100
98
  type: "array",
101
99
  items: { type: "string" },
102
100
  },
101
+ },
102
+ required: ["metadata"],
103
+ additionalProperties: false,
104
+ },
105
+ },
106
+ {
107
+ kind: "Telo.Definition",
108
+ metadata: { name: "Library", module: "Telo" },
109
+ capability: "Telo.Template",
110
+ schema: {
111
+ type: "object",
112
+ properties: {
113
+ kind: { type: "string" },
114
+ metadata: {
115
+ type: "object",
116
+ properties: {
117
+ name: { type: "string" },
118
+ version: { type: "string" },
119
+ source: { type: "string" },
120
+ module: { type: "string" },
121
+ },
122
+ required: ["name"],
123
+ additionalProperties: true,
124
+ },
125
+ variables: { type: "object" },
126
+ secrets: { type: "object" },
127
+ include: {
128
+ type: "array",
129
+ items: { type: "string" },
130
+ },
103
131
  exports: {
104
132
  type: "object",
105
133
  properties: {
@@ -1,36 +1,59 @@
1
- import type { ResourceManifest } from "@telorun/sdk";
2
1
  import { Environment } from "@marcbachmann/cel-js";
2
+ import type { ResourceManifest } from "@telorun/sdk";
3
3
  import { jsonSchemaToCelType } from "./schema-compat.js";
4
4
 
5
- export const celEnvironment = new Environment({ unlistedVariablesAreDyn: true })
6
- .registerFunction("join(list, string): string", (list: unknown[], sep: string) =>
7
- list.map(String).join(sep),
8
- )
9
- .registerFunction("keys(map): list", (map: unknown) => {
10
- if (map instanceof Map) return [...map.keys()];
11
- return Object.keys(map as Record<string, unknown>);
12
- })
13
- .registerFunction("values(map): list", (map: unknown) => {
14
- if (map instanceof Map) return [...map.values()];
15
- return Object.values(map as Record<string, unknown>);
16
- });
5
+ export interface CelHandlers {
6
+ sha256: (s: string) => string;
7
+ }
8
+
9
+ const stub = (name: string) => () => {
10
+ throw new Error(
11
+ `${name}() is not available in this environment. ` +
12
+ `Construct StaticAnalyzer or Loader with celHandlers to enable it.`,
13
+ );
14
+ };
15
+
16
+ const STUB_HANDLERS: CelHandlers = {
17
+ sha256: stub("sha256"),
18
+ };
19
+
20
+ /** Build a CEL `Environment` with Telo's stdlib of functions. Always registers the
21
+ * same function signatures (so `env.check()` succeeds for type-inference) — the
22
+ * handlers govern what the function does when called at runtime. Analyzer-only
23
+ * callers can omit handlers; runtime callers (kernel) must supply real ones. */
24
+ export function buildCelEnvironment(handlers: CelHandlers = STUB_HANDLERS): Environment {
25
+ return new Environment({ unlistedVariablesAreDyn: true })
26
+ .registerFunction("join(list, string): string", (list: unknown[], sep: string) =>
27
+ list.map(String).join(sep),
28
+ )
29
+ .registerFunction("keys(map): list", (map: unknown) => {
30
+ if (map instanceof Map) return [...map.keys()];
31
+ return Object.keys(map as Record<string, unknown>);
32
+ })
33
+ .registerFunction("values(map): list", (map: unknown) => {
34
+ if (map instanceof Map) return [...map.values()];
35
+ return Object.values(map as Record<string, unknown>);
36
+ })
37
+ .registerFunction("sha256(string): string", (s: string) => handlers.sha256(s));
38
+ }
17
39
 
18
- /** Clone `celEnvironment` and register typed variable declarations so that
40
+ /** Clone `baseEnv` and register typed variable declarations so that
19
41
  * `env.check(expr)` can infer return types for expressions referencing known variables.
20
42
  *
21
43
  * - `variables`: typed from the manifest's `variables` field if it is a schema map
22
- * (only `Kernel.Module` resources carry this); otherwise registered as `map` (dyn).
44
+ * (only module-identity docs — `Telo.Application` / `Telo.Library` — carry this); otherwise registered as `map` (dyn).
23
45
  * - `secrets`, `resources`, `env`: always `map` (dyn — output schemas unknown).
24
46
  * - `extraContextSchema`: additional variables from an `x-telo-context` annotation.
25
47
  *
26
48
  * NOTE: The set of kernel globals registered here must match `KERNEL_GLOBAL_NAMES`
27
49
  * in kernel-globals.ts, which is used for chain-access validation. */
28
50
  export function buildTypedCelEnvironment(
51
+ baseEnv: Environment,
29
52
  manifest: ResourceManifest,
30
53
  extraContextSchema?: Record<string, any> | null,
31
54
  ): Environment {
32
55
  try {
33
- const env = celEnvironment.clone();
56
+ const env = baseEnv.clone();
34
57
 
35
58
  // Build typed ObjectSchema from manifest.variables if it looks like a schema map
36
59
  const vars = (manifest as Record<string, unknown>).variables;
@@ -67,6 +90,6 @@ export function buildTypedCelEnvironment(
67
90
 
68
91
  return env;
69
92
  } catch {
70
- return celEnvironment.clone();
93
+ return baseEnv.clone();
71
94
  }
72
95
  }
@@ -20,10 +20,10 @@ export class DefinitionRegistry {
20
20
  /** Reverse inheritance index: parent kind → direct child kinds. */
21
21
  private readonly extendedBy = new Map<string, string[]>();
22
22
  /** Module identity table: identity string → canonical module name.
23
- * "kernel" → "Kernel", "std/pipeline" → "pipeline", etc. */
23
+ * "telo" → "Telo", "std/pipeline" → "pipeline", etc. */
24
24
  private readonly identityMap = new Map<string, string>();
25
25
  /** Reverse identity table: canonical module name → full identity string.
26
- * "Kernel" → "kernel", "pipeline" → "std/pipeline", etc.
26
+ * "Telo" → "telo", "pipeline" → "std/pipeline", etc.
27
27
  * Used to compute definition $id values for the AJV schema store. */
28
28
  private readonly reverseIdentityMap = new Map<string, string>();
29
29
 
@@ -40,10 +40,10 @@ export class DefinitionRegistry {
40
40
  this.extendedBy.set(definition.capability, [key]);
41
41
  }
42
42
  }
43
- // Auto-register the kernel identity when any Kernel built-in is registered.
44
- if (definition.kind === "Kernel.Abstract" && mod === "Kernel") {
45
- this.identityMap.set("kernel", "Kernel");
46
- this.reverseIdentityMap.set("Kernel", "kernel");
43
+ // Auto-register the telo identity when any Telo built-in is registered.
44
+ if (definition.kind === "Telo.Abstract" && mod === "Telo") {
45
+ this.identityMap.set("telo", "Telo");
46
+ this.reverseIdentityMap.set("Telo", "telo");
47
47
  }
48
48
  // If identity is already known, register the schema in AJV immediately.
49
49
  if (mod && definition.schema) {
@@ -52,11 +52,11 @@ export class DefinitionRegistry {
52
52
  }
53
53
 
54
54
  /** Register a module identity for x-telo-ref resolution.
55
- * Call once per Kernel.Module manifest when the manifest is loaded.
56
- * @param namespace The module's metadata.namespace (e.g. "std"), or null for kernel built-ins.
55
+ * Call once per module doc (Telo.Application or Telo.Library) when the manifest is loaded.
56
+ * @param namespace The module's metadata.namespace (e.g. "std"), or null for telo built-ins.
57
57
  * @param moduleName The module's metadata.name (e.g. "pipeline", "http-server"). */
58
58
  registerModuleIdentity(namespace: string | null, moduleName: string): void {
59
- const identity = namespace ? `${namespace}/${moduleName}` : "kernel";
59
+ const identity = namespace ? `${namespace}/${moduleName}` : "telo";
60
60
  this.identityMap.set(identity, moduleName);
61
61
  this.reverseIdentityMap.set(moduleName, identity);
62
62
  // Retroactively register AJV schemas for definitions of this module already in the registry.
@@ -110,7 +110,7 @@ export class DefinitionRegistry {
110
110
  * Splits on "#", looks up the left side in the identity table, and returns
111
111
  * "<canonicalModule>.<TypeName>".
112
112
  *
113
- * "kernel#Invocable" → "Kernel.Invocable"
113
+ * "telo#Invocable" → "Telo.Invocable"
114
114
  * "std/pipeline#Job" → "pipeline.Job"
115
115
  * "std/http-server#Server" → "http-server.Server"
116
116
  *
@@ -17,8 +17,15 @@ export interface DependencyGraph {
17
17
  cycle?: ReadonlyArray<ResourceNode>;
18
18
  }
19
19
 
20
- /** System resource kinds that are not runtime nodes in the dependency graph. */
21
- const SYSTEM_KINDS = new Set(["Kernel.Definition", "Kernel.Import"]);
20
+ /** System resource kinds that are not runtime nodes in the dependency graph.
21
+ * Module-identity docs (Telo.Application, Telo.Library) are intentionally
22
+ * not in this set: an Application's `targets` use `x-telo-ref` to real
23
+ * Runnable/Service resources, so the Application legitimately depends on
24
+ * them in boot order — modeling that as a graph edge is correct. A Library
25
+ * has no `targets`, so it becomes a zero-edge node, which is harmless.
26
+ * If the graph is ever consumed as "things to init", skip these kinds at
27
+ * the consumer site; the controller already runs them separately. */
28
+ const SYSTEM_KINDS = new Set(["Telo.Definition", "Telo.Import"]);
22
29
 
23
30
  const nodeKey = (kind: string, name: string) => `${kind}\0${name}`;
24
31
 
package/src/index.ts CHANGED
@@ -3,6 +3,8 @@ export { RegistryAdapter } from "./adapters/registry-adapter.js";
3
3
  export { AnalysisRegistry } from "./analysis-registry.js";
4
4
  export { StaticAnalyzer } from "./analyzer.js";
5
5
  export { Loader } from "./manifest-loader.js";
6
+ export { MODULE_KINDS, isModuleKind } from "./module-kinds.js";
7
+ export type { ModuleKind } from "./module-kinds.js";
6
8
  export { DEFAULT_MANIFEST_FILENAME, DiagnosticSeverity } from "./types.js";
7
9
  export type {
8
10
  AnalysisDiagnostic,
@@ -7,16 +7,17 @@ import type { ResourceManifest } from "@telorun/sdk";
7
7
  * must stay in sync with this list.
8
8
  *
9
9
  * Note: `env` is only available in the root module context. Child modules
10
- * loaded via Kernel.Import do not receive host environment variables.
10
+ * loaded via Telo.Import do not receive host environment variables.
11
11
  * There is no `imports` namespace at runtime — import snapshots are stored
12
12
  * under `resources.<alias>`.
13
13
  */
14
14
  export const KERNEL_GLOBAL_NAMES = ["variables", "secrets", "resources", "env"] as const;
15
15
 
16
16
  const SYSTEM_KINDS = new Set([
17
- "Kernel.Definition",
18
- "Kernel.Module",
19
- "Kernel.Abstract",
17
+ "Telo.Definition",
18
+ "Telo.Application",
19
+ "Telo.Library",
20
+ "Telo.Abstract",
20
21
  ]);
21
22
 
22
23
  /**
@@ -25,22 +26,30 @@ const SYSTEM_KINDS = new Set([
25
26
  * chain-access validation recognises kernel globals without module authors
26
27
  * having to re-declare them.
27
28
  *
28
- * - `variables` / `secrets`: typed from the `Kernel.Module` declaration
29
+ * - `variables` / `secrets`: typed from the root module doc — prefer
30
+ * Telo.Application when present, otherwise fall back to Telo.Library.
31
+ * Applications are the root whose variables/secrets contract governs CEL
32
+ * in the outer module; Libraries are only relevant when the caller scoped
33
+ * the manifest list to a single library's file.
29
34
  * - `resources`: enumerates all non-system resource names
30
35
  * - `env`: dynamic (runtime env vars, root module only)
31
36
  */
32
37
  export function buildKernelGlobalsSchema(
33
38
  manifests: ResourceManifest[],
34
39
  ): Record<string, any> {
35
- const moduleManifest = manifests.find((m) => m.kind === "Kernel.Module") as
36
- | Record<string, any>
37
- | undefined;
40
+ const moduleManifest =
41
+ (manifests.find((m) => m.kind === "Telo.Application") as
42
+ | Record<string, any>
43
+ | undefined) ??
44
+ (manifests.find((m) => m.kind === "Telo.Library") as
45
+ | Record<string, any>
46
+ | undefined);
38
47
 
39
48
  const resourceProps: Record<string, any> = {};
40
49
  for (const m of manifests) {
41
50
  const name = m.metadata?.name as string | undefined;
42
51
  if (!name || !m.kind) continue;
43
- // Kernel.Import snapshots are stored under resources.<alias> at runtime,
52
+ // Telo.Import snapshots are stored under resources.<alias> at runtime,
44
53
  // so they appear here alongside regular resources.
45
54
  if (!SYSTEM_KINDS.has(m.kind)) {
46
55
  resourceProps[name] = { type: "object", additionalProperties: true };
@@ -62,7 +71,7 @@ export function buildKernelGlobalsSchema(
62
71
  };
63
72
  }
64
73
 
65
- /** Wrap a JSON Schema property map (like `Kernel.Module.variables`) into a
74
+ /** Wrap a JSON Schema property map (like `Telo.Application.variables`) into a
66
75
  * closed object schema suitable for chain-access validation. Falls back to
67
76
  * an open map when the module declares no variables/secrets. */
68
77
  function buildSchemaMapSchema(