@telorun/analyzer 0.61.0 → 0.62.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 (69) hide show
  1. package/dist/analyzer.d.ts.map +1 -1
  2. package/dist/analyzer.js +130 -9
  3. package/dist/builtins.d.ts.map +1 -1
  4. package/dist/builtins.js +69 -12
  5. package/dist/cel-bindings.d.ts +0 -6
  6. package/dist/cel-bindings.d.ts.map +1 -1
  7. package/dist/cel-bindings.js +3 -28
  8. package/dist/definition-registry.d.ts +17 -0
  9. package/dist/definition-registry.d.ts.map +1 -1
  10. package/dist/definition-registry.js +31 -2
  11. package/dist/identifier-name.d.ts +114 -0
  12. package/dist/identifier-name.d.ts.map +1 -0
  13. package/dist/identifier-name.js +183 -0
  14. package/dist/index.d.ts +10 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +8 -0
  17. package/dist/manifest-schemas.d.ts +81 -0
  18. package/dist/manifest-schemas.d.ts.map +1 -1
  19. package/dist/manifest-schemas.js +208 -6
  20. package/dist/requires-block.d.ts +125 -0
  21. package/dist/requires-block.d.ts.map +1 -0
  22. package/dist/requires-block.js +182 -0
  23. package/dist/schema-keywords.d.ts +68 -0
  24. package/dist/schema-keywords.d.ts.map +1 -0
  25. package/dist/schema-keywords.js +324 -0
  26. package/dist/schema-region.d.ts +12 -1
  27. package/dist/schema-region.d.ts.map +1 -1
  28. package/dist/schema-region.js +12 -1
  29. package/dist/telo-version.d.ts +3 -0
  30. package/dist/telo-version.d.ts.map +1 -0
  31. package/dist/telo-version.js +8 -0
  32. package/dist/types.d.ts +31 -0
  33. package/dist/types.d.ts.map +1 -1
  34. package/dist/validate-identifier-names.d.ts +31 -0
  35. package/dist/validate-identifier-names.d.ts.map +1 -0
  36. package/dist/validate-identifier-names.js +144 -0
  37. package/dist/validate-observed-state.d.ts +9 -2
  38. package/dist/validate-observed-state.d.ts.map +1 -1
  39. package/dist/validate-observed-state.js +9 -2
  40. package/dist/validate-references.d.ts.map +1 -1
  41. package/dist/validate-references.js +5 -26
  42. package/dist/validate-requires.d.ts +49 -0
  43. package/dist/validate-requires.d.ts.map +1 -0
  44. package/dist/validate-requires.js +99 -0
  45. package/dist/value-type-keyword.d.ts +1 -1
  46. package/dist/value-type-keyword.d.ts.map +1 -1
  47. package/dist/value-type-keyword.js +1 -0
  48. package/dist/version-range.d.ts +88 -0
  49. package/dist/version-range.d.ts.map +1 -0
  50. package/dist/version-range.js +173 -0
  51. package/package.json +2 -2
  52. package/src/analyzer.ts +146 -10
  53. package/src/builtins.ts +73 -12
  54. package/src/cel-bindings.ts +3 -28
  55. package/src/definition-registry.ts +30 -2
  56. package/src/identifier-name.ts +228 -0
  57. package/src/index.ts +34 -0
  58. package/src/manifest-schemas.ts +223 -4
  59. package/src/requires-block.ts +253 -0
  60. package/src/schema-keywords.ts +359 -0
  61. package/src/schema-region.ts +12 -1
  62. package/src/telo-version.ts +9 -0
  63. package/src/types.ts +32 -0
  64. package/src/validate-identifier-names.ts +173 -0
  65. package/src/validate-observed-state.ts +9 -2
  66. package/src/validate-references.ts +5 -26
  67. package/src/validate-requires.ts +129 -0
  68. package/src/value-type-keyword.ts +1 -0
  69. package/src/version-range.ts +238 -0
