@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
@@ -14,10 +14,22 @@
14
14
  * drift: it means nothing is published, which is the correct reading for a
15
15
  * module that has never shipped.
16
16
  *
17
- * **It records the registry base**, because canonicalizing a relative `imports:`
18
- * source writes the destination into the manifest layer — so the digests below
19
- * are digests *against that base*, and comparing them to digests taken against
20
- * another one would be comparing two different artifacts.
17
+ * **Every entry records its own registry base**, because canonicalizing a
18
+ * relative `imports:` source writes the destination into the manifest layer — so
19
+ * each digest is a digest *against that base*, and comparing it to one taken
20
+ * against another would be comparing two different artifacts.
21
+ *
22
+ * Per entry rather than one top-level base with per-entry deltas: a workspace
23
+ * whose `release.modules` entries each author a destination has no meaningful
24
+ * top-level base, an absent one already means *nothing has been published yet*,
25
+ * and the agreement check returns early on that — so the multi-destination
26
+ * workspace would be the one whose bases are never compared. The file is
27
+ * generated and never hand-maintained, so the redundancy costs nothing and it
28
+ * removes the "differs from what?" question and the absent-versus-unknown
29
+ * conflation together. A **top-level `registry:` is a legacy form the reader
30
+ * accepts and never writes**, applied to every entry: the credential-free PR
31
+ * gate reads whatever ledger is committed on the branch, and that stays in the
32
+ * old shape until a release regenerates it.
21
33
  */
22
34
 
23
35
  import { Document, parseDocument } from "yaml";
@@ -31,17 +43,14 @@ export interface LedgerEntry {
31
43
  /** The version these digests were taken at — the tag the artifact published
32
44
  * under. */
33
45
  readonly version: string;
46
+ /** The publish destination base these digests were taken against
47
+ * (`oci://ghcr.io/telorun`). Absent only in a ledger written before the base
48
+ * was recorded at all. */
49
+ readonly registry?: string;
34
50
  readonly layers: LayerDigests;
35
51
  }
36
52
 
37
53
  export interface Ledger {
38
- /**
39
- * The publish destination base the digests were taken against
40
- * (`oci://ghcr.io/telorun`). Absent in a workspace that has published nothing
41
- * yet, which is why it is optional rather than required — but a `check` that
42
- * has entries and no base cannot reproduce them, and says so.
43
- */
44
- readonly registry?: string;
45
54
  readonly modules: ReadonlyMap<ModuleKey, LedgerEntry>;
46
55
  }
47
56
 
@@ -72,13 +81,14 @@ export function parseLedger(text: string, where: string): Ledger {
72
81
  }
73
82
  }
74
83
 
75
- const registry = record.registry;
76
- if (registry !== undefined && typeof registry !== "string") {
84
+ // The legacy top-level base: read and applied to every entry, never written.
85
+ const inherited = record.registry;
86
+ if (inherited !== undefined && typeof inherited !== "string") {
77
87
  throw new LedgerError(`${where}: 'registry' must be the publish destination base, as a string.`);
78
88
  }
79
89
 
80
90
  const rawModules = record.modules;
81
- if (rawModules === undefined || rawModules === null) return { ...(registry ? { registry } : {}), modules: new Map() };
91
+ if (rawModules === undefined || rawModules === null) return { modules: new Map() };
82
92
  if (typeof rawModules !== "object" || Array.isArray(rawModules)) {
83
93
  throw new LedgerError(`${where}: 'modules' must be a mapping of module path to entry.`);
84
94
  }
@@ -89,6 +99,20 @@ export function parseLedger(text: string, where: string): Ledger {
89
99
  throw new LedgerError(`${where}: entry '${key}' must be a mapping.`);
90
100
  }
91
101
  const entry = raw as Record<string, unknown>;
102
+ for (const field of Object.keys(entry)) {
103
+ if (field !== "version" && field !== "registry" && field !== "layers") {
104
+ throw new LedgerError(
105
+ `${where}: entry '${key}' has unknown field '${field}'. An entry carries 'version:', ` +
106
+ `'registry:' and 'layers:'.`,
107
+ );
108
+ }
109
+ }
110
+ if (entry.registry !== undefined && typeof entry.registry !== "string") {
111
+ throw new LedgerError(
112
+ `${where}: entry '${key}' has a non-string 'registry'. It is the publish destination base ` +
113
+ `the digests beside it were taken against.`,
114
+ );
115
+ }
92
116
  if (!isReleaseVersion(entry.version)) {
93
117
  throw new LedgerError(
94
118
  `${where}: entry '${key}' has no major.minor.patch 'version'. The ledger records the ` +
@@ -108,10 +132,15 @@ export function parseLedger(text: string, where: string): Ledger {
108
132
  layers[layer] = digest;
109
133
  }
110
134
  }
111
- modules.set(normalizeModuleKey(key), { version: entry.version, layers });
135
+ const registry = (entry.registry as string | undefined) ?? inherited;
136
+ modules.set(normalizeModuleKey(key), {
137
+ version: entry.version,
138
+ ...(registry ? { registry } : {}),
139
+ layers,
140
+ });
112
141
  }
113
142
 
114
- return { ...(registry ? { registry } : {}), modules };
143
+ return { modules };
115
144
  }
