@telorun/analyzer 0.65.0 → 0.67.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 (142) hide show
  1. package/dist/analyzer.d.ts +1 -1
  2. package/dist/analyzer.d.ts.map +1 -1
  3. package/dist/analyzer.js +194 -65
  4. package/dist/builtins.d.ts.map +1 -1
  5. package/dist/builtins.js +67 -16
  6. package/dist/call-graph.d.ts.map +1 -1
  7. package/dist/call-graph.js +7 -1
  8. package/dist/cel-environment.d.ts +12 -0
  9. package/dist/cel-environment.d.ts.map +1 -1
  10. package/dist/cel-environment.js +35 -19
  11. package/dist/cel-scope.d.ts +8 -0
  12. package/dist/cel-scope.d.ts.map +1 -1
  13. package/dist/cel-scope.js +89 -11
  14. package/dist/definition-registry.d.ts +41 -0
  15. package/dist/definition-registry.d.ts.map +1 -1
  16. package/dist/definition-registry.js +76 -0
  17. package/dist/dependency-graph.d.ts.map +1 -1
  18. package/dist/dependency-graph.js +65 -0
  19. package/dist/eval-paths.d.ts +68 -0
  20. package/dist/eval-paths.d.ts.map +1 -1
  21. package/dist/eval-paths.js +138 -0
  22. package/dist/flatten-for-analyzer.d.ts +37 -1
  23. package/dist/flatten-for-analyzer.d.ts.map +1 -1
  24. package/dist/flatten-for-analyzer.js +104 -5
  25. package/dist/import-resolution-diagnostics.d.ts +1 -1
  26. package/dist/import-resolution-diagnostics.js +1 -1
  27. package/dist/index.d.ts +12 -8
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +8 -6
  30. package/dist/inline-imports.d.ts.map +1 -1
  31. package/dist/inline-imports.js +1 -0
  32. package/dist/manifest-visitor.d.ts +4 -0
  33. package/dist/manifest-visitor.d.ts.map +1 -1
  34. package/dist/manifest-visitor.js +36 -1
  35. package/dist/migrations/entries/index.d.ts.map +1 -1
  36. package/dist/migrations/entries/index.js +2 -0
  37. package/dist/migrations/entries/schema-prepare-bucket.json +27 -0
  38. package/dist/peer-binding.d.ts +232 -0
  39. package/dist/peer-binding.d.ts.map +1 -0
  40. package/dist/peer-binding.js +418 -0
  41. package/dist/precompile.d.ts.map +1 -1
  42. package/dist/precompile.js +8 -0
  43. package/dist/ref-slot.d.ts +13 -0
  44. package/dist/ref-slot.d.ts.map +1 -1
  45. package/dist/ref-slot.js +26 -0
  46. package/dist/reference-field-map.d.ts +28 -0
  47. package/dist/reference-field-map.d.ts.map +1 -1
  48. package/dist/reference-field-map.js +25 -0
  49. package/dist/referrer-rule.d.ts +17 -0
  50. package/dist/referrer-rule.d.ts.map +1 -1
  51. package/dist/referrer-rule.js +4 -1
  52. package/dist/release/index.d.ts +1 -1
  53. package/dist/release/index.js +1 -1
  54. package/dist/resolve-ref-sentinels.d.ts.map +1 -1
  55. package/dist/resolve-ref-sentinels.js +14 -1
  56. package/dist/resource-input.d.ts +75 -0
  57. package/dist/resource-input.d.ts.map +1 -0
  58. package/dist/resource-input.js +90 -0
  59. package/dist/resource-rule.d.ts +40 -27
  60. package/dist/resource-rule.d.ts.map +1 -1
  61. package/dist/resource-rule.js +61 -7
  62. package/dist/rule-condition.d.ts +11 -0
  63. package/dist/rule-condition.d.ts.map +1 -1
  64. package/dist/rule-condition.js +13 -0
  65. package/dist/schema-projection.d.ts +119 -4
  66. package/dist/schema-projection.d.ts.map +1 -1
  67. package/dist/schema-projection.js +213 -18
  68. package/dist/system-kinds.d.ts +7 -2
  69. package/dist/system-kinds.d.ts.map +1 -1
  70. package/dist/system-kinds.js +7 -2
  71. package/dist/telo-version.d.ts +1 -1
  72. package/dist/telo-version.js +1 -1
  73. package/dist/template-body.d.ts +50 -0
  74. package/dist/template-body.d.ts.map +1 -0
  75. package/dist/template-body.js +58 -0
  76. package/dist/validate-cel-context.d.ts +1 -17
  77. package/dist/validate-cel-context.d.ts.map +1 -1
  78. package/dist/validate-cel-context.js +73 -78
  79. package/dist/validate-identifier-names.d.ts.map +1 -1
  80. package/dist/validate-identifier-names.js +17 -2
  81. package/dist/validate-reference-forms.d.ts +10 -0
  82. package/dist/validate-reference-forms.d.ts.map +1 -1
  83. package/dist/validate-reference-forms.js +23 -0
  84. package/dist/validate-references.d.ts +17 -0
  85. package/dist/validate-references.d.ts.map +1 -1
  86. package/dist/validate-references.js +77 -18
  87. package/dist/validate-referrer-rules.d.ts +38 -4
  88. package/dist/validate-referrer-rules.d.ts.map +1 -1
  89. package/dist/validate-referrer-rules.js +139 -31
  90. package/dist/validate-resource-inputs.d.ts +35 -0
  91. package/dist/validate-resource-inputs.d.ts.map +1 -0
  92. package/dist/validate-resource-inputs.js +319 -0
  93. package/dist/validate-resource-rules.d.ts +11 -2
  94. package/dist/validate-resource-rules.d.ts.map +1 -1
  95. package/dist/validate-resource-rules.js +18 -12
  96. package/dist/validate-schema-projection.d.ts.map +1 -1
  97. package/dist/validate-schema-projection.js +33 -2
  98. package/dist/validate-template-dispatch.d.ts +27 -0
  99. package/dist/validate-template-dispatch.d.ts.map +1 -0
  100. package/dist/validate-template-dispatch.js +95 -0
  101. package/dist/with-synthetic-positions.d.ts +1 -1
  102. package/dist/with-synthetic-positions.js +1 -1
  103. package/package.json +3 -3
  104. package/src/analyzer.ts +236 -70
  105. package/src/builtins.ts +69 -16
  106. package/src/call-graph.ts +7 -1
  107. package/src/cel-environment.ts +48 -19
  108. package/src/cel-scope.ts +113 -19
  109. package/src/definition-registry.ts +78 -0
  110. package/src/dependency-graph.ts +66 -0
  111. package/src/eval-paths.ts +152 -0
  112. package/src/flatten-for-analyzer.ts +117 -4
  113. package/src/import-resolution-diagnostics.ts +1 -1
  114. package/src/index.ts +57 -4
  115. package/src/inline-imports.ts +1 -0
  116. package/src/manifest-visitor.ts +41 -1
  117. package/src/migrations/entries/index.ts +2 -0
  118. package/src/migrations/entries/schema-prepare-bucket.json +27 -0
  119. package/src/peer-binding.ts +641 -0
  120. package/src/precompile.ts +8 -0
  121. package/src/ref-slot.ts +36 -0
  122. package/src/reference-field-map.ts +42 -0
  123. package/src/referrer-rule.ts +20 -1
  124. package/src/release/index.ts +1 -1
  125. package/src/resolve-ref-sentinels.ts +12 -1
  126. package/src/resource-input.ts +132 -0
  127. package/src/resource-rule.ts +69 -7
  128. package/src/rule-condition.ts +15 -0
  129. package/src/schema-projection.ts +302 -20
  130. package/src/system-kinds.ts +7 -2
  131. package/src/telo-version.ts +1 -1
  132. package/src/template-body.ts +104 -0
  133. package/src/validate-cel-context.ts +73 -78
  134. package/src/validate-identifier-names.ts +18 -3
  135. package/src/validate-reference-forms.ts +22 -0
  136. package/src/validate-references.ts +85 -16
  137. package/src/validate-referrer-rules.ts +215 -28
  138. package/src/validate-resource-inputs.ts +367 -0
  139. package/src/validate-resource-rules.ts +26 -12
  140. package/src/validate-schema-projection.ts +50 -1
  141. package/src/validate-template-dispatch.ts +99 -0
  142. package/src/with-synthetic-positions.ts +1 -1
