@telorun/analyzer 0.72.0 → 0.74.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 (208) hide show
  1. package/dist/analyzer.d.ts.map +1 -1
  2. package/dist/analyzer.js +76 -20
  3. package/dist/artifact-axes.d.ts +14 -0
  4. package/dist/artifact-axes.d.ts.map +1 -0
  5. package/dist/artifact-axes.js +12 -0
  6. package/dist/artifact-layer-index.d.ts +2 -2
  7. package/dist/artifact-layer-index.d.ts.map +1 -1
  8. package/dist/artifact-layer-index.js +9 -4
  9. package/dist/artifact-selector.d.ts +47 -21
  10. package/dist/artifact-selector.d.ts.map +1 -1
  11. package/dist/artifact-selector.js +85 -16
  12. package/dist/builtins.d.ts.map +1 -1
  13. package/dist/builtins.js +144 -113
  14. package/dist/call-graph.d.ts +12 -1
  15. package/dist/call-graph.d.ts.map +1 -1
  16. package/dist/call-graph.js +22 -0
  17. package/dist/catch-scope.js +1 -1
  18. package/dist/cel-scope-query.d.ts.map +1 -1
  19. package/dist/cel-scope-query.js +32 -11
  20. package/dist/cel-scope.d.ts.map +1 -1
  21. package/dist/cel-scope.js +1 -0
  22. package/dist/eval-paths.d.ts +36 -5
  23. package/dist/eval-paths.d.ts.map +1 -1
  24. package/dist/eval-paths.js +47 -6
  25. package/dist/extends-resolution.d.ts +12 -0
  26. package/dist/extends-resolution.d.ts.map +1 -1
  27. package/dist/extends-resolution.js +23 -0
  28. package/dist/forwarded-declaration.d.ts +26 -0
  29. package/dist/forwarded-declaration.d.ts.map +1 -0
  30. package/dist/forwarded-declaration.js +31 -0
  31. package/dist/index.d.ts +10 -4
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +5 -2
  34. package/dist/kernel-globals.d.ts.map +1 -1
  35. package/dist/kernel-globals.js +21 -5
  36. package/dist/module-alias-scope.d.ts +11 -0
  37. package/dist/module-alias-scope.d.ts.map +1 -1
  38. package/dist/module-alias-scope.js +10 -0
  39. package/dist/module-file-claims.d.ts +17 -1
  40. package/dist/module-file-claims.d.ts.map +1 -1
  41. package/dist/module-file-claims.js +38 -3
  42. package/dist/module-named-files.d.ts +72 -0
  43. package/dist/module-named-files.d.ts.map +1 -0
  44. package/dist/module-named-files.js +115 -0
  45. package/dist/native-entries.d.ts +83 -0
  46. package/dist/native-entries.d.ts.map +1 -0
  47. package/dist/native-entries.js +169 -0
  48. package/dist/nearest-name.d.ts +22 -0
  49. package/dist/nearest-name.d.ts.map +1 -0
  50. package/dist/nearest-name.js +43 -0
  51. package/dist/normalize-inline-resources.d.ts +41 -13
  52. package/dist/normalize-inline-resources.d.ts.map +1 -1
  53. package/dist/normalize-inline-resources.js +236 -54
  54. package/dist/reference-field-map.d.ts +59 -1
  55. package/dist/reference-field-map.d.ts.map +1 -1
  56. package/dist/reference-field-map.js +200 -34
  57. package/dist/release/destinations.d.ts +59 -0
  58. package/dist/release/destinations.d.ts.map +1 -0
  59. package/dist/release/destinations.js +75 -0
  60. package/dist/release/index.d.ts +6 -2
  61. package/dist/release/index.d.ts.map +1 -1
  62. package/dist/release/index.js +3 -1
  63. package/dist/release/ledger.d.ts +20 -11
  64. package/dist/release/ledger.d.ts.map +1 -1
  65. package/dist/release/ledger.js +43 -14
  66. package/dist/release/payload-digest.d.ts +2 -1
  67. package/dist/release/payload-digest.d.ts.map +1 -1
  68. package/dist/release/payload-digest.js +2 -1
  69. package/dist/release/release-plan.d.ts +3 -2
  70. package/dist/release/release-plan.d.ts.map +1 -1
  71. package/dist/release/release-plan.js +12 -19
  72. package/dist/release/workspace-config.d.ts +114 -34
  73. package/dist/release/workspace-config.d.ts.map +1 -1
  74. package/dist/release/workspace-config.js +322 -38
  75. package/dist/release/workspace-schema.d.ts +41 -0
  76. package/dist/release/workspace-schema.d.ts.map +1 -0
  77. package/dist/release/workspace-schema.js +77 -0
  78. package/dist/resolve-ref-sentinels.d.ts.map +1 -1
  79. package/dist/resolve-ref-sentinels.js +8 -5
  80. package/dist/resolve-throws-union.d.ts +25 -4
  81. package/dist/resolve-throws-union.d.ts.map +1 -1
  82. package/dist/resolve-throws-union.js +62 -32
  83. package/dist/resolve-zone-requirements.d.ts.map +1 -1
  84. package/dist/resolve-zone-requirements.js +2 -1
  85. package/dist/rewrite-synthetic-origins.d.ts +6 -1
  86. package/dist/rewrite-synthetic-origins.d.ts.map +1 -1
  87. package/dist/rewrite-synthetic-origins.js +107 -13
  88. package/dist/schema-compat.d.ts.map +1 -1
  89. package/dist/schema-compat.js +18 -5
  90. package/dist/schema-error-report.d.ts +7 -0
  91. package/dist/schema-error-report.d.ts.map +1 -1
  92. package/dist/schema-error-report.js +9 -4
  93. package/dist/schema-walk.d.ts +47 -13
  94. package/dist/schema-walk.d.ts.map +1 -1
  95. package/dist/schema-walk.js +150 -30
  96. package/dist/scope-declarations.d.ts +59 -0
  97. package/dist/scope-declarations.d.ts.map +1 -0
  98. package/dist/scope-declarations.js +45 -0
  99. package/dist/source-entries.d.ts +119 -0
  100. package/dist/source-entries.d.ts.map +1 -0
  101. package/dist/source-entries.js +349 -0
  102. package/dist/step-bodies.d.ts +24 -0
  103. package/dist/step-bodies.d.ts.map +1 -0
  104. package/dist/step-bodies.js +28 -0
  105. package/dist/telo-version.d.ts +1 -1
  106. package/dist/telo-version.js +1 -1
  107. package/dist/template-body.d.ts +10 -0
  108. package/dist/template-body.d.ts.map +1 -1
  109. package/dist/template-body.js +25 -0
  110. package/dist/validate-cel-context.d.ts +23 -8
  111. package/dist/validate-cel-context.d.ts.map +1 -1
  112. package/dist/validate-cel-context.js +51 -18
  113. package/dist/validate-exports.d.ts +28 -0
  114. package/dist/validate-exports.d.ts.map +1 -0
  115. package/dist/validate-exports.js +146 -0
  116. package/dist/validate-extends.d.ts.map +1 -1
  117. package/dist/validate-extends.js +37 -1
  118. package/dist/validate-module-artifact.d.ts +4 -2
  119. package/dist/validate-module-artifact.d.ts.map +1 -1
  120. package/dist/validate-module-artifact.js +43 -4
  121. package/dist/validate-native-entries.d.ts +15 -0
  122. package/dist/validate-native-entries.d.ts.map +1 -0
  123. package/dist/validate-native-entries.js +103 -0
  124. package/dist/validate-observed-state.d.ts +7 -1
  125. package/dist/validate-observed-state.d.ts.map +1 -1
  126. package/dist/validate-observed-state.js +24 -6
  127. package/dist/validate-provider-coherence.d.ts +4 -8
  128. package/dist/validate-provider-coherence.d.ts.map +1 -1
  129. package/dist/validate-provider-coherence.js +12 -123
  130. package/dist/validate-reference-forms.d.ts.map +1 -1
  131. package/dist/validate-reference-forms.js +2 -2
  132. package/dist/validate-references.d.ts.map +1 -1
  133. package/dist/validate-references.js +152 -65
  134. package/dist/validate-scope-reach.d.ts +31 -0
  135. package/dist/validate-scope-reach.d.ts.map +1 -0
  136. package/dist/validate-scope-reach.js +180 -0
  137. package/dist/validate-source-entries.d.ts +16 -0
  138. package/dist/validate-source-entries.d.ts.map +1 -0
  139. package/dist/validate-source-entries.js +90 -0
  140. package/dist/validate-template-body.d.ts +36 -0
  141. package/dist/validate-template-body.d.ts.map +1 -0
  142. package/dist/validate-template-body.js +246 -0
  143. package/dist/validate-throws-coverage.d.ts.map +1 -1
  144. package/dist/validate-throws-coverage.js +21 -39
  145. package/dist/value-type-keyword.d.ts +22 -5
  146. package/dist/value-type-keyword.d.ts.map +1 -1
  147. package/dist/value-type-keyword.js +16 -7
  148. package/package.json +4 -3
  149. package/src/analyzer.ts +97 -20
  150. package/src/artifact-axes.ts +24 -0
  151. package/src/artifact-layer-index.ts +9 -6
  152. package/src/artifact-selector.ts +110 -35
  153. package/src/builtins.ts +160 -113
  154. package/src/call-graph.ts +30 -0
  155. package/src/catch-scope.ts +1 -1
  156. package/src/cel-scope-query.ts +28 -9
  157. package/src/cel-scope.ts +1 -0
  158. package/src/eval-paths.ts +53 -6
  159. package/src/extends-resolution.ts +24 -0
  160. package/src/forwarded-declaration.ts +32 -0
  161. package/src/index.ts +37 -1
  162. package/src/kernel-globals.ts +28 -5
  163. package/src/migrations/CLAUDE.md +35 -0
  164. package/src/module-alias-scope.ts +17 -0
  165. package/src/module-file-claims.ts +63 -4
  166. package/src/module-named-files.ts +162 -0
  167. package/src/native-entries.ts +227 -0
  168. package/src/nearest-name.ts +47 -0
  169. package/src/normalize-inline-resources.ts +326 -70
  170. package/src/reference-field-map.ts +257 -52
  171. package/src/release/CLAUDE.md +32 -0
  172. package/src/release/destinations.ts +105 -0
  173. package/src/release/index.ts +33 -2
  174. package/src/release/ledger.ts +51 -21
  175. package/src/release/payload-digest.ts +2 -1
  176. package/src/release/release-plan.ts +16 -26
  177. package/src/release/workspace-config.ts +483 -47
  178. package/src/release/workspace-schema.ts +99 -0
  179. package/src/resolve-ref-sentinels.ts +7 -6
  180. package/src/resolve-throws-union.ts +87 -32
  181. package/src/resolve-zone-requirements.ts +2 -1
  182. package/src/rewrite-synthetic-origins.ts +107 -16
  183. package/src/schema-compat.ts +18 -4
  184. package/src/schema-error-report.ts +16 -4
  185. package/src/schema-walk.ts +162 -34
  186. package/src/scope-declarations.ts +98 -0
  187. package/src/source-entries.ts +502 -0
  188. package/src/step-bodies.ts +55 -0
  189. package/src/telo-version.ts +1 -1
  190. package/src/template-body.ts +25 -0
  191. package/src/validate-cel-context.ts +60 -18
  192. package/src/validate-exports.ts +185 -0
  193. package/src/validate-extends.ts +43 -0
  194. package/src/validate-module-artifact.ts +46 -3
  195. package/src/validate-native-entries.ts +147 -0
  196. package/src/validate-observed-state.ts +36 -7
  197. package/src/validate-provider-coherence.ts +17 -127
  198. package/src/validate-reference-forms.ts +2 -3
  199. package/src/validate-references.ts +151 -66
  200. package/src/validate-scope-reach.ts +213 -0
  201. package/src/validate-source-entries.ts +120 -0
  202. package/src/validate-template-body.ts +274 -0
  203. package/src/validate-throws-coverage.ts +24 -31
  204. package/src/value-type-keyword.ts +35 -9
  205. package/dist/validate-template-dispatch.d.ts +0 -27
  206. package/dist/validate-template-dispatch.d.ts.map +0 -1
  207. package/dist/validate-template-dispatch.js +0 -95
  208. package/src/validate-template-dispatch.ts +0 -99