@@ -0,0 +1,324 @@
1
+ /**
2
+ * The JSON Schema vocabulary an author writes inside a manifest, as data.
3
+ *
4
+ * WHY THIS EXISTS. A schema-valued slot used to be declared `type: object` and
5
+ * nothing more, so every surface that reads a kind schema — completion, hover,
6
+ * the editor's field walk, AJV — knew only "some object". Autocompletion was
7
+ * dead from the first key, and a typo (`requred:`, `type: 5`) was carried all
8
+ * the way to a runtime validation failure that named the wrong thing.
9
+ *
10
+ * ONE SOURCE, TWO SURFACES. The maps below are the whole vocabulary; the
11
+ * fragments in `manifest-schemas.ts` are built FROM them, and completion reads
12
+ * them directly. A second hand-written list is exactly how the IDE's suggestions
13
+ * and the validator's rules would drift apart.
14
+ *
15
+ * WHY THE ANNOTATIONS ARE NOT IN THE VALIDATING FRAGMENT. `x-telo-*` keys are
16
+ * offered by completion but deliberately never appear as literal property names
17
+ * in the schema that gets hoisted into a manifest. Several passes walk a
18
+ * definition's schema testing every object for an annotation KEY
19
+ * (`resolveSchemaRefKinds`, `validate-ref-slots`, `validate-zone-slots`), and a
20
+ * `properties` map holding a key spelled `x-telo-ref` reads to them as an
21
+ * annotated node — inventing diagnostics about a slot the author never wrote.
22
+ * So the hoisted body stays open (`additionalProperties: true`, which admits
23
+ * every annotation) and the annotation vocabulary stays here, on the analyzer
24
+ * side of the boundary, where no manifest walk can reach it.
25
+ *
26
+ * Browser-safe: no Node built-ins.
27
+ */
28
+ /** A JSON type name, as `type:` accepts it — one, or a list (`[string, "null"]`,
29
+ * the spelling `CEL_NULLABLE_ACCESS` guards). */
30
+ const TYPE_NAME = {
31
+ type: "string",
32
+ enum: ["object", "array", "string", "number", "integer", "boolean", "null"],
33
+ };
34
+ /**
35
+ * The draft-07 keywords, as a property map for a fragment named `self`.
36
+ *
37
+ * Parameterized by the fragment name because a schema's nested positions hold
38
+ * schemas of the SAME flavour: a property of a kind schema may carry
39
+ * annotations, a property of a data schema may not. Both recurse into
40
+ * themselves, and the document-local pointer is what the hoisting in
41
+ * `expandManifestFragments` makes resolvable.
42
+ */
43
+ export function jsonSchemaKeywords(self) {
44
+ const schema = { $ref: `#/$defs/${self}` };
45
+ const schemaList = { type: "array", items: schema };
46
+ return {
47
+ // Shape
48
+ type: {
49
+ title: "Type",
50
+ description: "The JSON type this value must have, or a list of accepted types.",
51
+ anyOf: [TYPE_NAME, { type: "array", items: TYPE_NAME }],
52
+ },
53
+ properties: {
54
+ title: "Properties",
55
+ description: "Schema per named property of an object.",
56
+ type: "object",
57
+ additionalProperties: schema,
58
+ },
59
+ required: {
60
+ title: "Required",
61
+ description: "Property names that must be present.",
62
+ type: "array",
63
+ items: { type: "string" },
64
+ },
65
+ additionalProperties: {
66
+ title: "Additional properties",
67
+ description: "`false` rejects any property not named above — which is what makes a typo an error rather than an ignored field.",
68
+ anyOf: [{ type: "boolean" }, schema],
69
+ },
70
+ patternProperties: {
71
+ title: "Pattern properties",
72
+ description: "Schema per regular expression matching a property name.",
73
+ type: "object",
74
+ additionalProperties: schema,
75
+ },
76
+ propertyNames: {
77
+ title: "Property names",
78
+ description: "Schema every property NAME must satisfy.",
79
+ ...schema,
80
+ },
81
+ items: {
82
+ title: "Items",
83
+ description: "Schema for each element of an array.",
84
+ anyOf: [schema, schemaList],
85
+ },
86
+ additionalItems: {
87
+ title: "Additional items",
88
+ description: "Schema for elements past a positional `items` list.",
89
+ anyOf: [{ type: "boolean" }, schema],
90
+ },
91
+ contains: {
92
+ title: "Contains",
93
+ description: "At least one element must satisfy this schema.",
94
+ ...schema,
95
+ },
96
+ // Composition
97
+ allOf: { title: "All of", description: "Every branch must match.", ...schemaList },
98
+ anyOf: { title: "Any of", description: "At least one branch must match.", ...schemaList },
99
+ oneOf: {
100
+ title: "One of",
101
+ description: "Exactly one branch must match. Prefer `anyOf` when a branch declares a value type — a consumer that does not know the keyword reads the branch as matching everything, and only `anyOf` degrades gracefully.",
102
+ ...schemaList,
103
+ },
104
+ not: { title: "Not", description: "The value must NOT match this schema.", ...schema },
105
+ if: { title: "If", description: "Condition selecting `then` / `else`.", ...schema },
106
+ then: { title: "Then", description: "Applied when `if` matches.", ...schema },
107
+ else: { title: "Else", description: "Applied when `if` does not match.", ...schema },
108
+ // Values
109
+ enum: { title: "Enum", description: "The complete set of accepted values.", type: "array" },
110
+ const: { title: "Const", description: "The single accepted value." },
111
+ default: {
112
+ title: "Default",
113
+ description: "Filled in when the value is absent. On an invocation contract this is applied at dispatch, so a caller may omit the field.",
114
+ },
115
+ examples: { title: "Examples", description: "Sample values, for documentation.", type: "array" },
116
+ // Numbers
117
+ minimum: { title: "Minimum", type: "number" },
118
+ maximum: { title: "Maximum", type: "number" },
119
+ exclusiveMinimum: { title: "Exclusive minimum", type: "number" },
120
+ exclusiveMaximum: { title: "Exclusive maximum", type: "number" },
121
+ multipleOf: { title: "Multiple of", type: "number", exclusiveMinimum: 0 },
122
+ // Strings
123
+ minLength: { title: "Min length", type: "integer", minimum: 0 },
124
+ maxLength: { title: "Max length", type: "integer", minimum: 0 },
125
+ pattern: { title: "Pattern", description: "Regular expression the string must match.", type: "string" },
126
+ format: {
127
+ title: "Format",
128
+ description: "Named string format (`date-time`, `uri`, `email`, …).",
129
+ type: "string",
130
+ },
131
+ contentMediaType: {
132
+ title: "Content media type",
133
+ description: "Media type of the string's content (`application/javascript`). Drives the editor's code-widget language.",
134
+ type: "string",
135
+ },
136
+ contentEncoding: { title: "Content encoding", type: "string" },
137
+ // Arrays
138
+ minItems: { title: "Min items", type: "integer", minimum: 0 },
139
+ maxItems: { title: "Max items", type: "integer", minimum: 0 },
140
+ uniqueItems: { title: "Unique items", type: "boolean" },
141
+ // Objects
142
+ minProperties: { title: "Min properties", type: "integer", minimum: 0 },
143
+ maxProperties: { title: "Max properties", type: "integer", minimum: 0 },
144
+ dependencies: {
145
+ title: "Dependencies",
146
+ description: "Per property, the other properties it requires — or a schema to apply.",
147
+ type: "object",
148
+ additionalProperties: { anyOf: [{ type: "array", items: { type: "string" } }, schema] },
149
+ },
150
+ // Documentation and structure
151
+ title: { title: "Title", description: "Human-readable label for this value.", type: "string" },
152
+ description: {
153
+ title: "Description",
154
+ description: "What this value is, for the author reading the manifest.",
155
+ type: "string",
156
+ },
157
+ deprecated: { title: "Deprecated", type: "boolean" },
158
+ readOnly: { title: "Read only", type: "boolean" },
159
+ writeOnly: { title: "Write only", type: "boolean" },
160
+ $ref: {
161
+ title: "Reference",
162
+ description: "Pointer to another schema: `#/$defs/<Name>` for one declared below, `telo:<module>/<Type>` for a named type a module declared.",
163
+ type: "string",
164
+ },
165
+ $defs: {
166
+ title: "Definitions",
167
+ description: "Named sub-schemas, private to this schema, referenced with `#/$defs/<Name>`.",
168
+ type: "object",
169
+ additionalProperties: schema,
170
+ },
171
+ definitions: {
172
+ title: "Definitions (draft-07)",
173
+ description: "Older spelling of `$defs`.",
174
+ type: "object",
175
+ additionalProperties: schema,
176
+ },
177
+ $comment: { title: "Comment", type: "string" },
178
+ $id: { title: "Id", type: "string" },
179
+ $schema: { title: "Schema dialect", type: "string" },
180
+ };
181
+ }
182
+ /**
183
+ * The `x-telo-*` annotation vocabulary — completion and hover only.
184
+ *
185
+ * TOTAL over {@link ANNOTATION_KEYWORDS} plus `x-telo-type`, which is what makes
186
+ * it a description of the existing list rather than a second copy of it: adding
187
+ * an annotation without a completion entry is a compile error here, and the
188
+ * first draft of this map — hand-written — had already silently dropped four
189
+ * annotations that live stdlib manifests use.
190
+ *
191
+ * Values are intentionally loose. What an annotation MEANS is checked by the
192
+ * pass that owns it (`validate-ref-slots`, `validate-zone-slots`,
193
+ * `validate-value-type-slots`), which reports in that annotation's own
194
+ * vocabulary; restating the shape here would give one mistake two diagnostics
195
+ * that disagree about what is wrong.
196
+ *
197
+ * A KNOWN BLIND SPOT, and not an oversight: several annotations
198
+ * (`x-telo-context`, `x-telo-error-context`, `x-telo-step-context`) hold JSON
199
+ * Schema themselves, and cannot be typed by the fragment this file feeds. A
200
+ * literal `x-telo-*` key inside a hoisted `properties` map reads to the
201
+ * annotation walkers as an annotated node, so the vocabulary has to stay out of
202
+ * anything that enters a manifest — which leaves an annotation's VALUE with
203
+ * neither validation nor completion. Closing it needs the walkers to distinguish
204
+ * a schema describing an annotation from an annotation, which nothing does yet.
205
+ */
206
+ export const TELO_SCHEMA_ANNOTATIONS = {
207
+ "x-telo-eval": {
208
+ title: "Evaluation mode",
209
+ description: "When `${{ }}` / `!cel` in this field is evaluated: `compile` at load, `runtime` per invocation. A CEL-bearing field MUST declare one, or the expression is read as a literal.",
210
+ type: "string",
211
+ enum: ["compile", "runtime"],
212
+ },
213
+ "x-telo-ref": {
214
+ title: "Reference slot",
215
+ description: "This field holds a `!ref` to a resource — plus what the declaring resource DOES with it (`use`), which is what every topology analysis reads.",
216
+ anyOf: [{ type: "string" }, { type: "object" }],
217
+ },
218
+ "x-telo-type": {
219
+ title: "Value type",
220
+ description: "What the value IS beyond JSON's types: `Telo.Bytes`, `Telo.Stream`, `Telo.TcpPort`, … Optionally parameterized (`{ name: Telo.Stream, of: Telo.Bytes }`).",
221
+ anyOf: [{ type: "string" }, { type: "object" }],
222
+ },
223
+ "x-telo-scope": {
224
+ title: "Execution scope",
225
+ description: "JSON Pointer to a region whose `!ref`s resolve against this field's inline resources, created on entry and torn down on exit.",
226
+ anyOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
227
+ },
228
+ "x-telo-context": {
229
+ title: "CEL context",
230
+ description: "The CEL variables in scope inside this field, as a JSON Schema. Analyzer-only.",
231
+ type: "object",
232
+ },
233
+ "x-telo-error-context": {
234
+ title: "Error context",
235
+ description: "Schema of the `error` CEL variable inside this field, at any nesting depth.",
236
+ type: "object",
237
+ },
238
+ "x-telo-step-context": {
239
+ title: "Step context",
240
+ description: "On a step array: how to build typed `steps.<name>.result` from each item's invoked resource.",
241
+ type: "object",
242
+ },
243
+ "x-telo-schema-from": {
244
+ title: "Schema from",
245
+ description: "Derive this field's validation schema from a sibling ref's definition schema.",
246
+ type: "string",
247
+ },
248
+ "x-telo-value-schema-from": {
249
+ title: "Value schema from",
250
+ description: "The value here must satisfy the type declared at the named field — checked for EVERY such slot, not only the branch a given input selects.",
251
+ type: "string",
252
+ },
253
+ "x-telo-bindings-from": {
254
+ title: "Bindings from",
255
+ description: "Merge the names declared in the named field's map into this field's CEL scope.",
256
+ type: "string",
257
+ },
258
+ "x-telo-context-from": {
259
+ title: "Context from",
260
+ description: "Merge the navigated value as a property map into this context node.",
261
+ type: "string",
262
+ },
263
+ "x-telo-context-from-root": {
264
+ title: "Context from root",
265
+ description: "Replace this context node's schema with the value navigated from the manifest root.",
266
+ type: "string",
267
+ },
268
+ "x-telo-context-from-ref-kind": {
269
+ title: "Context from ref kind",
270
+ description: "Type this node from a referenced kind's declared `inputType` / `outputType`.",
271
+ type: "string",
272
+ },
273
+ "x-telo-context-ref-from": {
274
+ title: "Context ref from",
275
+ description: "Type this node from the named manifest's field, falling back to its kind's.",
276
+ type: "string",
277
+ },
278
+ "x-telo-context-element-from": {
279
+ title: "Context element from",
280
+ description: "Type this binding from the ELEMENT of a sibling collection expression.",
281
+ type: "string",
282
+ },
283
+ "x-telo-context-collection-from": {
284
+ title: "Context collection from",
285
+ description: "Type this binding from a sibling collection expression itself. Withheld when the collection is live — re-exposing a cursor being drained is what no member-access rule catches.",
286
+ type: "string",
287
+ },
288
+ "x-telo-provides-zone": {
289
+ title: "Provides zone",
290
+ description: "This resource opens an execution zone; the annotated value is the CORRELATION KEY, never the zone (a zone is identified by the kind that provides it).",
291
+ anyOf: [{ type: "boolean" }, { type: "string" }],
292
+ },
293
+ "x-telo-requires-zone": {
294
+ title: "Requires zone",
295
+ description: "This resource must be reached through the named providing kind's body, optionally correlated through an ordered pointer list.",
296
+ anyOf: [{ type: "string" }, { type: "object" }],
297
+ },
298
+ "x-telo-widget": {
299
+ title: "Editor widget",
300
+ description: "Render this field with a richer control — `code` gives a Monaco editor.",
301
+ type: "string",
302
+ enum: ["code"],
303
+ },
304
+ "x-telo-topology-role": {
305
+ title: "Topology role",
306
+ description: "Names this field's part in the kind's topology, for the editor's graph view.",
307
+ type: "string",
308
+ },
309
+ "x-telo-inline": {
310
+ title: "Inline",
311
+ description: "Renders this field's value inline in the editor rather than behind an accordion.",
312
+ type: "boolean",
313
+ },
314
+ "x-telo-outcome-list": {
315
+ title: "Outcome list",
316
+ description: "Marks an array of conditional response-rendering entries (`returns:`).",
317
+ anyOf: [{ type: "boolean" }, { type: "string" }],
318
+ },
319
+ "x-telo-catches-for": {
320
+ title: "Catches for",
321
+ description: "Names the field whose failures this branch list handles.",
322
+ type: "string",
323
+ },
324
+ };
@@ -19,7 +19,18 @@
19
19
  *