116
145
 
117
146
  /**
@@ -127,12 +156,13 @@ export function serializeLedger(ledger: Ledger): string {
127
156
  const entry = ledger.modules.get(key)!;
128
157
  const layers: Record<string, string> = {};
129
158
  for (const layer of Object.keys(entry.layers).sort()) layers[layer] = entry.layers[layer];
130
- modules[key] = { version: entry.version, layers };
159
+ modules[key] = {
160
+ version: entry.version,
161
+ ...(entry.registry ? { registry: entry.registry } : {}),
162
+ layers,
163
+ };
131
164
  }
132
- const doc = new Document({
133
- ...(ledger.registry ? { registry: ledger.registry } : {}),
134
- modules,
135
- });
165
+ const doc = new Document({ modules });
136
166
  return (
137
167
  "# Generated by `telo release apply` — what each module looks like as published.\n" +
138
168
  "# A cache of the registry's answer, so a PR gate needs no credentials.\n" +
@@ -54,7 +54,8 @@ export const LOCALLY_DERIVED_LAYERS: ReadonlySet<string> = new Set([MANIFEST_LAY
54
54
  * `axis=value;…` form, because this string is read by a human in a ledger diff
55
55
  * and written into a plain YAML key; the axis order is `PLATFORM_AXES`, so the
56
56
  * rendering is deterministic rather than dependent on how the selector was
57
- * built.
57
+ * built. An axis a selector omits renders nothing, so a new axis appended to the
58
+ * vocabulary leaves every existing key unchanged — reordering it would not.
58
59
  */
59
60
  export function layerDigestKey(role: LayerRole | string, selector?: ArtifactSelector): string {
60
61
  if (!selector) return role;
@@ -67,14 +67,15 @@ export interface ModuleEvidence {
67
67
  /** Whether a file under this module's own directory that reaches the artifact
68
68
  * changed. Decides only whether a changelog line is requested. */
69
69
  readonly ownFilesChanged: boolean;
70
+ /** The publish destination base THIS module's payload was built against —
71
+ * per module, because a workspace may declare one per subtree. */
72
+ readonly registry: string;
70
73
  }
71
74
 
72
75
  export interface ReleaseEvidence {
73
76
  readonly modules: readonly ModuleEvidence[];
74
77
  readonly ledger: Ledger;
75
78
  readonly fragments: readonly ReleaseFragment[];
76
- /** The publish destination base the digests above were built against. */
77
- readonly registry?: string;
78
79
  }
79
80
 
80
81
  /** Why a module is in the plan. A module usually carries several. */
@@ -123,14 +124,25 @@ export function planRelease(evidence: ReleaseEvidence): ReleasePlan {
123
124
  const diagnostics: ReleaseDiagnostic[] = [];
124
125
  const byKey = new Map(evidence.modules.map((module) => [module.key, module]));
125
126
 
126
- checkRegistryAgreement(evidence, diagnostics);
127
-
128
127
  // What the digest says, per module. A module with no ledger entry has never
129
128
  // been published, which is not drift — there is nothing to differ from.
130
129
  const drift = new Map<ModuleKey, LayerChange[]>();
131
130
  for (const module of evidence.modules) {
132
131
  const recorded = evidence.ledger.modules.get(module.key);
133
132
  if (!recorded) continue;
133
+ if (recorded.registry && recorded.registry !== module.registry) {
134
+ diagnostics.push({
135
+ severity: "error",
136
+ code: "LEDGER_REGISTRY_MISMATCH",
137
+ message:
138
+ `${module.key}: its ledger digests were taken against '${recorded.registry}', but this ` +
139
+ `run built against '${module.registry}'. Publishing rewrites each relative import to ` +
140
+ `'<base>/<sibling>@<version>', so the manifest layers of the two are different bytes ` +
141
+ `and comparing them would report the module as changed. Publish to the recorded base, ` +
142
+ `or re-record with \`telo release verify --write\`.`,
143
+ });
144
+ continue;
145
+ }
134
146
  if (recorded.version !== module.version) {
135
147
  diagnostics.push({
136
148
  severity: "error",
@@ -340,28 +352,6 @@ function requestMissingChangelogEntries(
340
352
  });
341
353
  }
342
354
 
343
- /**
344
- * The base the digests were built against has to be the base they were recorded
345
- * against, or the manifest layers are not comparable: canonicalization writes
346
- * the destination into them.
347
- */
348
- function checkRegistryAgreement(
349
- evidence: ReleaseEvidence,
350
- diagnostics: ReleaseDiagnostic[],
351
- ): void {
352
- const recorded = evidence.ledger.registry;
353
- if (!recorded || !evidence.registry || recorded === evidence.registry) return;
354
- diagnostics.push({
355
- severity: "error",
356
- code: "LEDGER_REGISTRY_MISMATCH",
357
- message:
358
- `The ledger's digests were taken against '${recorded}', but this run built against ` +
359
- `'${evidence.registry}'. Publishing rewrites each relative import to ` +
360
- `'<base>/<sibling>@<version>', so the manifest layers of the two are different bytes ` +
361
- `and comparing them would report every module as changed.`,
362
- });
363
- }
364
-
365
355
  /**
366
356
  * Dependency order over in-repo imports — a dependency before its dependents.
367
357
  *