@@ -1,5 +1,6 @@
1
1
  import { OBSERVED_STATE_KEY } from "@telorun/sdk";
2
2
  import { effectiveStatusSchema } from "./extends-resolution.js";
3
+ import { isForwardedDeclaration } from "./forwarded-declaration.js";
3
4
  import { parseExportEntry } from "./flatten-for-analyzer.js";
4
5
  import { moduleScopedDefResolver, } from "./alias-resolver.js";
5
6
  import { buildReferenceFieldMap, isScopeEntry, resolveFieldValues, } from "./reference-field-map.js";
@@ -100,6 +101,10 @@ export function collectRunReachableNames(graph) {
100
101
  names.add(edge.toName);
101
102
  return names;
102
103
  }
104
+ /** The index key of a name a dependency declares, as read from inside it. */
105
+ export function forwardedResourceKey(module, name) {
106
+ return `${module}\0${name}`;
107
+ }
103
108
  /**
104
109
  * Index every resource a CEL `resources.…` read can name: the module's own
105
110
  * top-level resources, the ones declared inside `x-telo-scope` slots (a
@@ -115,17 +120,30 @@ export function buildObservedStateIndex(manifests, defs, aliases, scopes) {
115
120
  /** `module` is the resource's DECLARING module: an exported instance is
116
121
  * written with that library's aliases (`kind: Self.Listener`), which the
117
122
  * consumer's table cannot resolve. */