20
20
  * Browser-safe: no Node built-ins.
21
21
  */
22
- /** The kernel's schema-valued manifest keys. */
22
+ /**
23
+ * The kernel's schema-valued manifest keys.
24
+ *
25
+ * TWO CONTAINMENT RULES READ THIS SET, and they are not the same rule. This
26
+ * file's own {@link isInSchemaRegion} asks "is this node inside a schema" and
27
+ * answers by ANCESTRY, for the reason argued above. `expandManifestFragments`
28
+ * asks a different question — "which node will a validator compile, so a
29
+ * `#/$defs/…` pointer written below it resolves" — and answers by the TOP-LEVEL
30
+ * key alone, because that is the object handed to AJV; a nested occurrence is a
31
+ * slot describing a field named `schema`, not a compile root. Neither rule is a
32
+ * weaker version of the other, so do not "fix" one to match.
33
+ */
23
34
  export declare const SCHEMA_REGION_KEYS: readonly string[];
24
35
  /**
25
36
  * True when `path` reaches into a schema region — some ANCESTOR segment is a
@@ -1 +1 @@
1
- {"version":3,"file":"schema-region.d.ts","sourceRoot":"","sources":["../src/schema-region.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,gDAAgD;AAChD,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAM/C,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,OAAO,CAM5E"}
1
+ {"version":3,"file":"schema-region.d.ts","sourceRoot":"","sources":["../src/schema-region.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAM/C,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,OAAO,CAM5E"}
@@ -19,7 +19,18 @@
19
19
  *
20
20
  * Browser-safe: no Node built-ins.
21
21
  */