@@ -119,7 +119,13 @@ function resolveUseAtSite(entry, root, concretePath, schemaDefault) {
119
119
  if (resolved)
120
120
  return { use: resolved };
121
121
  }
122
- const slot = { kinds: entry.refs, uses: entry.uses, useCases: entry.useCases, inline: false };
122
+ const slot = {
123
+ kinds: entry.refs,
124
+ uses: entry.uses,
125
+ useCases: entry.useCases,
126
+ inline: false,
127
+ valueBranches: [],
128
+ };
123
129
  const unresolvedReason = isTaggedSentinel(selector)
124
130
  ? "dynamic"
125
131
  : selector === undefined
@@ -13,6 +13,18 @@ export type { CelHandlers } from "@telorun/templating";
13
13
  * NOTE: The set of kernel globals registered here must match `KERNEL_GLOBAL_NAMES`
14
14
  * in kernel-globals.ts, which is used for chain-access validation. */
15
15
  export declare function buildTypedCelEnvironment(baseEnv: Environment, manifest: ResourceManifest, extraContextSchema?: Record<string, any> | null, rootModuleManifest?: ResourceManifest): Environment;
16
+ /**
17
+ * A kind document — whose CEL is written for whoever instantiates the kind, not
18
+ * evaluated in the declaring module's own scope.
19
+ *
20
+ * Its `examples:` show a consumer's route reading `request` and `result`, its
21
+ * `description`s show `${{ secrets.API_KEY }}`, and a rule condition reads the
22
+ * `self` / `referrer` its own evaluator binds. None of those names are in scope
23
+ * where they are WRITTEN, and all of them are correct where they are READ — so
24
+ * every check that asks "is this name in scope here" has to stand down on these
25
+ * documents, or it reports errors nobody can fix without deleting the example.
26
+ */
27
+ export declare function isKindDocument(manifest: ResourceManifest): boolean;
16
28
  /** CEL environment for the `variables:`/`secrets:` expressions on a `Telo.Import`.
17
29
  *
18
30
  * Import inputs are a config-only contract: their expressions are evaluated
@@ -1 +1 @@
1
- {"version":3,"file":"cel-environment.d.ts","sourceRoot":"","sources":["../src/cel-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAcrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;uEASuE;AACvE,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAI/C,kBAAkB,CAAC,EAAE,gBAAgB,GACpC,WAAW,CAgGb;AAuBD;;;;;;8DAM8D;AAC9D,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,WAAW,EACpB,cAAc,EAAE,gBAAgB,GAAG,SAAS,GAC3C,WAAW,CAkCb"}
1
+ {"version":3,"file":"cel-environment.d.ts","sourceRoot":"","sources":["../src/cel-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAcrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;uEASuE;AACvE,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAI/C,kBAAkB,CAAC,EAAE,gBAAgB,GACpC,WAAW,CA8Gb;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAElE;AAuBD;;;;;;8DAM8D;AAC9D,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,WAAW,EACpB,cAAc,EAAE,gBAAgB,GAAG,SAAS,GAC3C,WAAW,CAkCb"}
@@ -35,24 +35,26 @@ rootModuleManifest) {
35
35
  for (const brand of Object.keys(VALUE_BRAND_BASE)) {
36
36
  env.registerType(brand, { fields: {} });
37
37
  }
38
- // Build typed ObjectSchema from manifest.variables if it looks like a schema map
39
- const vars = manifest.variables;
40
- if (vars !== null && typeof vars === "object" && !Array.isArray(vars)) {
41
- const entries = Object.entries(vars).filter(([, v]) => v !== null && typeof v === "object" && !Array.isArray(v));
42
- if (entries.length > 0) {
43
- const schema = {};
44
- for (const [k, v] of entries) {
45
- schema[k] = jsonSchemaToCelType(v);
46
- }
47
- env.registerVariable({ name: "variables", schema });
48
- }
49
- else {
50
- env.registerVariable("variables", "map");
51
- }
52
- }
53
- else {
54
- env.registerVariable("variables", "map");
55
- }
38
+ // `variables` / `secrets`: the DECLARING module's blocks, which is the
39
+ // contract the resource's CEL is evaluated against at runtime. Read the
40
+ // same three ways `ports` and `module` are, and for the same reason — a
41
+ // resource doc does not carry them, so typing from `manifest` alone left
42
+ // every ordinary resource with an open `variables` and no check at all,
43
+ // while `ports.<typo>` one line away was an error.
44
+ //
45
+ // Order matters: a module-identity doc analyzing itself carries its own
46
+ // block; a resource forwarded from an imported library carries its
47
+ // library's as `metadata.moduleGlobals`, which must win over the consuming
48
+ // application's; everything else is the entry module's own doc.
49
+ const moduleGlobals = manifest.metadata?.moduleGlobals;
50
+ // A KIND document is the exception, and it is not a detail: the CEL inside
51
+ // a `Telo.Definition`'s `schema:` — an `examples:` entry, a `description`
52
+ // showing `${{ secrets.API_KEY }}` — illustrates what a CONSUMER writes, in
53
+ // the consumer's scope. Closing those over the declaring module's blocks
54
+ // reported an error against a name the module never meant to declare, and
55
+ // one nobody could fix without deleting the example.
56
+ const root = (isKindDocument(manifest) ? undefined : rootModuleManifest);
57
+ registerConfigNamespace(env, manifest.variables ?? moduleGlobals?.variables ?? root?.variables, "variables");
56
58
  // `ports` namespace: each entry types as the brand its `protocol` selects
57
59
  // (tcp → TcpPort, udp → UdpPort), so `${{ ports.http }}` carries a nominal
58
60
  // type that consuming fields can check against.
@@ -73,7 +75,7 @@ rootModuleManifest) {
73
75
  else {
74
76
  env.registerVariable("ports", "map");
75
77
  }
76
- env.registerVariable("secrets", "map");
78
+ registerConfigNamespace(env, manifest.secrets ?? moduleGlobals?.secrets ?? root?.secrets, "secrets");
77
79
  env.registerVariable("resources", "map");
78
80
  // `module` — the declaring module's own `metadata`, so a manifest reads its
79
81
  // version instead of restating it. A resource forwarded from an imported
@@ -110,6 +112,20 @@ rootModuleManifest) {
110
112
  return baseEnv.clone();
111
113
  }
112
114
  }
115
+ /**
116
+ * A kind document — whose CEL is written for whoever instantiates the kind, not
117
+ * evaluated in the declaring module's own scope.
118
+ *
119
+ * Its `examples:` show a consumer's route reading `request` and `result`, its
120
+ * `description`s show `${{ secrets.API_KEY }}`, and a rule condition reads the
121
+ * `self` / `referrer` its own evaluator binds. None of those names are in scope
122
+ * where they are WRITTEN, and all of them are correct where they are READ — so
123
+ * every check that asks "is this name in scope here" has to stand down on these
124
+ * documents, or it reports errors nobody can fix without deleting the example.
125
+ */
126
+ export function isKindDocument(manifest) {
127
+ return manifest.kind === "Telo.Definition" || manifest.kind === "Telo.Abstract";
128
+ }
113
129
  /** Register a `variables`/`secrets` namespace typed from a module doc's schema map
114
130
  * (`{ name: <schema>, … }`), falling back to dyn `map` when absent or untyped. */