118
- const record = (kind, key, scoped, module) => {
123
+ const record = (kind, key, scoped, module, forwardedFrom) => {
119
124
  const status = effectiveStatusSchema(resolve.in(kind, module), resolve);
120
- out.set(key, { kind, ...(status ? { status } : {}), ...(scoped ? { scoped } : {}) });
125
+ out.set(key, {
126
+ kind,
127
+ ...(status ? { status } : {}),
128
+ ...(scoped ? { scoped } : {}),
129
+ ...(forwardedFrom !== undefined ? { forwardedFrom } : {}),
130
+ });
121
131
  };
122
132
  for (const manifest of manifests) {
123
133
  const kind = manifest.kind;
124
134
  const name = manifest.metadata?.name;
125
135
  if (!kind || SYSTEM_KINDS.has(kind))
126
136
  continue;
137
+ const module = manifest.metadata?.module;
138
+ // A dependency's declarations publish into ITS module's `resources`, not this
139
+ // one's: they are keyed by that module, and an export is also indexed under
140
+ // `<Alias>.<name>` below.
141
+ const forwardedFrom = isForwardedDeclaration(manifest) ? module : undefined;
142
+ if (forwardedFrom === undefined && isForwardedDeclaration(manifest))
143
+ continue;
144
+ const keyOf = (n) => forwardedFrom === undefined ? n : forwardedResourceKey(forwardedFrom, n);
127
145
  if (name)
128
- record(kind, name, false, manifest.metadata?.module);
146
+ record(kind, keyOf(name), false, module, forwardedFrom);
129
147
  const schema = resolve(kind)?.schema;
130
148
  if (!schema)
131
149
  continue;
@@ -137,7 +155,7 @@ export function buildObservedStateIndex(manifests, defs, aliases, scopes) {
137
155
  const scopedKind = scopedEntry?.kind;
138
156
  const scopedName = scopedEntry?.metadata?.name;
139
157
  if (typeof scopedKind === "string" && typeof scopedName === "string") {
140
- record(scopedKind, scopedName, true);
158
+ record(scopedKind, keyOf(scopedName), true, forwardedFrom, forwardedFrom);
141
159
  }
142
160
  }
143
161
  }
@@ -207,8 +225,8 @@ function* importedExports(manifests, aliases) {
207
225
  * flat field pass exactly as they do today. */
208
226
  export function buildObservedStateResourcesSchema(index, open) {
209
227
  const properties = {};
210
- for (const [key, { status }] of index) {
211
- if (!status)
228
+ for (const [key, { status, forwardedFrom }] of index) {
229
+ if (!status || forwardedFrom !== undefined)
212
230
  continue;
213
231
  applyObservedStateNode(properties, key, status);
214
232
  }
@@ -12,20 +12,16 @@ import { type AnalysisDiagnostic } from "./types.js";
12
12
  * `capability` is not `Telo.Provider`.
13
13
  * - PROVIDE_DISPATCHER_CONFLICT: `provide:` co-exists with `invoke:` or `run:`
14
14
  * on the same definition.
15
- * - PROVIDE_TARGET_UNKNOWN: `provide.name` does not resolve to an entry in
16
- * `resources:`.
17
- * - PROVIDE_TARGET_NOT_INVOCABLE: `provide.name` resolves to a resource whose
18
- * kind is registered but not a `Telo.Invocable`.
19
15
  * - PROVIDER_MISSING_IMPLEMENTATION: definition with `capability: Telo.Provider`
20
16
  * declares neither `controllers:` (TS-backed) nor `provide:` (template-backed).
21
17
  * - MOUNT_ON_NON_MOUNT: `mount:` declared on a definition whose `capability` is
22
18
  * not `Telo.Mount`.
23
19
  * - MOUNT_DISPATCHER_CONFLICT: `mount:` co-exists with another dispatch
24
20
  * entry-point (`invoke:` / `run:` / `provide:`).
25
- * - MOUNT_TARGET_UNKNOWN: `mount.name` does not resolve to an entry in
26
- * `resources:`.
27
- * - MOUNT_TARGET_NOT_MOUNTABLE: `mount.name` resolves to a resource whose kind
28
- * is registered but not a `Telo.Mount`.
21
+ *
22
+ * What a dispatch slot NAMES — that it is a `!ref`, that the entry exists, that
23
+ * its capability carries the method is `validate-template-body`'s, for every
24
+ * slot alike.
29
25
  */
30
26
  export declare function validateProviderCoherence(manifests: ResourceManifest[], registry: DefinitionRegistry, aliases: AliasResolver): AnalysisDiagnostic[];
31
27
  //# sourceMappingURL=validate-provider-coherence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-provider-coherence.d.ts","sourceRoot":"","sources":["../src/validate-provider-coherence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,aAAa,GACrB,kBAAkB,EAAE,CA+NtB"}
1
+ {"version":3,"file":"validate-provider-coherence.d.ts","sourceRoot":"","sources":["../src/validate-provider-coherence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAMnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,aAAa,GACrB,kBAAkB,EAAE,CAiHtB"}
@@ -1,4 +1,4 @@
1
- import { controllerBearingAncestor } from "./extends-resolution.js";
1
+ import { controllerBearingAncestor, inheritedCapability, } from "./extends-resolution.js";
2
2
  import { DiagnosticSeverity } from "./types.js";
3
3
  const SOURCE = "telo-analyzer";
4
4
  /**
@@ -11,20 +11,16 @@ const SOURCE = "telo-analyzer";
11
11
  * `capability` is not `Telo.Provider`.
12
12
  * - PROVIDE_DISPATCHER_CONFLICT: `provide:` co-exists with `invoke:` or `run:`
13
13
  * on the same definition.
14
- * - PROVIDE_TARGET_UNKNOWN: `provide.name` does not resolve to an entry in
15
- * `resources:`.
16
- * - PROVIDE_TARGET_NOT_INVOCABLE: `provide.name` resolves to a resource whose
17
- * kind is registered but not a `Telo.Invocable`.
18
14
  * - PROVIDER_MISSING_IMPLEMENTATION: definition with `capability: Telo.Provider`
19
15
  * declares neither `controllers:` (TS-backed) nor `provide:` (template-backed).
20
16
  * - MOUNT_ON_NON_MOUNT: `mount:` declared on a definition whose `capability` is
21
17
  * not `Telo.Mount`.
22
18
  * - MOUNT_DISPATCHER_CONFLICT: `mount:` co-exists with another dispatch
23
19
  * entry-point (`invoke:` / `run:` / `provide:`).
24
- * - MOUNT_TARGET_UNKNOWN: `mount.name` does not resolve to an entry in
25
- * `resources:`.
26
- * - MOUNT_TARGET_NOT_MOUNTABLE: `mount.name` resolves to a resource whose kind
27
- * is registered but not a `Telo.Mount`.
20
+ *
21
+ * What a dispatch slot NAMES — that it is a `!ref`, that the entry exists, that
22
+ * its capability carries the method is `validate-template-body`'s, for every
23
+ * slot alike.
28
24
  */
29
25
  export function validateProviderCoherence(manifests, registry, aliases) {
30
26
  const diagnostics = [];
@@ -50,13 +46,18 @@ export function validateProviderCoherence(manifests, registry, aliases) {
50
46
  const resource = { kind: m.kind, name };
51
47
  const label = `${m.kind}/${name}`;
52
48
  const md = m;
53
- const capability = typeof md.capability === "string" ? md.capability : undefined;
49
+ const resolveDef = (k) => registry.resolve(aliases.resolveKind(k) ?? k) ?? registry.resolve(k);
50
+ // The INHERITED capability, not the declared one: an `extends` child writes
51
+ // none of its own, so reading `md.capability` reported a child that inherits
52
+ // `Telo.Provider` and declares `provide:` as PROVIDE_ON_NON_PROVIDER
53
+ // "(found '<unset>')" — the same defect `celRuleApplies` and
54
+ // `validate-template-body` were fixed for, in the file next door.
55
+ const capability = inheritedCapability(m, resolveDef);
54
56
  const provide = md.provide;
55
57
  const invoke = md.invoke;
56
58
  const run = md.run;
57
59
  const mount = md.mount;
58
60
  const controllers = md.controllers;
59
- const resources = md.resources;
60
61
  const hasProvide = provide !== undefined && provide !== null;
61
62
  const hasInvoke = invoke !== undefined && invoke !== null;
62
63
  const hasRun = run !== undefined && run !== null;
@@ -83,67 +84,6 @@ export function validateProviderCoherence(manifests, registry, aliases) {
83
84
  data: { resource, filePath, path: "provide" },
84
85
  });
85
86
  }
86
- if (hasProvide && typeof provide === "object" && !Array.isArray(provide)) {
87
- const provideObj = provide;
88
- const providedName = typeof provideObj.name === "string" ? provideObj.name : undefined;
89
- const providedKind = typeof provideObj.kind === "string" ? provideObj.kind : undefined;
90
- if (providedName && Array.isArray(resources)) {
91
- const match = resources.find((r) => {
92
- const meta = r?.metadata;
93
- return typeof meta?.name === "string" && meta.name === providedName;
94
- });
95
- if (!match) {
96
- diagnostics.push({
97
- severity: DiagnosticSeverity.Error,
98
- code: "PROVIDE_TARGET_UNKNOWN",
99
- source: SOURCE,
100
- message: `${label}: 'provide.name: ${providedName}' does not match any entry's ` +
101
- `metadata.name in 'resources:'.`,
102
- data: { resource, filePath, path: "provide.name" },
103
- });
104
- }
105
- else if (typeof match.kind === "string") {
106
- // `provide.kind` is the type contract the analyzer uses to type
107
- // `result` CEL against the target's `outputType`. The runtime
108
- // dispatches on `provide.name` and ignores `provide.kind`, so a
109
- // mismatch silently degrades `result` typing to an open schema
110
- // (and at runtime quietly invokes the actually-matched resource).
111
- // Flag the divergence so result-typing never lies.
112
- if (providedKind) {
113
- const providedCanonical = aliases.resolveKind(providedKind) ?? providedKind;
114
- const matchCanonical = aliases.resolveKind(match.kind) ?? match.kind;
115
- if (providedCanonical !== matchCanonical) {
116
- diagnostics.push({
117
- severity: DiagnosticSeverity.Error,
118
- code: "PROVIDE_KIND_MISMATCH",
119
- source: SOURCE,
120
- message: `${label}: 'provide.kind: ${providedKind}' disagrees with the matched ` +
121
- `'resources:' entry's kind '${match.kind}' (matched by metadata.name ` +
122
- `'${providedName}'). The runtime dispatches by name, so 'provide.kind' ` +
123
- `is decorative — but the analyzer types 'result:' against it, and a ` +
124
- `mismatch silently turns off that typing.`,
125
- data: { resource, filePath, path: "provide.kind" },
126
- });
127
- }
128
- }
129
- const resolvedKind = aliases.resolveKind(match.kind) ?? match.kind;
130
- const targetDef = registry.resolve(resolvedKind) ?? registry.resolve(match.kind);
131
- if (targetDef && targetDef.kind === "Telo.Definition") {
132
- const targetCap = targetDef.capability;
133
- if (typeof targetCap === "string" && targetCap !== "Telo.Invocable") {
134
- diagnostics.push({
135
- severity: DiagnosticSeverity.Error,
136
- code: "PROVIDE_TARGET_NOT_INVOCABLE",
137
- source: SOURCE,
138
- message: `${label}: 'provide.name: ${providedName}' resolves to a ${match.kind} ` +
139
- `(capability '${targetCap}'); 'provide:' requires a Telo.Invocable target.`,
140
- data: { resource, filePath, path: "provide.name" },
141
- });
142
- }
143
- }
144
- }
145
- }
146
- }
147
87
  if (hasMount && capability !== "Telo.Mount") {
148
88
  diagnostics.push({
149
89
  severity: DiagnosticSeverity.Error,
@@ -165,60 +105,9 @@ export function validateProviderCoherence(manifests, registry, aliases) {
165
105
  data: { resource, filePath, path: "mount" },
166
106
  });
167
107
  }
168
- if (hasMount) {
169
- // Resolve the target's name from either form: the bare string (the
170
- // primary, documented form — `mount: api`) or the object's `name`. A CEL
171
- // target (`${{ … }}`) can only be checked at runtime, so skip those.
172
- let mountedName;
173
- if (typeof mount === "string") {
174
- if (!mount.includes("${{"))
175
- mountedName = mount;
176
- }
177
- else if (typeof mount === "object" && !Array.isArray(mount)) {
178
- const mountObj = mount;
179
- if (typeof mountObj.name === "string" && !mountObj.name.includes("${{")) {
180
- mountedName = mountObj.name;
181
- }
182
- }
183
- const mountPath = typeof mount === "string" ? "mount" : "mount.name";
184
- if (mountedName && Array.isArray(resources)) {
185
- const match = resources.find((r) => {
186
- const meta = r?.metadata;
187
- return typeof meta?.name === "string" && meta.name === mountedName;
188
- });
189
- if (!match) {
190
- diagnostics.push({
191
- severity: DiagnosticSeverity.Error,
192
- code: "MOUNT_TARGET_UNKNOWN",
193
- source: SOURCE,
194
- message: `${label}: '${mountPath}: ${mountedName}' does not match any entry's ` +
195
- `metadata.name in 'resources:'.`,
196
- data: { resource, filePath, path: mountPath },
197
- });
198
- }
199
- else if (typeof match.kind === "string") {
200
- const resolvedKind = aliases.resolveKind(match.kind) ?? match.kind;
201
- const targetDef = registry.resolve(resolvedKind) ?? registry.resolve(match.kind);
202
- if (targetDef && targetDef.kind === "Telo.Definition") {
203
- const targetCap = targetDef.capability;
204
- if (typeof targetCap === "string" && targetCap !== "Telo.Mount") {
205
- diagnostics.push({
206
- severity: DiagnosticSeverity.Error,
207
- code: "MOUNT_TARGET_NOT_MOUNTABLE",
208
- source: SOURCE,
209
- message: `${label}: '${mountPath}: ${mountedName}' resolves to a ${match.kind} ` +
210
- `(capability '${targetCap}'); 'mount:' requires a Telo.Mount target.`,
211
- data: { resource, filePath, path: mountPath },
212
- });
213
- }
214
- }
215
- }
216
- }
217
- }
218
108
  // A definition that inherits a controller by delegation (concrete `extends`,
219
109
  // no own controller/template) satisfies the implementation requirement
220
110
  // through its parent — `base:` supplies the parent's config.
221
- const resolveDef = (k) => registry.resolve(aliases.resolveKind(k) ?? k) ?? registry.resolve(k);
222
111
  const inheritsController = typeof md.extends === "string" &&
223
112
  controllerBearingAncestor(m, resolveDef) !== undefined;
224
113
  if (capability === "Telo.Provider" && !hasControllers && !hasProvide && !inheritsController) {
@@ -1 +1 @@
1
- {"version":3,"file":"validate-reference-forms.d.ts","sourceRoot":"","sources":["../src/validate-reference-forms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAInE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,kBAAkB,EAAE,CA0EtB"}
1
+ {"version":3,"file":"validate-reference-forms.d.ts","sourceRoot":"","sources":["../src/validate-reference-forms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAKnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,kBAAkB,EAAE,CAwEtB"}
@@ -1,4 +1,5 @@
1
1
  import { isTaggedSentinel } from "@telorun/templating";
2
+ import { isForwardedDeclaration } from "./forwarded-declaration.js";
2
3
  import { visitManifest } from "./manifest-visitor.js";
3
4
  import { satisfiesValueBranch } from "./reference-field-map.js";
4
5
  import { REF_VALIDATION_SKIP_KINDS as SYSTEM_KINDS } from "./system-kinds.js";
@@ -40,8 +41,7 @@ export function validateReferenceForms(resources, registry, aliases, aliasesByMo
40
41
  if (!aliases)
41
42
  return [];
42
43
  const diagnostics = [];
43
- const isForeign = (r) => r.metadata?.forwardedExport === true;
44
- const localResources = resources.filter((r) => !isForeign(r));
44
+ const localResources = resources.filter((r) => !isForwardedDeclaration(r));
45
45
  visitManifest(localResources, registry, {
46
46
  onRef: (e) => {
47
47
  const value = e.value;
@@ -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;AAarD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAMnE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,kBAAkB;AAC5B;8EAC8E;AAC9E,aAAa,UAAQ,GACpB,OAAO,CAwBT;AAgED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,GACvB,kBAAkB,EAAE,CAwjBtB"}
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;AAarD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AA2InE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,kBAAkB;AAC5B;8EAC8E;AAC9E,aAAa,UAAQ,GACpB,OAAO,CAwBT;AAgED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,GACvB,kBAAkB,EAAE,CAwgBtB"}
@@ -6,8 +6,141 @@ import { REF_VALIDATION_SKIP_KINDS as SYSTEM_KINDS } from "./system-kinds.js";
6
6
  import { resolveTypeFieldToSchema } from "./validate-cel-context.js";
7
7
  import { DiagnosticSeverity } from "./types.js";
8
8
  import { moduleAliasScope } from "./module-alias-scope.js";
9
+ import { isModuleKind } from "./module-kinds.js";
9
10
  import { isInjectedDeclaration } from "./resource-input.js";
11
+ import { outsideScopesOf } from "./scope-declarations.js";
12
+ import { isForwardedDeclaration, isForwardedExport } from "./forwarded-declaration.js";
10
13
  const SOURCE = "telo-analyzer";
14
+ /** What a name IS on one side of a collision. The three shapes share the
15
+ * kernel's one namespace but read as different things to an author — an alias
16
+ * is not a resource, and a module's own name is not a declaration inside it —
17
+ * so calling all three "resource name" describes two of them wrongly. */
18
+ function describeNamed(m, name) {
19
+ if (m.kind === "Telo.Import")
20
+ return `import alias '${name}'`;
21
+ if (isModuleKind(m.kind))
22
+ return `this module's own name (${m.kind} '${name}')`;
23
+ return `resource ${m.kind}/${name}`;
24
+ }
25
+ /**
26
+ * WHAT SHARES A NAME AT THE KERNEL.
27
+ *
28
+ * `registerManifest` keys on `metadata.name` alone, per module context, for
29
+ * EVERY kind — so an import alias, a kind definition and an ordinary resource
30
+ * are one namespace. This check used to model a narrower one: `Telo.Import` was
31
+ * excluded outright, on the stated grounds that an alias "lives in a separate
32
+ * namespace from resources", and `Telo.Definition` / `Telo.Abstract` fell out
33
+ * through the ref-validation skip set, which answers a different question. All
34
+ * three passed `telo check` and then died at boot with `ERR_DUPLICATE_RESOURCE`
35
+ * — an application named after one of its own imports (`metadata.name: Schedule`
36
+ * beside `Schedule: oci://…/scheduler`) being the shape that reaches an author,
37
+ * since nothing about the two lines looks like one name written twice.
38
+ *
39
+ * Grouped per DECLARING MODULE, which is what makes including imports sound: a
40
+ * library's own `Telo.Import` / `Telo.Definition` docs are forwarded into a
41
+ * consumer's flat set, and two modules sharing an alias (`Console` in an app and
42
+ * again in a library it imports) is ordinary rather than a collision. A module
43
+ * doc carries no `metadata.module` and IS its own scope — that is what puts an
44
+ * application's own name in the same group as its imports.
45
+ *
46
+ * Kept apart from the resolution lookup it used to share a map with: that one
47
+ * must hold resolution targets alone, so the two disagree about membership by
48
+ * design and merging them re-keyed every bare-name reference.
49
+ */
50
+ function duplicateNameDiagnostics(resources, isForeign, moduleOf) {
51
+ const diagnostics = [];
52
+ const moduleDocNames = new Set();
53
+ for (const r of resources) {
54
+ if (isModuleKind(r.kind) && typeof r.metadata?.name === "string") {
55
+ moduleDocNames.add(r.metadata.name);
56
+ }
57
+ }
58
+ // A module doc belongs in the scope its OWN resources are in. Flattened, they
59
+ // carry `metadata.module` equal to its name, so it scopes to itself — which is
60
+ // what puts an application's name beside its imports. Unflattened (a hand-built
61
+ // fixture, a single-file editor analysis) nothing is stamped, so its resources
62
+ // sit in the unnamed scope and the doc has to join them there or a collision
63
+ // with one of them splits across two groups and goes unreported.
64
+ const stampedModules = new Set();
65
+ for (const r of resources) {
66
+ const m = moduleOf(r);
67
+ if (m)
68
+ stampedModules.add(m);
69
+ }
70
+ // scope → name → declarations, so no separator has to be safe against a name.
71
+ const byScope = new Map();
72
+ const seen = new Set();
73
+ for (const r of resources) {
74
+ if (!r.metadata?.name || isForeign(r))
75
+ continue;
76
+ const name = r.metadata.name;
77
+ const scope = isModuleKind(r.kind)
78
+ ? (stampedModules.has(name) ? name : "")
79
+ : (moduleOf(r) ?? "");
80
+ // A scope with no module doc in this set is an imported library's, whose
81
+ // doc `selectModuleManifestsForAnalysis` drops — its internals are its own
82
+ // author's to fix, and its own `telo check` reports them.
83
+ if (scope !== "" && moduleDocNames.size > 0 && !moduleDocNames.has(scope))
84
+ continue;
85
+ // Dedup pipeline echoes — the same physical document emitted twice through
86
+ // an analyzer host's pipeline (telo studio walks a file reachable as both an
87
+ // entry module and an `include:` partial). Keyed on (kind, name, source,
88
+ // sourceLine), so two textually-distinct docs in one file keep separate
89
+ // fingerprints and still trip the diagnostic.
90
+ const meta = r.metadata;
91
+ const fingerprint = `${r.kind} ${name} ${meta.source} ${meta.sourceLine}`;
92
+ if (seen.has(fingerprint))
93
+ continue;
94
+ seen.add(fingerprint);
95
+ let names = byScope.get(scope);
96
+ if (!names)
97
+ byScope.set(scope, (names = new Map()));
98
+ const existing = names.get(name);
99
+ if (existing)
100
+ existing.push(r);
101
+ else
102
+ names.set(name, [r]);
103
+ }
104
+ for (const names of byScope.values()) {
105
+ for (const [name, list] of names) {
106
+ if (list.length <= 1)
107
+ continue;
108
+ const [first, ...rest] = list;
109
+ for (const dup of rest) {
110
+ // Two imports sharing an alias is ONE defect with its own diagnostic
111
+ // (`DUPLICATE_IMPORT_ALIAS`), which says what to do about it; reporting
112
+ // it again here would describe one mistake as two.
113
+ if (dup.kind === "Telo.Import" && first.kind === "Telo.Import")
114
+ continue;
115
+ const dupMeta = dup.metadata;
116
+ // The precomputed range matters because editor hosts resolve positions
117
+ // via a `${file}::${kind}::${name}` lookup, which collides on duplicates.
118
+ const range = typeof dupMeta?.sourceLine === "number"
119
+ ? {
120
+ start: { line: dupMeta.sourceLine, character: 0 },
121
+ end: { line: dupMeta.sourceLine, character: Number.MAX_SAFE_INTEGER },
122
+ }
123
+ : undefined;
124
+ diagnostics.push({
125
+ severity: DiagnosticSeverity.Error,
126
+ code: "DUPLICATE_RESOURCE_NAME",
127
+ source: SOURCE,
128
+ message: `${dup.kind}/${name}: ${describeNamed(dup, name)} collides with ` +
129
+ `${describeNamed(first, name)} declared earlier — the kernel registers both ` +
130
+ `under '${name}' in one namespace, so boot fails with ERR_DUPLICATE_RESOURCE. ` +
131
+ `Rename one of them.`,
132
+ ...(range ? { range } : {}),
133
+ data: {
134
+ resource: { kind: dup.kind, name },
135
+ filePath: dupMeta?.source,
136
+ path: "metadata.name",
137
+ },
138
+ });
139
+ }
140
+ }
141
+ }
142
+ return diagnostics;
143
+ }
11
144
  /**
12
145
  * Liskov substitutability at a kind constraint: is `resolved` (a canonical
13
146
  * `<module>.<Kind>`) accepted where `targetKind` is required?
@@ -142,48 +275,24 @@ export function validateReferences(resources, context) {
142
275
  const aliasesByModule = context.aliasesByModule;
143
276
  if (!aliases || !registry)
144
277
  return diagnostics;
145
- // Build outer resource lookup by name for resolution check, collecting
146
- // every entry per name so we can surface name collisions as diagnostics
147
- // (the kernel's resource registry shares one namespace across all
148
- // non-system kinds e.g. `Telo.Application HelloApi` and `Http.Api
149
- // HelloApi` collide at boot with `ERR_DUPLICATE_RESOURCE`. Catching it
150
- // statically removes a class of "everything analyzes clean, then the
151
- // kernel refuses to start" surprises.)
152
- //
153
- // Telo.Import is excluded from the duplicate check on top of the
154
- // SYSTEM_KINDS skip: its `metadata.name` is an alias, not a resource
155
- // identity (aliases live in a separate namespace from resources, and
156
- // colliding aliases vs. resource names is benign — the alias is only
157
- // ever read as a kind prefix).
158
- // Group manifests by name to detect collisions. Two subtleties:
278
+ // Build the outer resource lookup by name, for the resolution checks below.
279
+ // WHICH NAMES COLLIDE is a different question over a different namespace and
280
+ // is answered by `duplicateNameDiagnostics`; this map answers only "what does
281
+ // a bare `!ref <name>` resolve to", so it holds resolution TARGETS alone — an
282
+ // import alias and a kind definition are neither, and admitting them here
283
+ // would resolve a reference to something no ref slot can accept.
159
284
  //
160
- // 1. Some analyzer hosts emit the SAME physical document twice through
161
- // their pipeline e.g. telo studio's `toAnalysisManifests` walks
162
- // each workspace module's documents independently, and a file
163
- // reachable from two angles (entry module + `include:` partial)
164
- // shows up twice. The fingerprint includes `sourceLine` so identical
165
- // docs (same kind, name, source, AND source line) collapse to one,
166
- // while two textually-separate documents in the same file (different
167
- // source lines) keep separate fingerprints and trip the diagnostic.
168
- // 2. The diagnostic carries a precomputed `range` pointing at the
169
- // duplicate's source line — editor hosts that resolve diagnostic
170
- // positions via a `${file}::${kind}::${name}` lookup would otherwise
171
- // collide on duplicates (Map.set overwrites) and place the squiggle
172
- // ambiguously. The explicit `range` short-circuits that lookup.
173
- // Dedup pipeline echoes — the same physical document emitted twice
174
- // through an analyzer host's pipeline. Keyed on (kind, name, source,
175
- // sourceLine), so two textually-distinct docs in the same file (same
176
- // source, different sourceLine) keep separate fingerprints and still
177
- // trip the diagnostic. `analyze()` enforces that every non-system
178
- // manifest carries both positional fields — no defensive guard needed.
285
+ // The list per name survives because resolution falls back to the FIRST
286
+ // occurrence when a collision exists, which keeps the rest of the pass
287
+ // behaving as it did before duplicates were reported at all.
179
288
  // Forwarded foreign exports (an imported library's exported instances, carrying a
180
289
  // metadata.module that isn't a root module) are resolution TARGETS only: excluded from
181
290
  // duplicate detection and local name resolution, and never walked as ref sources.
182
291
  const moduleOf = (r) => r.metadata?.module;
183
- // Forwarded exports are flagged by flattenForAnalyzer (`metadata.forwardedExport`); they're
184
- // cross-module resolution targets only — excluded from duplicate detection and local name
185
- // resolution, and never walked as ref sources.
186
- const isForeign = (r) => r.metadata?.forwardedExport === true;
292
+ // A dependency's code a forwarded export, or what extraction pulled out of one — is
293
+ // excluded from duplicate detection and local name resolution, and never walked as a
294
+ // ref source. Only an EXPORT is a cross-module resolution target.
295
+ const isForeign = isForwardedDeclaration;
187
296
  // Forwarded exported instances keyed `${module}\0${name}` — the lookup that resolves
188
297
  // whether a cross-module `!ref Alias.name` names a real exported instance.
189
298
  const byModuleName = new Map();
@@ -201,7 +310,7 @@ export function validateReferences(resources, context) {
201
310
  loadedModules.add(m);
202
311
  continue;
203
312
  }
204
- if (!r.metadata?.name || SYSTEM_KINDS.has(r.kind) || !isForeign(r))
313
+ if (!r.metadata?.name || SYSTEM_KINDS.has(r.kind) || !isForwardedExport(r))
205
314
  continue;
206
315
  const m = moduleOf(r);
207
316
  if (!m)
@@ -229,33 +338,7 @@ export function validateReferences(resources, context) {
229
338
  else
230
339
  byNameAll.set(name, [r]);
231
340
  }
232
- for (const [name, list] of byNameAll) {
233
- if (list.length <= 1)
234
- continue;
235
- const [first, ...rest] = list;
236
- const firstLabel = `${first.kind}/${name}`;
237
- for (const dup of rest) {
238
- const dupMeta = dup.metadata;
239
- const range = typeof dupMeta?.sourceLine === "number"
240
- ? {
241
- start: { line: dupMeta.sourceLine, character: 0 },
242
- end: { line: dupMeta.sourceLine, character: Number.MAX_SAFE_INTEGER },
243
- }
244
- : undefined;
245
- diagnostics.push({
246
- severity: DiagnosticSeverity.Error,
247
- code: "DUPLICATE_RESOURCE_NAME",
248
- source: SOURCE,
249
- message: `${dup.kind}/${name}: resource name collides with ${firstLabel} declared earlier (kernel runtime would fail with ERR_DUPLICATE_RESOURCE)`,
250
- ...(range ? { range } : {}),
251
- data: {
252
- resource: { kind: dup.kind, name },
253
- filePath: dupMeta?.source,
254
- path: "metadata.name",
255
- },
256
- });
257
- }
258
- }
341
+ diagnostics.push(...duplicateNameDiagnostics(resources, isForeign, moduleOf));
259
342
  // The dot rule that used to live here is now the strictest special case of
260
343
  // the identifier grammar in `validate-identifier-names.ts` — a dot is one of
261
344
  // several characters that make a name unreferenceable, and checking one of
@@ -317,6 +400,10 @@ export function validateReferences(resources, context) {
317
400
  const target = visibleScopeManifests.find((m) => m.metadata?.name === localName) ??
318
401
  byName.get(localName);
319
402
  if (!target) {
403
+ // A scoped name out of this declaration's reach has its own
404
+ // diagnostic, which says why; this one would say only "not found".
405
+ if (outsideScopesOf(r).some((scope) => scope.names.includes(localName)))
406
+ return;
320
407
  diagnostics.push({
321
408
  severity: DiagnosticSeverity.Error,
322
409
  code: "UNRESOLVED_REFERENCE",
@@ -0,0 +1,31 @@
1
+ import type { ResourceManifest } from "@telorun/sdk";
2
+ import type { AliasResolver } from "./alias-resolver.js";
3
+ import type { DefinitionRegistry } from "./definition-registry.js";
4
+ import { type AnalysisDiagnostic } from "./types.js";
5
+ /**
6
+ * `SCOPED_NAME_OUT_OF_REACH` — a declaration written inside a scope's region but
7
+ * created outside the scope (see {@link OutsideScope}) naming one of the scope's
8
+ * resources.
9
+ *
10
+ * Written there, the name reads as reachable — the scope's names shadow the
11
+ * module's in its regions — while at run time it resolves to nothing, or to a
12
+ * module-level resource that merely shares it. Reported at the reference, in
13
+ * each spelling a name can be read in:
14
+ *
15
+ * - a `!ref` sentinel, anywhere — the tag is unambiguous;
16
+ * - a resolved `{kind, name}`, but ONLY where the declaring kind says a
17
+ * reference is: a reference slot of its field map, or a step's dispatch slot.
18
+ * An object that merely has those keys is data;
19
+ * - `resources.<name>` in a CEL expression.
20
+ *
21
+ * Lexical: a scope declared on the way from the out-of-reach declaration down to
22
+ * the reference — found through the kinds' own scope slots — shadows the name
23
+ * again, since that one IS created around it. Entry-module-scoped: a
24
+ * dependency's code is its own analysis's to report.
25
+ *
26
+ * Browser-safe.
27
+ */
28
+ export declare function validateScopedNameReach(manifests: ResourceManifest[], registry: DefinitionRegistry, aliases: AliasResolver, aliasesByModule: Map<string, AliasResolver>, rootModules: ReadonlySet<string>,
29
+ /** Member-access chains of a CEL expression — the analyzer's own parse. */
30
+ accessChains: (expression: string) => string[][]): AnalysisDiagnostic[];
31
+ //# sourceMappingURL=validate-scope-reach.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-scope-reach.d.ts","sourceRoot":"","sources":["../src/validate-scope-reach.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAanE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAYzE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,aAAa,EACtB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EAC3C,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC;AAChC,2EAA2E;AAC3E,YAAY,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,EAAE,EAAE,GAC/C,kBAAkB,EAAE,CAyItB"}