22
- /** The kernel's schema-valued manifest keys. */
22
+ /**
23
+ * The kernel's schema-valued manifest keys.
24
+ *
25
+ * TWO CONTAINMENT RULES READ THIS SET, and they are not the same rule. This
26
+ * file's own {@link isInSchemaRegion} asks "is this node inside a schema" and
27
+ * answers by ANCESTRY, for the reason argued above. `expandManifestFragments`
28
+ * asks a different question — "which node will a validator compile, so a
29
+ * `#/$defs/…` pointer written below it resolves" — and answers by the TOP-LEVEL
30
+ * key alone, because that is the object handed to AJV; a nested occurrence is a
31
+ * slot describing a field named `schema`, not a compile root. Neither rule is a
32
+ * weaker version of the other, so do not "fix" one to match.
33
+ */
23
34
  export const SCHEMA_REGION_KEYS = [
24
35
  "schema",
25
36
  "status",
@@ -0,0 +1,3 @@
1
+ /** The surface generation this analyzer implements. */
2
+ export declare const TELO_SURFACE_VERSION = "0.77.0";
3
+ //# sourceMappingURL=telo-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telo-version.d.ts","sourceRoot":"","sources":["../src/telo-version.ts"],"names":[],"mappings":"AAOA,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,WAAW,CAAC"}
@@ -0,0 +1,8 @@
1
+ // GENERATED by scripts/generate-telo-version.mjs — do not edit.
2
+ //
3
+ // The manifest SURFACE GENERATION this build implements, read from the linked
4
+ // cli/kernel/sdk version. Distinct from this package's own npm version: that is
5
+ // its release identity, this is the scale a module's `requires.telo` range is
6
+ // written against, and every kernel in every language reports the same scale.
7
+ /** The surface generation this analyzer implements. */
8
+ export const TELO_SURFACE_VERSION = "0.77.0";
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { HostVersions } from "./requires-block.js";
1
2
  import type { ZoneModuleDocuments } from "./zone-module-documents.js";
2
3
  /** Matches LSP DiagnosticSeverity values exactly.
3
4
  * https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnosticSeverity */
@@ -145,6 +146,36 @@ export interface AnalysisOptions {
145
146
  * (on-disk stamp) attests that the manifests passed a real analyze
146
147
  * pass at the same analyzer / kernel version. */
147
148
  skipValidation?: boolean;
149
+ /** The manifest SURFACE GENERATION the runtime performing this analysis
150
+ * implements, against which every module's `requires.telo` range is checked.
151
+ *
152
+ * Defaults to this build's own (`TELO_SURFACE_VERSION`), which is the right
153
+ * answer for the kernel, the CLI and the editor alike. In a browser no kernel
154
+ * is running, so the only well-posed question is whether the runtime *doing
155
+ * the analysis* can read the module — and that is exactly the case where the
156
+ * diagnostic is needed, since an editor too old to parse a construct
157
+ * otherwise produces vocabulary errors it cannot explain.
158
+ *
159
+ * The kernel deliberately does NOT override with its own package version.
160
+ * The constant is generated from the linked kernel version, so the two are
161
+ * the same number by construction; where they can drift — a kernel resolving
162
+ * an older analyzer than it was released with — the analyzer is the half that
163
+ * PARSES, and claiming a generation higher than the bundled parser implements
164
+ * would be a claim the stack cannot honour.
165
+ *
166
+ * So this exists for checking against a DIFFERENT target than oneself: a CI
167
+ * matrix, or an editor setting naming the version a team deploys on.
168
+ *
169
+ * Defaulted rather than optional-and-silent on purpose: there is no "caller
170
+ * forgot to pass it, check silently skipped" path. */
171
+ teloVersion?: string;
172
+ /** Versions of the HOST the analysis is being performed for, against which a
173
+ * module's `requires.host.*` ranges are checked.
174
+ *
175
+ * Absent in a browser, where there is no host to speak for, and an axis with
176
+ * no supplied version is skipped rather than guessed. The kernel and the CLI
177
+ * supply it because they are the host. */
178
+ hostVersions?: HostVersions;
148
179
  }
149
180
  /** Pre-seeded state for incremental analysis. Passed to StaticAnalyzer.analyze() so it does
150
181
  * not rebuild from scratch on every call. The provided instances are mutated — new definitions
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { ResourceManifest } from "@telorun/sdk";
2
+ import type { AliasResolver } from "./alias-resolver.js";
3
+ import type { CallGraph } from "./call-graph.js";
4
+ import type { DefinitionRegistry } from "./definition-registry.js";
5
+ import { type AnalysisDiagnostic } from "./types.js";
6
+ /**
7
+ * Every author-written name in one pass — resource instances, kinds, modules,
8
+ * import aliases, step names and `variables` / `secrets` / `ports` keys. The
9
+ * rules and their rationale are in `identifier-name.ts`; this file only decides
10
+ * what each name IS and where to report it.
11
+ *
12
+ * One pass rather than a check bolted onto each surface's own validator: the
13
+ * rule is identical everywhere, and seven copies of it would drift the way the
14
+ * dot rule already had (enforced for resources, unenforced for steps, aliases
15
+ * and config declarations, which reach CEL through the same identifier space).
16
+ *
17
+ * **Step names come from the call graph, never from a walk of our own.** The
18
+ * graph already owns the analyzer's only step-array recursion and carries each
19
+ * step's name, owner and concrete path, which is exactly what a diagnostic
20
+ * needs. Re-walking would be a second answer to "what steps exist".
21
+ *
22
+ * **Scoped to the entry's own modules, at every tier including the errors.** A
23
+ * published dependency's naming is not the consumer's to fix, and reporting an
24
+ * error they cannot act on is worse than not reporting it — the standing
25
+ * precedent of `X_TELO_REF_UNRESOLVED` and `validate-extends`. The library's
26
+ * own author sees all of it when their module is analyzed as a root.
27
+ *
28
+ * Browser-safe.
29
+ */
30
+ export declare function validateIdentifierNames(manifests: ResourceManifest[], registry: DefinitionRegistry, aliases: AliasResolver, rootModules: Set<string>, graph: CallGraph): AnalysisDiagnostic[];
31
+ //# sourceMappingURL=validate-identifier-names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-identifier-names.d.ts","sourceRoot":"","sources":["../src/validate-identifier-names.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,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIrD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,aAAa,EACtB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,KAAK,EAAE,SAAS,GACf,kBAAkB,EAAE,CAgEtB"}
@@ -0,0 +1,144 @@
1
+ import { TYPE_LEVEL_DOC_KINDS, checkName, } from "./identifier-name.js";
2
+ const SOURCE = "telo-analyzer";
3
+ /**
4
+ * Every author-written name in one pass — resource instances, kinds, modules,
5
+ * import aliases, step names and `variables` / `secrets` / `ports` keys. The
6
+ * rules and their rationale are in `identifier-name.ts`; this file only decides
7
+ * what each name IS and where to report it.
8
+ *
9
+ * One pass rather than a check bolted onto each surface's own validator: the
10
+ * rule is identical everywhere, and seven copies of it would drift the way the
11
+ * dot rule already had (enforced for resources, unenforced for steps, aliases
12
+ * and config declarations, which reach CEL through the same identifier space).
13
+ *
14
+ * **Step names come from the call graph, never from a walk of our own.** The
15
+ * graph already owns the analyzer's only step-array recursion and carries each
16
+ * step's name, owner and concrete path, which is exactly what a diagnostic
17
+ * needs. Re-walking would be a second answer to "what steps exist".
18
+ *
19
+ * **Scoped to the entry's own modules, at every tier including the errors.** A
20
+ * published dependency's naming is not the consumer's to fix, and reporting an
21
+ * error they cannot act on is worse than not reporting it — the standing
22
+ * precedent of `X_TELO_REF_UNRESOLVED` and `validate-extends`. The library's
23
+ * own author sees all of it when their module is analyzed as a root.
24
+ *
25
+ * Browser-safe.
26
+ */
27
+ export function validateIdentifierNames(manifests, registry, aliases, rootModules, graph) {
28
+ const out = [];
29
+ for (const manifest of manifests) {
30
+ const metadata = manifest.metadata;
31
+ const name = typeof metadata?.name === "string" ? metadata.name : undefined;
32
+ if (!manifest.kind || !name)
33
+ continue;
34
+ // A name synthesized by inline extraction (`TestAdd_steps_0_invoke`) is
35
+ // derived from the author's own names, not written — reporting its shape
36
+ // would blame them for a spelling this pass's own pipeline chose.
37
+ if (metadata?.xTeloOrigin)
38
+ continue;
39
+ const ownModule = metadata?.module;
40
+ if (ownModule && !rootModules.has(ownModule))
41
+ continue;
42
+ const level = levelFor(manifest, registry, aliases);
43
+ push(out, checkName(name, level, surfaceFor(manifest.kind)), {
44
+ kind: manifest.kind,
45
+ name,
46
+ filePath: metadata?.source,
47
+ path: "metadata.name",
48
+ });
49
+ // The module doc's config contract. Each key becomes `variables.<key>` /
50
+ // `secrets.<key>` / `ports.<key>` in CEL, so it lives in the same
51
+ // identifier space as a resource name and breaks the same way. An
52
+ // import's `variables:` / `secrets:` are deliberately NOT checked — those
53
+ // keys are the imported library's declarations, so a violation there is
54
+ // the library author's, reported when their module is analyzed as a root.
55
+ if (manifest.kind === "Telo.Application" || manifest.kind === "Telo.Library") {
56
+ for (const field of ["variables", "secrets", "ports"]) {
57
+ const block = manifest[field];
58
+ if (!block || typeof block !== "object" || Array.isArray(block))
59
+ continue;
60
+ for (const key of Object.keys(block)) {
61
+ push(out, checkName(key, "value", `${singular(field)} name`), {
62
+ kind: manifest.kind,
63
+ name,
64
+ filePath: metadata?.source,
65
+ path: `${field}.${key}`,
66
+ });
67
+ }
68
+ }
69
+ }
70
+ }
71
+ for (const node of graph.nodes.values()) {
72
+ if (node.type !== "step" || !node.name)
73
+ continue;
74
+ const owner = graph.nodes.get(node.owner);
75
+ if (owner?.type !== "resource")
76
+ continue;
77
+ const ownerMeta = owner.manifest.metadata;
78
+ if (ownerMeta?.xTeloOrigin)
79
+ continue;
80
+ const ownModule = ownerMeta?.module;
81
+ if (ownModule && !rootModules.has(ownModule))
82
+ continue;
83
+ push(out, checkName(node.name, "value", "step name"), {
84
+ kind: owner.kind,
85
+ name: owner.name,
86
+ filePath: ownerMeta?.source,
87
+ path: `${node.path}.name`,
88
+ });
89
+ }
90
+ return out;
91
+ }
92
+ /**
93
+ * A resource instance is value-level, EXCEPT when its capability is
94
+ * `Telo.Type`: a named shape has no runtime instance, is referenced from
95
+ * `inputType:` / `outputType:` type slots and resolves as
96
+ * `telo:<module>/<Name>`, so its name denotes a type despite being declared as
97
+ * a resource. Capability-driven rather than by kind name, so no resource kind
98
+ * is hardcoded here.
99
+ *
100
+ * An unresolvable kind falls back to value level — the honest default, since
101
+ * `UNDEFINED_KIND` already reports the real problem and guessing type level
102
+ * would stack a case error on top of it.
103
+ */
104
+ function levelFor(manifest, registry, aliases) {
105
+ if (TYPE_LEVEL_DOC_KINDS.has(manifest.kind))
106
+ return "type";
107
+ // The root resolver is the right one unconditionally: every manifest
108
+ // reaching here belongs to a root module, the others having been skipped.
109
+ const canonical = aliases.resolveKind(manifest.kind) ?? manifest.kind;
110
+ return registry.resolve(canonical)?.capability === "Telo.Type" ? "type" : "value";
111
+ }
112
+ /** The noun phrase a diagnostic uses as its subject. */
113
+ function surfaceFor(kind) {
114
+ switch (kind) {
115
+ case "Telo.Application":
116
+ case "Telo.Library":
117
+ return "module name";
118
+ case "Telo.Definition":
119
+ case "Telo.Abstract":
120
+ return "kind name";
121
+ case "Telo.Import":
122
+ return "import alias";
123
+ default:
124
+ return "resource name";
125
+ }
126
+ }
127
+ function singular(field) {
128
+ return field === "variables" ? "variable" : field === "secrets" ? "secret" : "port";
129
+ }
130
+ function push(out, violation, at) {
131
+ if (!violation)
132
+ return;
133
+ out.push({
134
+ severity: violation.severity,
135
+ code: violation.code,
136
+ source: SOURCE,
137
+ message: `${at.kind}/${at.name}: ${violation.message}`,
138
+ data: {
139
+ resource: { kind: at.kind, name: at.name },
140
+ filePath: at.filePath,
141
+ path: at.path,
142
+ },
143
+ });
144
+ }
@@ -7,8 +7,15 @@ import type { CallGraph } from "./call-graph.js";
7
7
  * {@link validateObservedStateDeclarations} rather than here, so the author gets
8
8
  * a message naming the rule and the fix instead of AJV's "must NOT be valid".
9
9
  *
10
- * Exported from the analyzer and re-used by the kernel's manifest schemas, so
11
- * the rule has one definition rather than two kept in sync by hand.
10
+ * THE KERNEL'S SHAPE CHECK, not the analyzer's. The analyzer's builtins point
11
+ * their `status:` slot at the `JsonSchema7` fragment, which describes the same
12
+ * block far more precisely; the kernel keeps this permissive one, and the two do
13
+ * not drift into disagreement because the fragment only ever NARROWS what this
14
+ * accepts. That split is the same one the `required:` rule above draws: the
15
+ * loader answers "is this the right shape at all", and the check that can name
16
+ * the offending keyword and its line stays with `telo check`. Wiring the fragment
17
+ * into the kernel too would turn a check-time diagnostic into a boot failure for
18
+ * every already-published manifest carrying a sloppy keyword.
12
19
  */
13
20
  export declare const OBSERVED_STATE_SCHEMA: {
14
21
  type: string;