115
131
  function registerConfigNamespace(env, block, name) {
@@ -132,6 +132,14 @@ export declare class CelScopeResolver {
132
132
  private stepContext;
133
133
  private invocationContext;
134
134
  private errorScopes;
135
+ /** The same two facts per `resources:` entry of a `Telo.Definition` — a
136
+ * template body is a declaration of ANOTHER kind, so its step body and its
137
+ * error branches are that kind's, not the enclosing definition's. */
138
+ private bodyScopes;
139
+ /** The enclosing definition's `self` schema, resolved once per resource. A
140
+ * template body's contexts resolve against the BODY, so `self` — the one
141
+ * binding anchored on the definition — is substituted before they do. */
142
+ private selfSchema;
135
143
  constructor(inputs: CelScopeInputs);
136
144
  /** The `steps` context schema for the current resource, or undefined when its
137
145
  * kind declares no step body. Exposed because the step-inputs check needs the
@@ -1 +1 @@
1
- {"version":3,"file":"cel-scope.d.ts","sourceRoot":"","sources":["../src/cel-scope.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cel-scope.d.ts","sourceRoot":"","sources":["../src/cel-scope.ts"],"names":[],"mappings":"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"}
package/dist/cel-scope.js CHANGED
@@ -5,6 +5,7 @@ import { analyzerContractScope, PERMISSIVE_CONTRACT, resolveContract, } from "./
5
5
  import { mergeKernelGlobalsIntoContext, } from "./kernel-globals.js";
6
6
  import { resolveLocalRef, walkStepArray } from "./schema-walk.js";
7
7
  import { readStepSlot } from "./step-slot.js";
8
+ import { bodyForPath, templateBodies } from "./template-body.js";
8
9
  import { getManifestItem, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
9
10
  /** Build a closed JSON Schema for the `self` CEL variable available inside a
10
11
  * `Telo.Definition` template body. Mirrors the runtime template controller's
@@ -268,6 +269,14 @@ export class CelScopeResolver {
268
269
  stepContext;
269
270
  invocationContext;
270
271
  errorScopes = new Map();
272
+ /** The same two facts per `resources:` entry of a `Telo.Definition` — a
273
+ * template body is a declaration of ANOTHER kind, so its step body and its
274
+ * error branches are that kind's, not the enclosing definition's. */
275
+ bodyScopes = [];
276
+ /** The enclosing definition's `self` schema, resolved once per resource. A
277
+ * template body's contexts resolve against the BODY, so `self` — the one
278
+ * binding anchored on the definition — is substituted before they do. */
279
+ selfSchema;
271
280
  constructor(inputs) {
272
281
  this.inputs = inputs;
273
282
  }
@@ -293,10 +302,30 @@ export class CelScopeResolver {
293
302
  enterResource(m, definition) {
294
303
  const { allManifests, defs, aliases, scopes } = this.inputs;
295
304
  this.invocationContext = m.metadata?.xTeloInvocationContext;
296
- this.stepContext = definition?.schema
297
- ? buildStepContextSchema(m, definition.schema, allManifests, defs, aliases, scopes)
305
+ // The INHERITANCE-RESOLVED schema at both sites, for the reason the CEL
306
+ // context regions use it: an `extends` child is authored against
307
+ // merge(parent, own), so a step body or a `catch:` region the parent
308
+ // declares is in force on the child. All three consumers share one memo so
309
+ // they cannot come to disagree about which schema a kind means.
310
+ const authorSchema = defs.effectiveSchemaOf(definition);
311
+ this.stepContext = authorSchema
312
+ ? buildStepContextSchema(m, authorSchema, allManifests, defs, aliases, scopes)
298
313
  : undefined;
299
- this.errorScopes = collectErrorContextScopes(definition?.schema);
314
+ this.errorScopes = collectErrorContextScopes(authorSchema);
315
+ this.selfSchema =
316
+ m.kind === "Telo.Definition" ? buildSelfSchema(m, defs, aliases) : undefined;
317
+ this.bodyScopes = templateBodies(m, defs, aliases, scopes).map((body) => {
318
+ const bodySchema = defs.effectiveSchemaOf(body.definition);
319
+ return {
320
+ prefix: body.prefix,
321
+ scopePrefix: body.scopePrefix,
322
+ manifest: body.manifest,
323
+ stepContext: bodySchema
324
+ ? buildStepContextSchema(body.manifest, bodySchema, allManifests, defs, aliases, scopes)
325
+ : undefined,
326
+ errorScopes: collectErrorContextScopes(bodySchema),
327
+ };
328
+ });
300
329
  }
301
330
  /**
302
331
  * What the expression at `site` is typed against.
@@ -333,16 +362,36 @@ export class CelScopeResolver {
333
362
  const { path } = site;
334
363
  const m = site.source;
335
364
  let matched = site.contextSchema ?? this.invocationContext;
336
- if (this.stepContext) {
365
+ // Inside a template body the step and error regions are the NESTED kind's.
366
+ // Its `steps` accumulator and its `catch:` branches are declared there, and
367
+ // the enclosing definition's (there are none) would say nothing about them.
368
+ const inBody = this.bodyScopes.length > 0 ? bodyForPath(this.bodyScopes, path) : undefined;
369
+ const stepContext = inBody ? inBody.stepContext : this.stepContext;
370
+ const errorScopes = inBody ? inBody.errorScopes : this.errorScopes;
371
+ if (stepContext) {
337
372
  const base = matched ?? { type: "object", properties: {}, additionalProperties: true };
338
373
  matched = {
339
374
  ...base,
340
- properties: { ...(base.properties ?? {}), steps: this.stepContext },
375
+ properties: {
376
+ // `inputs` is in scope beside `steps` wherever a step body runs — the
377
+ // step engine evaluates a step against the enclosing kind's own
378
+ // arguments, which is what `steps[0].inputs.x` reads. Modelled here
379
+ // because it was modelled NOWHERE: the step context is open, so a
380
+ // chain through it was never checked and the name's absence went
381
+ // unnoticed until something asked whether the root existed.
382
+ //
383
+ // OPEN, not typed from the kind's `inputType`: closing it would newly
384
+ // reject every read of an argument the contract does not spell out,
385
+ // which is a separate decision from knowing the name is legal.
386
+ inputs: { type: "object", additionalProperties: true },
387
+ ...(base.properties ?? {}),
388
+ steps: stepContext,
389
+ },
341
390
  };
342
391
  }
343
392
  // `error` is only in scope inside an error-bearing branch (e.g. a
344
393
  // `catch:` / `finally:`), so it's merged per-path, not resource-wide.
345
- const errorSchema = this.errorScopes.size > 0 ? errorContextForPath(path, this.errorScopes) : undefined;
394
+ const errorSchema = errorScopes.size > 0 ? errorContextForPath(path, errorScopes) : undefined;
346
395
  if (errorSchema) {
347
396
  const base = matched ?? { type: "object", properties: {}, additionalProperties: true };
348
397
  matched = {
@@ -358,17 +407,46 @@ export class CelScopeResolver {
358
407
  return this.inputs.observedStateContext;
359
408
  }
360
409
  const { defs, aliases, scopes, allManifests, kernelGlobals } = this.inputs;
361
- const manifestItem = site.matchedScope
362
- ? getManifestItem(path, site.matchedScope, m)
363
- : m;
364
- const rootForResolver = manifestRootForResolver(m, defs, aliases, allManifests, scopes);
410
+ // A template body's context annotations are the NESTED kind's, and every one
411
+ // that anchors at a root — `x-telo-context-element-from`,
412
+ // `-collection-from`, `-from-root`, `x-telo-bindings-from` — means the root
413
+ // of the DECLARATION they were written for. Resolving them against the
414
+ // enclosing `Telo.Definition` looked `collection:` up on a document that has
415
+ // no such field, so `item` typed open and a typo below it went unreported —
416
+ // the one place a nested declaration did not answer as the same declaration
417
+ // written at the top level.
418
+ //
419
+ // So the path and the scope are rebased into the body and the body becomes
420
+ // the resolution root. `self` is the single binding that genuinely belongs to
421
+ // the enclosing definition, and it is substituted below rather than left as
422
+ // an annotation the rebased root would misread.
423
+ const body = inBody;
424
+ const localPath = body ? path.slice(body.prefix.length + 1) : path;
425
+ const localScope = body && site.matchedScope?.startsWith(`${body.scopePrefix}.`)
426
+ ? `$.${site.matchedScope.slice(body.scopePrefix.length + 1)}`
427
+ : body
428
+ ? undefined
429
+ : site.matchedScope;
430
+ const rootManifest = body ? body.manifest : m;
431
+ if (body && this.selfSchema && matched.properties?.self) {
432
+ matched = {
433
+ ...matched,
434
+ properties: { ...matched.properties, self: this.selfSchema },
435
+ };
436
+ }
437
+ const manifestItem = localScope
438
+ ? getManifestItem(localPath, localScope, rootManifest)
439
+ : rootManifest;
440
+ const rootForResolver = body
441
+ ? rootManifest
442
+ : manifestRootForResolver(m, defs, aliases, allManifests, scopes);
365
443
  const resolved = resolveContextAnnotations(matched, manifestItem, {
366
444
  manifestRoot: rootForResolver,
367
445
  defs,
368
446
  aliases,
369
447
  allManifests: allManifests,
370
448
  });
371
- return mergeKernelGlobalsIntoContext(withBindingNames(resolved, m),
449
+ return mergeKernelGlobalsIntoContext(withBindingNames(resolved, rootManifest),
372
450
  // Typed in the module that DECLARED this resource — for a manifest
373
451
  // forwarded from an imported library, that is its `moduleGlobals` stamp,
374
452
  // not the consuming application's block.
@@ -18,6 +18,9 @@ export declare class DefinitionRegistry {
18
18
  private readonly fieldMaps;
19
19
  /** Reverse inheritance index: parent kind → direct child kinds. */
20
20
  private readonly extendedBy;
21
+ /** Memoized inheritance-resolved schemas. `null` records "resolved to
22
+ * nothing", so a kind with no schema is not re-walked on every resource. */
23
+ private readonly effectiveSchemas;
21
24
  /** DEPRECATED module identity table: identity string → canonical module name
22
25
  * ("std/pipeline" → "pipeline"). Serves only the legacy
23
26
  * `<namespace>/<module>#<Kind>` form of `x-telo-ref`, kept resolvable for
@@ -131,6 +134,27 @@ export declare class DefinitionRegistry {
131
134
  * never registered. */
132
135
  resolveRef(xTeloRef: string): string | undefined;
133
136
  resolve(kind: string): ResourceDefinition | undefined;
137
+ /**
138
+ * The kind's AUTHOR-FACING schema — its own merged with everything it
139
+ * inherits along `extends` — memoized per kind.
140
+ *
141
+ * This is what every consumer reading a kind's schema to interpret an
142
+ * INSTANCE must use, because that is the schema the instance was authored
143
+ * against and the one the kernel stamps at definition registration. Reading
144
+ * `resolve(kind).schema` instead sees only what the leaf declared, so an
145
+ * annotation on a parent — a CEL context region, a step body, an error
146
+ * context — is invisible on every child, silently and per consumer. Sharing
147
+ * one memo is what keeps those consumers from drifting apart.
148
+ *
149
+ * Lazy for the same reason `getFieldMap` is: a child registered before its
150
+ * parent still sees the inherited half once both are present.
151
+ */
152
+ effectiveSchema(kind: string): Record<string, unknown> | undefined;
153
+ /** The effective schema for a definition, by the kind it is registered under.
154
+ * Falls back to the definition's own schema when it is not in this registry
155
+ * (a synthesized or not-yet-registered kind), which is the pre-inheritance
156
+ * behaviour and the safe direction: it types less, never wrongly. */
157
+ effectiveSchemaOf(definition: ResourceDefinition | undefined): Record<string, unknown> | undefined;
134
158
  /** Returns the reference field map for the given kind, computed lazily from the
135
159
  * kind's AUTHOR-FACING (inheritance-resolved) schema and memoized. Lazy so a
136
160
  * child registered before its parent still sees the parent's inherited ref
@@ -166,6 +190,23 @@ export declare class DefinitionRegistry {
166
190
  */
167
191
  resolveSchemaFromNode(schemaFrom: string, ownerScope: AliasResolver): Record<string, any> | undefined;
168
192
  private resolveSchemaFromSubMap;
193
+ /** The kinds a definition descends from DIRECTLY — the same two edges
194
+ * `register` feeds into `extendedBy`, read the other way.
195
+ *
196
+ * Both spellings, because either can carry the edge that satisfies a slot:
197
+ * `capability:` is the legacy implements-this form, `extends:` the canonical
198
+ * one. A consumer walking these by hand would be a second reading of what an
199
+ * inheritance edge IS, and the downward index and the upward walk have to
200
+ * agree about that forever. */
201
+ parentsOf(kind: string): string[];
202
+ /** Whether every kind this one descends from is registered.
203
+ *
204
+ * When it is, what the kind implements is FULLY KNOWN: a target it does not
205
+ * reach is one it genuinely does not implement, so a mismatch is a verdict.
206
+ * When a hop is missing — an unimported abstract, an alias the declaring file
207
+ * could not resolve — a mismatch cannot be told from a missing dependency,
208
+ * which is the partial context every reference check stays lenient in. */
209
+ ancestryResolved(kind: string): boolean;
169
210
  /** Returns all definitions that transitively extend the given abstract kind.
170
211
  * Follows the capability chain to any depth (equivalent to instanceof in OOP).
171
212
  * Definitions are included regardless of registration order. */
@@ -1 +1 @@
1
- {"version":3,"file":"definition-registry.d.ts","sourceRoot":"","sources":["../src/definition-registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"definition-registry.d.ts","sourceRoot":"","sources":["../src/definition-registry.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,9 @@ export class DefinitionRegistry {
23
23
  fieldMaps = new Map();
24
24
  /** Reverse inheritance index: parent kind → direct child kinds. */
25
25
  extendedBy = new Map();
26
+ /** Memoized inheritance-resolved schemas. `null` records "resolved to
27
+ * nothing", so a kind with no schema is not re-walked on every resource. */
28
+ effectiveSchemas = new Map();
26
29
  /** DEPRECATED module identity table: identity string → canonical module name
27
30
  * ("std/pipeline" → "pipeline"). Serves only the legacy
28
31
  * `<namespace>/<module>#<Kind>` form of `x-telo-ref`, kept resolvable for
@@ -38,6 +41,7 @@ export class DefinitionRegistry {
38
41
  // so any already-computed map recomputes against the now-larger def set; the
39
42
  // maps rebuild lazily on first `getFieldMap` (after all defs are registered).
40
43
  this.fieldMaps.clear();
44
+ this.effectiveSchemas.clear();
41
45
  // `capability` populates extendedBy for backward-compat with the legacy pattern where
42
46
  // a concrete definition overloaded `capability: <AbstractKind>` to mean "implements
43
47
  // this abstract." The canonical pattern is `extends` (below). Both populate the index,
@@ -263,6 +267,43 @@ export class DefinitionRegistry {
263
267
  resolve(kind) {
264
268
  return this.defs.get(kind);
265
269
  }
270
+ /**
271
+ * The kind's AUTHOR-FACING schema — its own merged with everything it
272
+ * inherits along `extends` — memoized per kind.
273
+ *
274
+ * This is what every consumer reading a kind's schema to interpret an
275
+ * INSTANCE must use, because that is the schema the instance was authored
276
+ * against and the one the kernel stamps at definition registration. Reading
277
+ * `resolve(kind).schema` instead sees only what the leaf declared, so an
278
+ * annotation on a parent — a CEL context region, a step body, an error
279
+ * context — is invisible on every child, silently and per consumer. Sharing
280
+ * one memo is what keeps those consumers from drifting apart.
281
+ *
282
+ * Lazy for the same reason `getFieldMap` is: a child registered before its
283
+ * parent still sees the inherited half once both are present.
284
+ */
285
+ effectiveSchema(kind) {
286
+ const cached = this.effectiveSchemas.get(kind);
287
+ if (cached !== undefined)
288
+ return cached ?? undefined;
289
+ const def = this.defs.get(kind);
290
+ if (!def)
291
+ return undefined;
292
+ const schema = effectiveAuthorSchema(def, (k) => this.resolve(k));
293
+ this.effectiveSchemas.set(kind, schema ?? null);
294
+ return schema;
295
+ }
296
+ /** The effective schema for a definition, by the kind it is registered under.
297
+ * Falls back to the definition's own schema when it is not in this registry
298
+ * (a synthesized or not-yet-registered kind), which is the pre-inheritance
299
+ * behaviour and the safe direction: it types less, never wrongly. */
300
+ effectiveSchemaOf(definition) {
301
+ if (!definition)
302
+ return undefined;
303
+ const { name, module: mod } = definition.metadata;
304
+ const resolved = this.effectiveSchema(mod ? `${mod}.${name}` : name);
305
+ return resolved ?? definition.schema;
306
+ }
266
307
  /** Returns the reference field map for the given kind, computed lazily from the
267
308
  * kind's AUTHOR-FACING (inheritance-resolved) schema and memoized. Lazy so a
268
309
  * child registered before its parent still sees the parent's inherited ref
@@ -368,6 +409,41 @@ export class DefinitionRegistry {
368
409
  return null;
369
410
  return buildFieldMapAtPath(subSchema, fieldPath);
370
411
  }
412
+ /** The kinds a definition descends from DIRECTLY — the same two edges
413
+ * `register` feeds into `extendedBy`, read the other way.
414
+ *
415
+ * Both spellings, because either can carry the edge that satisfies a slot:
416
+ * `capability:` is the legacy implements-this form, `extends:` the canonical
417
+ * one. A consumer walking these by hand would be a second reading of what an
418
+ * inheritance edge IS, and the downward index and the upward walk have to
419
+ * agree about that forever. */
420
+ parentsOf(kind) {
421
+ const def = this.defs.get(kind);
422
+ if (!def)
423
+ return [];
424
+ return [def.capability, def.extends].filter((parent) => typeof parent === "string" && parent.length > 0);
425
+ }
426
+ /** Whether every kind this one descends from is registered.
427
+ *
428
+ * When it is, what the kind implements is FULLY KNOWN: a target it does not
429
+ * reach is one it genuinely does not implement, so a mismatch is a verdict.
430
+ * When a hop is missing — an unimported abstract, an alias the declaring file
431
+ * could not resolve — a mismatch cannot be told from a missing dependency,
432
+ * which is the partial context every reference check stays lenient in. */
433
+ ancestryResolved(kind) {
434
+ const queue = [kind];
435
+ const seen = new Set();
436
+ while (queue.length > 0) {
437
+ const current = queue.shift();
438
+ if (seen.has(current))
439
+ continue;
440
+ seen.add(current);
441
+ if (!this.defs.has(current))
442
+ return false;
443
+ queue.push(...this.parentsOf(current));
444
+ }
445
+ return true;
446
+ }
371
447
  /** Returns all definitions that transitively extend the given abstract kind.
372
448
  * Follows the capability chain to any depth (equivalent to instanceof in OOP).
373
449
  * Definitions are included regardless of registration order. */
@@ -1 +1 @@
1
- {"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../src/dependency-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B;kDAC8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACpC;oFACgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACrC;AAID;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,eAAe,CAyDjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,MAAM,CAOtE"}
1
+ {"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../src/dependency-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGnE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B;kDAC8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACpC;oFACgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACrC;AAID;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,eAAe,CA2DjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,MAAM,CAOtE"}
@@ -1,4 +1,5 @@
1
1
  import { buildCallGraph, projectToPairs } from "./call-graph.js";
2
+ import { readSuppliedResources } from "./resource-input.js";
2
3
  const nodeKey = (kind, name) => `${kind}\0${name}`;
3
4
  /**
4
5
  * Builds a directed acyclic graph (DAG) of boot-time resource dependencies and
@@ -45,6 +46,7 @@ export function buildDependencyGraph(resources, registry, aliases, aliasesByModu
45
46
  for (const key of nodes.keys())
46
47
  if (!deps.has(key))
47
48
  deps.set(key, new Set());
49
+ addResourceInputEdges(resources, nodes, deps);
48
50
  // --- Kahn's topological sort ---
49
51
  // in-degree[X] = number of X's dependencies (size of deps[X])
50
52
  // reverse[dep] = set of nodes that depend on dep (for degree decrement)
@@ -130,3 +132,66 @@ function findCycle(nodes, deps) {
130
132
  }
131
133
  return [];
132
134
  }
135
+ /**
136
+ * Boot-order edges for the one thing a `Telo.Import` does hold: the instances it
137
+ * hands DOWN to its target library's declared `resources:` inputs.
138
+ *
139
+ * An import is otherwise module wiring rather than a runtime node — it is in
140
+ * `DEPENDENCY_GRAPH_SKIP_KINDS` and the call graph gives it no node at all — but
141
+ * a borrowed instance must exist before the import initializes, and a cycle
142
+ * through one is a cycle like any other. The edges are added here rather than
143
+ * read off the reference field map because the accepted KIND at this slot is
144
+ * declared by the TARGET library, not by the `Telo.Import` schema, so there is
145
+ * no `x-telo-ref` for the map to read; the constraint itself is checked by
146
+ * `validate-resource-inputs`.
147
+ */
148
+ function addResourceInputEdges(resources, nodes, deps) {
149
+ // Every import that supplies inputs becomes a node FIRST, so a cross-module
150
+ // reference below can resolve to the import that exports its target.
151
+ const imports = [];
152
+ for (const m of resources) {
153
+ if (m.kind !== "Telo.Import")
154
+ continue;
155
+ const alias = m.metadata?.name;
156
+ const supplied = readSuppliedResources(m);
157
+ if (!alias || Object.keys(supplied).length === 0)
158
+ continue;
159
+ const key = nodeKey(m.kind, alias);
160
+ nodes.set(key, { kind: m.kind, name: alias });
161
+ if (!deps.has(key))
162
+ deps.set(key, new Set());
163
+ imports.push({ key, supplied });
164
+ }
165
+ if (imports.length === 0)
166
+ return;
167
+ const byName = new Map();
168
+ for (const [key, node] of nodes)
169
+ byName.set(node.name, key);
170
+ // An import is keyed by its alias, and that is also how a cross-module
171
+ // reference names it — index those so one resolves.
172
+ for (const m of resources) {
173
+ if (m.kind !== "Telo.Import")
174
+ continue;
175
+ const alias = m.metadata?.name;
176
+ const key = alias ? nodeKey(m.kind, alias) : undefined;
177
+ if (alias && key && nodes.has(key))
178
+ byName.set(alias, key);
179
+ }
180
+ for (const { key, supplied } of imports) {
181
+ const set = deps.get(key);
182
+ for (const value of Object.values(supplied)) {
183
+ const ref = value;
184
+ // A CROSS-MODULE reference (`!ref Other.db`) names an instance exported by
185
+ // another import, never a local resource. Looking it up in the local name
186
+ // map would find an unrelated resource of the same name — a wrong edge,
187
+ // and possibly a phantom cycle — or nothing at all. What it depends on is
188
+ // the IMPORT that exports it, which is the projection
189
+ // `localDependencyNames` already makes at runtime.
190
+ const alias = typeof ref?.alias === "string" ? ref.alias : undefined;
191
+ const targetName = alias && alias !== "Self" ? alias : typeof ref?.name === "string" ? ref.name : undefined;
192
+ const target = targetName ? byName.get(targetName) : undefined;
193
+ if (target && target !== key)
194
+ set.add(target);
195
+ }
196
+ }
197
+ }