@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
@@ -0,0 +1,227 @@
1
+ /**
2
+ * A module's **native files** — the `native:` block on a `Telo.Library` or
3
+ * `Telo.Application` doc, naming each platform-specific file the runtime does
4
+ * not import as a controller (a sidecar addon, a `dlopen`'d library, a
5
+ * per-platform data blob) once per platform tuple.
6
+ *
7
+ * ```yaml
8
+ * native:
9
+ * - name: better-sqlite3
10
+ * format: node
11
+ * os: linux
12
+ * arch: amd64
13
+ * libc: gnu
14
+ * abi: node-137
15
+ * path: ./native/linux-amd64-gnu-node-137/better_sqlite3.node
16
+ * ```
17
+ *
18
+ * Declared on the module doc rather than on a controller candidate, so the
19
+ * platform matrix is written once whatever the kind count, and the
20
+ * platform-neutral bundle ships once. Publish places each entry's file in the
21
+ * `native` layer of its selector (spec §1.2).
22
+ *
23
+ * `format` plus the platform axes build the same `ArtifactSelector` a
24
+ * controller candidate does, through the one selector normalizer. The declared
25
+ * `path` is the in-layer path — never flattened — because every layer of a
26
+ * module extracts into one directory, so paths must be disjoint per tuple.
27
+ *
28
+ * The single reader: the strict half is `validate-native-entries.ts`, and
29
+ * publish consumes the entries this returns. Browser-safe: string work only.
30
+ */
31
+
32
+ import {
33
+ ArtifactSelectorError,
34
+ PLATFORM_AXES,
35
+ selectorFromQualifiers,
36
+ type ArtifactSelector,
37
+ } from "./artifact-selector.js";
38
+
39
+ /** Every key an entry may carry. */
40
+ const KNOWN_KEYS = new Set<string>(["name", "format", "path", ...PLATFORM_AXES]);
41
+
42
+ /** Axes an entry must state: a native file is built for one platform. */
43
+ const REQUIRED_AXES = ["os", "arch"] as const;
44
+
45
+ /** The selector value grammar, applied to `name` as written. */
46
+ const NAME_TOKEN = /^[a-z0-9][a-z0-9_.-]*$/;
47
+
48
+ const GLOB_CHARS = /[*?[\]{}]/;
49
+ const URI_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
50
+
51
+ export interface NativeEntry {
52
+ /** The logical name controller code asks for. */
53
+ readonly name: string;
54
+ readonly selector: ArtifactSelector;
55
+ /** Module-root-relative POSIX path, normalized. */
56
+ readonly path: string;
57
+ /** Position in the `native:` list. */
58
+ readonly index: number;
59
+ /** Human-facing label for diagnostics: `native[0] ('better-sqlite3')`. */
60
+ readonly origin: string;
61
+ }
62
+
63
+ /**
64
+ * Why an entry could not be read.
65
+ *
66
+ * - `shape` — a missing, non-string or unknown key; the owner doc's JSON Schema
67
+ * reports these in the same analysis pass.
68
+ * - `invalid` — a value the schema accepts and the grammar rejects: an empty
69
+ * required string, a `name` or selector value outside the token grammar, an
70
+ * `abi` outside its value form, a path that names no single file.
71
+ * - `escape` — a `path` pointing outside the module directory.
72
+ */
73
+ export interface NativeEntryProblem {
74
+ readonly kind: "shape" | "invalid" | "escape";
75
+ readonly index?: number;
76
+ readonly origin: string;
77
+ /** Dotted/bracketed path from the module doc root, for `data.path`. */
78
+ readonly path: string;
79
+ /** What is wrong, prefixed with `origin`. */
80
+ readonly message: string;
81
+ }
82
+
83
+ export interface NativeEntries {
84
+ readonly entries: NativeEntry[];
85
+ readonly problems: NativeEntryProblem[];
86
+ }
87
+
88
+ export type PathVerdict = { path: string } | { kind: "invalid" | "escape"; detail: string };
89
+
90
+ /** Normalize a module-relative path, deciding confinement from the written
91
+ * path alone: the module root is the one directory every path is measured
92
+ * from, so `..` below depth zero is an escape wherever the module sits. */
93
+ export function normalizeNativePath(raw: string): PathVerdict {
94
+ if (URI_SCHEME.test(raw) || raw.startsWith("/") || raw.startsWith("\\")) {
95
+ return {
96
+ kind: "escape",
97
+ detail: `path '${raw}' is not relative to the module root. A native file ships inside the module artifact, so name it relative to the directory holding telo.yaml.`,
98
+ };
99
+ }
100
+ if (GLOB_CHARS.test(raw)) {
101
+ return {
102
+ kind: "invalid",
103
+ detail: `path '${raw}' looks like a pattern. A native entry names exactly one file.`,
104
+ };
105
+ }
106
+ const out: string[] = [];
107
+ for (const segment of raw.split(/[/\\]+/)) {
108
+ if (segment === "" || segment === ".") continue;
109
+ if (segment !== "..") {
110
+ out.push(segment);
111
+ continue;
112
+ }
113
+ if (out.length === 0) {
114
+ return {
115
+ kind: "escape",
116
+ detail: `path '${raw}' points above the module root. A native file must ship inside the module directory.`,
117
+ };
118
+ }
119
+ out.pop();
120
+ }
121
+ if (out.length === 0) {
122
+ return { kind: "invalid", detail: `path '${raw}' resolves to the module root, not to a file.` };
123
+ }
124
+ return { path: out.join("/") };
125
+ }
126
+
127
+ /**
128
+ * Read the `native:` block off an owner document's JSON projection.
129
+ *
130
+ * An entry with any problem is left out of `entries`, never read partially: a
131
+ * half-read entry would name a layer the author did not declare.
132
+ */
133
+ export function readNativeEntries(ownerJson: unknown): NativeEntries {
134
+ const declared = (ownerJson as { native?: unknown } | null)?.native;
135
+ const entries: NativeEntry[] = [];
136
+ const problems: NativeEntryProblem[] = [];
137
+ if (declared === undefined) return { entries, problems };
138
+ if (!Array.isArray(declared)) {
139
+ problems.push({
140
+ kind: "shape",
141
+ origin: "native",
142
+ path: "native",
143
+ message: "native: expected a list of entries.",
144
+ });
145
+ return { entries, problems };
146
+ }
147
+
148
+ declared.forEach((raw, index) => {
149
+ const at = `native[${index}]`;
150
+ const shape = (detail: string) =>
151
+ problems.push({ kind: "shape", index, origin: at, path: at, message: `${at}: ${detail}` });
152
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
153
+ shape("expected an object.");
154
+ return;
155
+ }
156
+ const entry = raw as Record<string, unknown>;
157
+
158
+ const unknown = Object.keys(entry).filter((key) => !KNOWN_KEYS.has(key));
159
+ if (unknown.length > 0) {
160
+ shape(
161
+ `unknown ${unknown.length === 1 ? "key" : "keys"} ${unknown.map((k) => `'${k}'`).join(", ")}. ` +
162
+ `Known: ${[...KNOWN_KEYS].join(", ")}.`,
163
+ );
164
+ return;
165
+ }
166
+ const requiredKeys = ["name", "format", ...REQUIRED_AXES, "path"];
167
+ const missing = requiredKeys.filter((key) => typeof entry[key] !== "string");
168
+ if (missing.length > 0) {
169
+ shape(`${missing.map((k) => `'${k}'`).join(", ")} must be strings.`);
170
+ return;
171
+ }
172
+ // A blank string satisfies the schema's `type: string`, so it is reported here.
173
+ const blank = requiredKeys.filter((key) => (entry[key] as string).trim() === "");
174
+ if (blank.length > 0) {
175
+ for (const key of blank) {
176
+ problems.push({
177
+ kind: "invalid",
178
+ index,
179
+ origin: at,
180
+ path: `${at}.${key}`,
181
+ message: `${at}: '${key}' must not be empty.`,
182
+ });
183
+ }
184
+ return;
185
+ }
186
+
187
+ const name = entry.name as string;
188
+ const origin = `${at} ('${name}')`;
189
+ if (!NAME_TOKEN.test(name)) {
190
+ problems.push({
191
+ kind: "invalid",
192
+ index,
193
+ origin,
194
+ path: `${at}.name`,
195
+ message:
196
+ `${origin}: name '${name}' is not a canonical token. Use lowercase letters, digits, ` +
197
+ `'.', '-' or '_', starting with a letter or digit.`,
198
+ });
199
+ return;
200
+ }
201
+
202
+ let selector: ArtifactSelector;
203
+ try {
204
+ selector = selectorFromQualifiers(entry.format, entry, origin);
205
+ } catch (err) {
206
+ if (!(err instanceof ArtifactSelectorError)) throw err;
207
+ problems.push({ kind: "invalid", index, origin, path: at, message: err.message });
208
+ return;
209
+ }
210
+
211
+ const verdict = normalizeNativePath((entry.path as string).trim());
212
+ if (!("path" in verdict)) {
213
+ problems.push({
214
+ kind: verdict.kind,
215
+ index,
216
+ origin,
217
+ path: `${at}.path`,
218
+ message: `${origin}: ${verdict.detail}`,
219
+ });
220
+ return;
221
+ }
222
+
223
+ entries.push({ name, selector, path: verdict.path, index, origin });
224
+ });
225
+
226
+ return { entries, problems };
227
+ }
@@ -0,0 +1,47 @@
1
+ import { distance } from "./levenshtein.js";
2
+
3
+ /**
4
+ * THE closest candidate to a misspelled name, or undefined.
5
+ *
6
+ * One rule, because a suggestion here is emitted as a `DiagnosticFix` — a
7
+ * whole-value replacement an editor applies in one click — so "closest" has to
8
+ * mean the same thing wherever it is offered. Two properties carry that:
9
+ *
10
+ * - **A TIE returns undefined.** Two candidates at equal distance make the pick
11
+ * arbitrary, and an arbitrary pick that is one click from being applied is
12
+ * worse than no suggestion: it resolves by whatever order the candidate set
13
+ * happened to be built in, which is a `Set`'s insertion order.
14
+ * - **The threshold scales but is CAPPED** (`min(3, len/3)`, and nothing under
15
+ * 1). Without the cap a long name accepts a distant match; without the floor
16
+ * a two-character name gets a "correction" sharing nothing with it.
17
+ *
18
+ * Both are `computeSuggestKind`'s rule, lifted here so the kind-suggestion, the
19
+ * template-body and the export checks cannot drift into three answers — which
20
+ * they had, two of them byte-identical copies differing from this one in both
21
+ * properties.
22
+ */
23
+ export function nearestName(
24
+ target: string,
25
+ candidates: Iterable<string>,
26
+ ): string | undefined {
27
+ if (!target) return undefined;
28
+ const threshold = Math.min(3, Math.floor(target.length / 3));
29
+ if (threshold < 1) return undefined;
30
+
31
+ let best: string | undefined;
32
+ let bestDist = threshold + 1;
33
+ let tied = false;
34
+
35
+ for (const candidate of candidates) {
36
+ const d = distance(target, candidate);
37
+ if (d < bestDist) {
38
+ best = candidate;
39
+ bestDist = d;
40
+ tied = false;
41
+ } else if (d === bestDist) {
42
+ tied = true;
43
+ }
44
+ }
45
+
46
+ return !best || bestDist > threshold || tied ? undefined : best;
47
+ }