@telorun/analyzer 0.61.0 → 0.62.1

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 (73) 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/release/payload-digest.d.ts +7 -3
  21. package/dist/release/payload-digest.d.ts.map +1 -1
  22. package/dist/release/payload-digest.js +7 -3
  23. package/dist/requires-block.d.ts +125 -0
  24. package/dist/requires-block.d.ts.map +1 -0
  25. package/dist/requires-block.js +182 -0
  26. package/dist/schema-keywords.d.ts +68 -0
  27. package/dist/schema-keywords.d.ts.map +1 -0
  28. package/dist/schema-keywords.js +324 -0
  29. package/dist/schema-region.d.ts +12 -1
  30. package/dist/schema-region.d.ts.map +1 -1
  31. package/dist/schema-region.js +12 -1
  32. package/dist/telo-version.d.ts +3 -0
  33. package/dist/telo-version.d.ts.map +1 -0
  34. package/dist/telo-version.js +8 -0
  35. package/dist/types.d.ts +31 -0
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/validate-identifier-names.d.ts +31 -0
  38. package/dist/validate-identifier-names.d.ts.map +1 -0
  39. package/dist/validate-identifier-names.js +144 -0
  40. package/dist/validate-observed-state.d.ts +9 -2
  41. package/dist/validate-observed-state.d.ts.map +1 -1
  42. package/dist/validate-observed-state.js +9 -2
  43. package/dist/validate-references.d.ts.map +1 -1
  44. package/dist/validate-references.js +5 -26
  45. package/dist/validate-requires.d.ts +49 -0
  46. package/dist/validate-requires.d.ts.map +1 -0
  47. package/dist/validate-requires.js +99 -0
  48. package/dist/value-type-keyword.d.ts +1 -1
  49. package/dist/value-type-keyword.d.ts.map +1 -1
  50. package/dist/value-type-keyword.js +1 -0
  51. package/dist/version-range.d.ts +88 -0
  52. package/dist/version-range.d.ts.map +1 -0
  53. package/dist/version-range.js +173 -0
  54. package/package.json +2 -2
  55. package/src/analyzer.ts +146 -10
  56. package/src/builtins.ts +73 -12
  57. package/src/cel-bindings.ts +3 -28
  58. package/src/definition-registry.ts +30 -2
  59. package/src/identifier-name.ts +228 -0
  60. package/src/index.ts +34 -0
  61. package/src/manifest-schemas.ts +223 -4
  62. package/src/release/payload-digest.ts +7 -3
  63. package/src/requires-block.ts +253 -0
  64. package/src/schema-keywords.ts +359 -0
  65. package/src/schema-region.ts +12 -1
  66. package/src/telo-version.ts +9 -0
  67. package/src/types.ts +32 -0
  68. package/src/validate-identifier-names.ts +173 -0
  69. package/src/validate-observed-state.ts +9 -2
  70. package/src/validate-references.ts +5 -26
  71. package/src/validate-requires.ts +129 -0
  72. package/src/value-type-keyword.ts +1 -0
  73. package/src/version-range.ts +238 -0
@@ -1 +1 @@
1
- {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"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"}
package/dist/analyzer.js CHANGED
@@ -29,12 +29,15 @@ import { collectValueSchemaIssues } from "./validate-value-schema.js";
29
29
  import { DiagnosticSeverity } from "./types.js";
30
30
  import { extractAccessChains, extractCelRegionScopes, getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
31
31
  import { buildEvalPaths, evalPathsCover } from "./eval-paths.js";
32
- import { BINDINGS_ANNOTATION, bindingContextProperties, bindingPathChain, CEL_RESERVED_WORDS, findBindingSites, resolveBindingOrder, schemaAtChain, } from "./cel-bindings.js";
32
+ import { BINDINGS_ANNOTATION, bindingContextProperties, bindingPathChain, findBindingSites, resolveBindingOrder, schemaAtChain, } from "./cel-bindings.js";
33
+ import { CEL_RESERVED_WORDS, checkName } from "./identifier-name.js";
34
+ import { validateIdentifierNames } from "./validate-identifier-names.js";
33
35
  import { validateExtends } from "./validate-extends.js";
34
36
  import { validateLogging } from "./validate-logging.js";
35
37
  import { validateModuleArtifact } from "./validate-module-artifact.js";
36
38
  import { validateIncludePlacement } from "./validate-include-placement.js";
37
39
  import { validateModuleMetadata } from "./validate-module-metadata.js";
40
+ import { validateRequires } from "./validate-requires.js";
38
41
  import { validateBaseMapping } from "./validate-base-mapping.js";
39
42
  import { validateInvocationContract } from "./validate-invocation-contract.js";
40
43
  import { collectStepInputIssues } from "./validate-step-inputs.js";
@@ -819,6 +822,85 @@ function collectCelValueSlots(data, schema, path) {
819
822
  }
820
823
  return slots;
821
824
  }
825
+ /**
826
+ * Files belonging to a module this runtime declared itself unable to read.
827
+ *
828
+ * Attribution is by FILE rather than by resource identity, the same choice
829
+ * `remapMigratedPaths` makes and for the same reason: a diagnostic carries at
830
+ * most two routing facts and routinely only one, so indexing by resource would
831
+ * leave every diagnostic without `data.resource` unreachable.
832
+ *
833
+ * **A module NAME is not a graph-unique key.** Names are module-scoped, so two
834
+ * libraries may both be called `Store` — CLAUDE.md names this exact hazard for
835
+ * migration provenance ("two libraries declaring a Store would share one
836
+ * bucket"), where the answer is to narrow or not remap at all. Same answer here:
837
+ * the gated doc's own `metadata.source` is always suppressed, and a name is used
838
+ * to reach its `include:` partials only when that name identifies exactly ONE
839
+ * module doc in the set. Where it does not, the partials keep their diagnostics
840
+ * rather than risk silencing an unrelated library's — a stray extra diagnostic is
841
+ * a far cheaper failure than a hidden one.
842
+ *
843
+ * A module doc with no `source` contributes nothing — suppressing on a guess
844
+ * would hide diagnostics belonging to files nobody named.
845
+ */
846
+ function filesOfUnreadableModules(manifests, requiresDiagnostics) {
847
+ const files = new Set();
848
+ const gatedNames = new Set();
849
+ for (const d of requiresDiagnostics) {
850
+ if (d.code !== "MODULE_REQUIRES_NEWER_RUNTIME")
851
+ continue;
852
+ const data = d.data;
853
+ // The gated document itself, addressed by the file it was declared in.
854
+ if (typeof data?.filePath === "string" && data.filePath)
855
+ files.add(data.filePath);
856
+ const name = data?.resource?.name;
857
+ if (typeof name === "string")
858
+ gatedNames.add(name);
859
+ }
860
+ if (files.size === 0 && gatedNames.size === 0)
861
+ return files;
862
+ // How many module docs answer to each gated name — the ambiguity test.
863
+ const docsPerName = new Map();
864
+ for (const m of manifests) {
865
+ if (m.kind !== "Telo.Application" && m.kind !== "Telo.Library")
866
+ continue;
867
+ const name = m.metadata?.name;
868
+ if (typeof name === "string" && gatedNames.has(name)) {
869
+ docsPerName.set(name, (docsPerName.get(name) ?? 0) + 1);
870
+ }
871
+ }
872
+ for (const m of manifests) {
873
+ const metadata = (m.metadata ?? {});
874
+ const owner = metadata.module;
875
+ if (typeof owner !== "string" || !gatedNames.has(owner))
876
+ continue;
877
+ if (docsPerName.get(owner) !== 1)
878
+ continue; // ambiguous — do not guess
879
+ if (typeof metadata.source === "string" && metadata.source)
880
+ files.add(metadata.source);
881
+ }
882
+ return files;
883
+ }
884
+ /**
885
+ * Drop every diagnostic anchored in a file whose module this runtime cannot
886
+ * read, except the gate diagnostic itself.
887
+ *
888
+ * A filter rather than a guard on each validator: threading "skip this module"
889
+ * through thirty validators would make each one responsible for a rule none of
890
+ * them owns, and a validator added later would silently opt out of it. A
891
+ * diagnostic with no `filePath` is KEPT — suppression must never be the default
892
+ * for something it cannot attribute.
893
+ */
894
+ function suppressUnreadableModuleDiagnostics(diagnostics, unreadableFiles) {
895
+ if (unreadableFiles.size === 0)
896
+ return diagnostics;
897
+ return diagnostics.filter((d) => {
898
+ if (d.code === "MODULE_REQUIRES_NEWER_RUNTIME")
899
+ return true;
900
+ const filePath = d.data?.filePath;
901
+ return typeof filePath !== "string" || !unreadableFiles.has(filePath);
902
+ });
903
+ }
822
904
  export class StaticAnalyzer {
823
905
  celEnv;
824
906
  constructor(options = {}) {
@@ -1278,6 +1360,21 @@ export class StaticAnalyzer {
1278
1360
  });
1279
1361
  }
1280
1362
  }
1363
+ // Declared runtime requirements, FIRST among the validators and suppressing
1364
+ // the rest for any module this runtime cannot read. A module that adopted
1365
+ // newer syntax also produces the vocabulary errors that syntax causes here —
1366
+ // an unknown `use` token, an object where a zone annotation expects a
1367
+ // pointer, an `additionalProperties` violation against a kernel-owned
1368
+ // schema — every one of which is true and blames the module's author for a
1369
+ // version skew. Reporting them beside the gate would bury the one message
1370
+ // that names the actual cause and the actual fix.
1371
+ const requiresDiagnostics = validateRequires(allManifests, {
1372
+ teloVersion: options?.teloVersion,
1373
+ hostVersions: options?.hostVersions,
1374
+ entryModules: rootModules,
1375
+ });
1376
+ const unreadableFiles = filesOfUnreadableModules(allManifests, requiresDiagnostics);
1377
+ diagnostics.push(...requiresDiagnostics);
1281
1378
  if (!options?.skipValidation) {
1282
1379
  diagnostics.push(...validateSchemaTypeRefs(allManifests, defs, aliases, aliasesByModule, rootModules));
1283
1380
  // §14.1 / §10.3: redaction paths and `on_full: block` are statically
@@ -1292,6 +1389,11 @@ export class StaticAnalyzer {
1292
1389
  // these fields, which is precisely why they need a check: a mistyped one
1293
1390
  // has no runtime failure mode that would ever surface it.
1294
1391
  diagnostics.push(...validateModuleMetadata(allManifests, defs, aliases));
1392
+ // Every author-written name. Telo has no lexer, so a name's shape is
1393
+ // unchecked where it is declared and its consequences land at whichever
1394
+ // CEL site reads it — for a hyphen, sometimes as silent arithmetic. Takes
1395
+ // the call graph for step names rather than re-walking the step arrays.
1396
+ diagnostics.push(...validateIdentifierNames(allManifests, defs, aliases, rootModules, getCallGraph()));
1295
1397
  // A file embed resolves at resource creation, so one written on a doc that
1296
1398
  // is never instantiated is read by nothing and would ship silently.
1297
1399
  diagnostics.push(...validateIncludePlacement(allManifests));
@@ -1304,7 +1406,7 @@ export class StaticAnalyzer {
1304
1406
  // normalisation have already run above; that's all downstream
1305
1407
  // consumers (prepare, init loop) require.
1306
1408
  if (options?.skipValidation) {
1307
- return diagnostics;
1409
+ return suppressUnreadableModuleDiagnostics(diagnostics, unreadableFiles);
1308
1410
  }
1309
1411
  // Build a name→manifest map for looking up referenced resources
1310
1412
  const byName = new Map();
@@ -1769,15 +1871,34 @@ export class StaticAnalyzer {
1769
1871
  const keywords = new Set(CEL_RESERVED_WORDS);
1770
1872
  for (const name of Object.keys(declared)) {
1771
1873
  const shadows = inScope.has(name);
1772
- if (!shadows && !keywords.has(name))
1874
+ if (shadows || keywords.has(name)) {
1875
+ diagnostics.push({
1876
+ severity: DiagnosticSeverity.Error,
1877
+ code: "BINDING_NAME_RESERVED",
1878
+ source: SOURCE,
1879
+ message: shadows
1880
+ ? `${m.kind}/${bindingsName}: binding '${name}' shadows a variable already in scope here (${[...inScope].sort().join(", ")}). Rename the binding — a scope variable always wins, so this one would never be read.`
1881
+ : `${m.kind}/${bindingsName}: binding '${name}' is a CEL keyword, so no expression can read it as a reference. Rename the binding.`,
1882
+ data: {
1883
+ resource: resourceRef,
1884
+ filePath: bindingsFile,
1885
+ path: `${celBindingSites.field}.${name}`,
1886
+ },
1887
+ });
1888
+ continue;
1889
+ }
1890
+ // A binding is read by bare name, so it lives in the same
1891
+ // identifier space as a resource or step name and breaks the
1892
+ // same way. The keyword tier is unreachable here — the check
1893
+ // above owns it, and can also say what is being shadowed.
1894
+ const violation = checkName(name, "value", "binding name");
1895
+ if (!violation)
1773
1896
  continue;
1774
1897
  diagnostics.push({
1775
- severity: DiagnosticSeverity.Error,
1776
- code: "BINDING_NAME_RESERVED",
1898
+ severity: violation.severity,
1899
+ code: violation.code,
1777
1900
  source: SOURCE,
1778
- message: shadows
1779
- ? `${m.kind}/${bindingsName}: binding '${name}' shadows a variable already in scope here (${[...inScope].sort().join(", ")}). Rename the binding — a scope variable always wins, so this one would never be read.`
1780
- : `${m.kind}/${bindingsName}: binding '${name}' is a CEL keyword, so no expression can read it as a reference. Rename the binding.`,
1901
+ message: `${m.kind}/${bindingsName}: ${violation.message}`,
1781
1902
  data: {
1782
1903
  resource: resourceRef,
1783
1904
  filePath: bindingsFile,
@@ -2120,7 +2241,7 @@ export class StaticAnalyzer {
2120
2241
  diagnostics.push(...validateUnusedDeclarations(allManifests, this.celEnv));
2121
2242
  // Reroute diagnostics on synthetic (inline-extracted) resources back to
2122
2243
  // the chain root so position-index lookups land on the parent doc.
2123
- return rewriteSyntheticOrigins(diagnostics, allManifests);
2244
+ return rewriteSyntheticOrigins(suppressUnreadableModuleDiagnostics(diagnostics, unreadableFiles), allManifests);
2124
2245
  }
2125
2246
  analyzeErrors(manifests, options, registry, zoneExportCache) {
2126
2247
  return this.analyze(manifests, options, registry, zoneExportCache).filter((d) => d.severity === DiagnosticSeverity.Error);
@@ -1 +1 @@
1
- {"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAwLvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,EA2rB/C,CAAC"}
1
+ {"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAiNvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,EAusB/C,CAAC"}
package/dist/builtins.js CHANGED
@@ -1,5 +1,25 @@
1
- import { manifestFragment } from "./manifest-schemas.js";
2
- import { OBSERVED_STATE_SCHEMA } from "./validate-observed-state.js";
1
+ import { manifestFragment, manifestFragmentRef, withSchemaFragments } from "./manifest-schemas.js";
2
+ /** A slot holding author-written JSON Schema. Localized and hoisted by
3
+ * {@link withSchemaFragments} on the enclosing schema, which is what makes the
4
+ * `#/$defs` pointer resolve inside whatever AJV compiles.
5
+ *
6
+ * `KindSchema` and `JsonSchema7` share a body; the name is the discriminator
7
+ * the IDE reads off the `x-telo-fragment` stamp to decide whether the
8
+ * `x-telo-*` vocabulary belongs here. A kind's own `schema:` is where it does;
9
+ * a `status:` block or an `inputType:` describes plain data, where it does not. */
10
+ const kindSchemaSlot = {
11
+ title: "Schema",
12
+ description: "Configuration this kind accepts, as JSON Schema plus `x-telo-*` annotations.",
13
+ $ref: manifestFragmentRef("KindSchema"),
14
+ };
15
+ /** Observed state a kind reports while running, as a data schema. `required:` is
16
+ * rejected separately by `validateObservedStateDeclarations`, which can say why
17
+ * and what to write instead. */
18
+ const observedStateSlot = {
19
+ title: "Observed state",
20
+ description: "What a resource of this kind reports while running, published at `resources.<name>.status.<field>`.",
21
+ $ref: manifestFragmentRef("JsonSchema7"),
22
+ };
3
23
  /** Descriptive provenance a module declares about itself, shared by
4
24
  * `Telo.Application` and `Telo.Library`.
5
25
  *
@@ -21,6 +41,31 @@ const PROVENANCE_METADATA = {
21
41
  license: { type: "string" },
22
42
  documentation: { type: "string" },
23
43
  };
44
+ /** The declared runtime requirements block, shared by `Telo.Application` and
45
+ * `Telo.Library` — the two module kinds, whose schemas are otherwise
46
+ * independent and would drift.
47
+ *
48
+ * **Deliberately says only "these are objects", and nothing about the values.**
49
+ * The grammar belongs to `requires-block.ts`, which is the single reader, and
50
+ * every rule that matters — `^` and `~` refused, a bare version refused, bounds
51
+ * that must not exclude each other, an upper bound that must name a version that
52
+ * exists — needs a parse and a comparison, not a schema. Adding `type: "string"`
53
+ * here bought nothing and cost a duplicate: `telo: 80` then produced BOTH a
54
+ * `SCHEMA_VIOLATION` and a `REQUIRES_INVALID` for one node, one of them phrased
55
+ * by a layer that does not know what the value is for.
56
+ *
57
+ * Left open at both tiers for the same reason. An unrecognized axis is reported
58
+ * by the reader with the vocabulary it knows — a far better message than AJV's —
59
+ * and, critically, is SUPPRESSED while the `telo` requirement is itself unmet,
60
+ * since an older runtime not knowing a newer axis is a consequence of the
61
+ * version skew rather than a second defect. AJV cannot express that ordering. */
62
+ const REQUIRES_SCHEMA = {
63
+ type: "object",
64
+ properties: {
65
+ host: { type: "object" },
66
+ },
67
+ additionalProperties: true,
68
+ };
24
69
  /** Author-declared subset of `files:` that ships in the artifact's lazily
25
70
  * materialized `assets` layer. Optional: an unclaimed file joins the `common`
26
71
  * layer, which is pulled alongside any controller layer, so omitting this costs
@@ -265,13 +310,13 @@ export const KERNEL_BUILTINS = [
265
310
  // kernel registers this controller directly at boot, before any lazy
266
311
  // resolution — it states truthfully who provides it.
267
312
  controllers: [{ runtime: "kernel", entry: "Telo.JsonSchema" }],
268
- schema: {
313
+ schema: withSchemaFragments({
269
314
  type: "object",
270
315
  properties: {
271
316
  schema: {
272
317
  title: "Schema",
273
318
  description: "JSON Schema definition for the declared data type.",
274
- type: "object",
319
+ $ref: manifestFragmentRef("JsonSchema7"),
275
320
  },
276
321
  extends: {
277
322
  title: "Extends",
@@ -304,13 +349,13 @@ export const KERNEL_BUILTINS = [
304
349
  },
305
350
  required: ["schema"],
306
351
  additionalProperties: false,
307
- },
352
+ }),
308
353
  },
309
354
  {
310
355
  kind: "Telo.Definition",
311
356
  metadata: { name: "Abstract", module: "Telo" },
312
357
  capability: "Telo.Template",
313
- schema: {
358
+ schema: withSchemaFragments({
314
359
  type: "object",
315
360
  properties: {
316
361
  kind: { type: "string" },
@@ -321,15 +366,15 @@ export const KERNEL_BUILTINS = [
321
366
  additionalProperties: true,
322
367
  },
323
368
  capability: { type: "string" },
324
- schema: { type: "object", additionalProperties: true },
325
- status: OBSERVED_STATE_SCHEMA,
369
+ schema: kindSchemaSlot,
370
+ status: observedStateSlot,
326
371
  },
327
372
  required: ["metadata"],
328
373
  // Telo.Abstract is an extension point by design — it must accept forward-compatible
329
374
  // fields (e.g. inputType/outputType from the typed-abstracts plan) without requiring
330
375
  // the analyzer to enumerate them here.
331
376
  additionalProperties: true,
332
- },
377
+ }),
333
378
  },
334
379
  {
335
380
  kind: "Telo.Definition",
@@ -347,11 +392,17 @@ export const KERNEL_BUILTINS = [
347
392
  // matching how Run.Sequence steps factor dispatch from data. The dispatch
348
393
  // entry-point (`invoke` / `provide` / `run`) determines how `inputs`/`result`
349
394
  // are interpreted at runtime. See analyzer/nodejs/plans/template-internal-cel-validation.md.
350
- schema: {
395
+ schema: withSchemaFragments({
351
396
  type: "object",
352
397
  additionalProperties: true,
353
398
  properties: {
354
- status: OBSERVED_STATE_SCHEMA,
399
+ // The kind's own configuration contract. Declared as a slot for the
400
+ // first time here: it was reachable only as an unnamed extra property,
401
+ // so nothing could say what belonged in it — no completion inside a
402
+ // `schema:` block, and a misspelled keyword surviving to a runtime
403
+ // failure that named a different field.
404
+ schema: kindSchemaSlot,
405
+ status: observedStateSlot,
355
406
  resources: {
356
407
  type: "array",
357
408
  items: {
@@ -521,7 +572,7 @@ export const KERNEL_BUILTINS = [
521
572
  },
522
573
  },
523
574
  },
524
- },
575
+ }),
525
576
  },
526
577
  {
527
578
  kind: "Telo.Definition",
@@ -772,6 +823,9 @@ export const KERNEL_BUILTINS = [
772
823
  // CLI flag — so a level derived from the host environment goes through a
773
824
  // `variables:` entry read with `!cel`. See kernel/specs/logging.md §12.
774
825
  logging: ROOT_LOGGING_SCHEMA,
826
+ // The runtime range this module is verified against. See
827
+ // `analyzer/nodejs/src/requires-block.ts`.
828
+ requires: REQUIRES_SCHEMA,
775
829
  },
776
830
  required: ["metadata"],
777
831
  additionalProperties: false,
@@ -862,6 +916,9 @@ export const KERNEL_BUILTINS = [
862
916
  },
863
917
  additionalProperties: true,
864
918
  },
919
+ // The runtime range this module is verified against. See
920
+ // `analyzer/nodejs/src/requires-block.ts`.
921
+ requires: REQUIRES_SCHEMA,
865
922
  },
866
923
  required: ["metadata"],
867
924
  additionalProperties: false,
@@ -14,12 +14,6 @@ export interface BindingSites {
14
14
  /** Variable names the annotated contexts declare — a binding may not shadow one. */
15
15
  scopeNames: Set<string>;
16
16
  }
17
- /**
18
- * CEL keywords. A binding named after one is unreachable — `true` lexes as a
19
- * literal, `in` as an operator — so it is reserved alongside the scope names,
20
- * which turns silence into a diagnostic.
21
- */
22
- export declare const CEL_RESERVED_WORDS: readonly string[];
23
17
  /** Locate a kind's bindings field and the scope names its annotated contexts
24
18
  * declare. Returns undefined for a kind that declares no bindings region. */
25
19
  export declare function findBindingSites(definitionSchema: Record<string, any> | undefined): BindingSites | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"cel-bindings.d.ts","sourceRoot":"","sources":["../src/cel-bindings.ts"],"names":[],"mappings":"AAIA;;;;gCAIgC;AAChC,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAE1D,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd;;gCAE4B;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,oFAAoF;IACpF,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAsB/C,CAAC;AAEF;8EAC8E;AAC9E,wBAAgB,gBAAgB,CAC9B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GAChD,YAAY,GAAG,SAAS,CAW1B;AAuDD;uEACuE;AACvE,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAW1B;AAED;;mFAEmF;AACnF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,IAAI,CAQhE;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACjC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAMrB;AAED;oEACoE;AACpE,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACxB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CASjC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACtE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;CACpB,CAwBA"}
1
+ {"version":3,"file":"cel-bindings.d.ts","sourceRoot":"","sources":["../src/cel-bindings.ts"],"names":[],"mappings":"AAIA;;;;gCAIgC;AAChC,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAE1D,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd;;gCAE4B;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,oFAAoF;IACpF,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACzB;AAMD;8EAC8E;AAC9E,wBAAgB,gBAAgB,CAC9B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GAChD,YAAY,GAAG,SAAS,CAW1B;AAuDD;uEACuE;AACvE,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAW1B;AAED;;mFAEmF;AACnF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,IAAI,CAQhE;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACjC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAMrB;AAED;oEACoE;AACpE,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACxB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CASjC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACtE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;CACpB,CAwBA"}
@@ -7,34 +7,9 @@ import { extractContextsFromSchema } from "./validate-cel-context.js";
7
7
  * contexts that see it may be anchored anywhere (a decision table annotates both
8
8
  * `choices` and `default`). */
9
9
  export const BINDINGS_ANNOTATION = "x-telo-bindings-from";
10
- /**
11
- * CEL keywords. A binding named after one is unreachable — `true` lexes as a
12
- * literal, `in` as an operator so it is reserved alongside the scope names,
13
- * which turns silence into a diagnostic.
14
- */
15
- export const CEL_RESERVED_WORDS = [
16
- "as",
17
- "break",
18
- "const",
19
- "continue",
20
- "else",
21
- "false",
22
- "for",
23
- "function",
24
- "if",
25
- "import",
26
- "in",
27
- "let",
28
- "loop",
29
- "namespace",
30
- "null",
31
- "package",
32
- "return",
33
- "true",
34
- "var",
35
- "void",
36
- "while",
37
- ];
10
+ // CEL keywords live in `identifier-name.ts` — a binding named after one is
11
+ // unreachable for exactly the reason a resource or step named after one is, so
12
+ // the list belongs to the identifier vocabulary rather than to this file.
38
13
  /** Locate a kind's bindings field and the scope names its annotated contexts
39
14
  * declare. Returns undefined for a kind that declares no bindings region. */
40
15
  export function findBindingSites(definitionSchema) {
@@ -52,6 +52,23 @@ export declare class DefinitionRegistry {
52
52
  * `$ref: "telo://<module>/<Name>"` would then silently validate against the
53
53
  * kind's schema instead. The caller reports it; nothing is overwritten. */
54
54
  registerNamedTypeSchema(id: string, schema: Record<string, any>): boolean;
55
+ /**
56
+ * Register a schema, surviving one AJV refuses.
57
+ *
58
+ * `addSchema` META-VALIDATES and THROWS, and a throw here escapes the whole
59
+ * analyze pass: one author schema with `minimum: "3"` in it aborted the run
60
+ * with AJV's own unanchored text and took every other diagnostic in the file
61
+ * down with it — including the anchored one that says exactly which keyword is
62
+ * wrong. Registration is a lookup table for `$ref` resolution, so failing to
63
+ * fill one entry costs a reference that could not have resolved anyway.
64
+ *
65
+ * Nothing is swallowed: an unregisterable schema is invalid, and the two
66
+ * checks that report it both run afterwards and both anchor on the offending
67
+ * line — `SCHEMA_VIOLATION` from the `KindSchema` / `JsonSchema7` fragment the
68
+ * slot points at, and `SCHEMA_COMPILE_ERROR` from {@link schemaCompileError},
69
+ * which wraps `compile` for this same reason.
70
+ */
71
+ private tryAddSchema;
55
72
  /** True when a schema is registered under `id` (a canonical `telo://` type id
56
73
  * or a definition `$id`). Used to flag schema `$ref`s that resolve to nothing. */
57
74
  hasSchemaId(id: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"definition-registry.d.ts","sourceRoot":"","sources":["../src/definition-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAC;AAIlC,+EAA+E;AAC/E,qBAAa,kBAAkB;;IAK7B;;sFAEkF;IAClF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD;;mFAE+E;IAC/E,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IAEzD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;IAClE,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D;;;;wEAIoE;IACpE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAEzD,QAAQ,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI;IAoC9C,OAAO,CAAC,aAAa;IASrB;;;;;;;;;;;;;2FAauF;IACvF,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAK1E;;;;;;;;;;gFAU4E;IAC5E,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO;IAQzE;uFACmF;IACnF,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIhC;;;gEAG4D;IAC5D,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS;IAMxD;;;;oFAIgF;IAChF,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE;IAWtE;;;;;;iCAM6B;IAC7B,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,GAAG,SAAS;IASnE;;;;4EAIwE;IACxE,OAAO,CAAC,iBAAiB;IAkBzB;;;;;;;;;;;;;;4BAcwB;IACxB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAShD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAIrD;;;uCAGmC;IACnC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAWxD,gGAAgG;IAChG,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,GACvD,iBAAiB,GAAG,SAAS;IAOhC;;;;;;;;qFAQiF;IACjF,2BAA2B,CACzB,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,aAAa,EACtB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC1C,iBAAiB,GAAG,SAAS;IAkChC,OAAO,CAAC,uBAAuB;IA+B/B;;qEAEiE;IACjE,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAgBxD,KAAK,IAAI,MAAM,EAAE;CAGlB"}
1
+ {"version":3,"file":"definition-registry.d.ts","sourceRoot":"","sources":["../src/definition-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAC;AAIlC,+EAA+E;AAC/E,qBAAa,kBAAkB;;IAK7B;;sFAEkF;IAClF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD;;mFAE+E;IAC/E,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IAEzD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;IAClE,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D;;;;wEAIoE;IACpE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAEzD,QAAQ,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI;IAoC9C,OAAO,CAAC,aAAa;IASrB;;;;;;;;;;;;;2FAauF;IACvF,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAK1E;;;;;;;;;;gFAU4E;IAC5E,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO;IAQzE;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,YAAY;IASpB;uFACmF;IACnF,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIhC;;;gEAG4D;IAC5D,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS;IAMxD;;;;oFAIgF;IAChF,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE;IAWtE;;;;;;iCAM6B;IAC7B,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,GAAG,SAAS;IASnE;;;;4EAIwE;IACxE,OAAO,CAAC,iBAAiB;IAqBzB;;;;;;;;;;;;;;4BAcwB;IACxB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAShD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAIrD;;;uCAGmC;IACnC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAWxD,gGAAgG;IAChG,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,GACvD,iBAAiB,GAAG,SAAS;IAOhC;;;;;;;;qFAQiF;IACjF,2BAA2B,CACzB,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,aAAa,EACtB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC1C,iBAAiB,GAAG,SAAS;IAkChC,OAAO,CAAC,uBAAuB;IA+B/B;;qEAEiE;IACjE,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAgBxD,KAAK,IAAI,MAAM,EAAE;CAGlB"}
@@ -108,10 +108,36 @@ export class DefinitionRegistry {
108
108
  return false;
109
109
  if (this.registeredSchemaIds.has(id) || this.ajv.getSchema(id))
110
110
  return true;
111
- this.ajv.addSchema(schema, id);
111
+ if (!this.tryAddSchema(schema, id))
112
+ return true;
112
113
  this.registeredSchemaIds.add(id);
113
114
  return true;
114
115
  }
116
+ /**
117
+ * Register a schema, surviving one AJV refuses.
118
+ *
119
+ * `addSchema` META-VALIDATES and THROWS, and a throw here escapes the whole
120
+ * analyze pass: one author schema with `minimum: "3"` in it aborted the run
121
+ * with AJV's own unanchored text and took every other diagnostic in the file
122
+ * down with it — including the anchored one that says exactly which keyword is
123
+ * wrong. Registration is a lookup table for `$ref` resolution, so failing to
124
+ * fill one entry costs a reference that could not have resolved anyway.
125
+ *
126
+ * Nothing is swallowed: an unregisterable schema is invalid, and the two
127
+ * checks that report it both run afterwards and both anchor on the offending
128
+ * line — `SCHEMA_VIOLATION` from the `KindSchema` / `JsonSchema7` fragment the
129
+ * slot points at, and `SCHEMA_COMPILE_ERROR` from {@link schemaCompileError},
130
+ * which wraps `compile` for this same reason.
131
+ */
132
+ tryAddSchema(schema, id) {
133
+ try {
134
+ this.ajv.addSchema(schema, id);
135
+ return true;
136
+ }
137
+ catch {
138
+ return false;
139
+ }
140
+ }
115
141
  /** True when a schema is registered under `id` (a canonical `telo://` type id
116
142
  * or a definition `$id`). Used to flag schema `$ref`s that resolve to nothing. */
117
143
  hasSchemaId(id) {
@@ -173,7 +199,10 @@ export class DefinitionRegistry {
173
199
  if (this.ajv.getSchema(id)) {
174
200
  throw new Error(`Duplicate definition schema $id: "${id}" is already registered`);
175
201
  }
176
- this.ajv.addSchema(schema, id);
202
+ // A schema AJV refuses is left unregistered rather than aborting the pass —
203
+ // see {@link tryAddSchema}. The id stays claimed either way, so a later
204
+ // named type cannot quietly take a kind's place.
205
+ this.tryAddSchema(schema, id);
177
206
  this.registeredSchemaIds.add(id);
178
207
  this.definitionSchemaIds.add(id);
179
208
  }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Naming rules for every author-written Telo identifier — resource instances,
3
+ * kinds, modules, import aliases, step names, config declarations and CEL
4
+ * bindings. The single reader, so no surface re-derives the vocabulary; the
5
+ * `ref-slot.ts` / `zone-slot.ts` precedent.
6
+ *
7
+ * **The rules exist because Telo has no lexer.** A name is a YAML scalar, so
8
+ * nothing rejects its shape where it is declared, and every consequence
9
+ * surfaces later at a CEL site that references it — or not at all. Probed
10
+ * against the engine the runtime actually uses (`@marcbachmann/cel-js`):
11
+ *
12
+ * resources.my-server.url → EVALUATES, as `resources.my - server.url`
13
+ * resources.in → ParseError: Expected IDENTIFIER, got IN
14
+ * resources.2fa → ParseError
15
+ * resources.for → parses fine
16
+ *
17
+ * The first line is the one that decides the design. When a bare name is in
18
+ * scope — which `x-telo-bindings-from` deliberately makes possible — a
19
+ * hyphenated resource name yields a wrong number with no diagnostic anywhere.
20
+ * That is a swallowed error in the reference grammar, not a style preference.
21
+ * The last line is why the reserved set here is the whole keyword list rather
22
+ * than the subset today's parser happens to reject in field position: which
23
+ * keywords tokenize there is a property of a dependency, and a name that
24
+ * breaks on a parser upgrade was never safe.
25
+ *
26
+ * **Three tiers, because they fail in three different ways.**
27
+ *
28
+ * 1. `INVALID_NAME` (error, every surface) — not a CEL-safe identifier, or a
29
+ * CEL keyword. Below this line the name is unreferenceable or silently
30
+ * mis-referenced. This subsumes the old dot-only rule, which was the
31
+ * strictest special case of it: `!ref` splits on the first dot.
32
+ *
33
+ * 2. `INVALID_TYPE_NAME` (error) — a type-level name not starting uppercase.
34
+ * An error rather than a warning because half the reference grammar already
35
+ * rejects the alternative: `EXTENDS_ALIAS_PATTERN` in the kernel's manifest
36
+ * schema hard-rejects `extends: foo.Bar`, while nothing rejects the
37
+ * `metadata.name: foo` that produced it. A lowercase kind is a kind nobody
38
+ * can extend, so this only moves an existing failure to where it is
39
+ * fixable.
40
+ *
41
+ * 3. `NAME_CASE_CONVENTION` (warning) — a value-level name not starting
42
+ * lowercase. Warn-only, Rust's `non_snake_case` posture: a name is
43
+ * occasionally dictated from outside, and Telo has no `#[allow]`, so a hard
44
+ * error would leave no escape.
45
+ *
46
+ * **The convention: case encodes what the name denotes.** PascalCase names a
47
+ * *type* — something writable in `kind:` / `extends:` / an `x-telo-ref` or type
48
+ * slot. camelCase names a *value* — something holding data at runtime, read
49
+ * through `resources.` / `steps.` / `variables.` in CEL. That is what
50
+ * distinguishes `kind: Console.WriteLine` from `!ref Console.writeLine`, two
51
+ * character-identical grammars otherwise, and the collision is not
52
+ * hypothetical: it is the sanctioned singleton pattern (declare
53
+ * `kind: Self.WriteLine`, export the instance, withhold the kind).
54
+ *
55
+ * **Only the first character is checked.** A full camelCase pattern would
56
+ * relitigate `httpApi` vs `httpAPI` and `OAuthClient` vs `OauthClient` with no
57
+ * benefit — the first character is what carries the type/value signal, and a
58
+ * stricter rule would fight legitimate names containing acronyms or digits
59
+ * while owning a judgement nobody asked this pass to make. An
60
+ * entirely-acronym type name (`SQL`, `AI`) passes unchanged.
61
+ *
62
+ * **No `DiagnosticFix`.** A fix is a whole-value replacement for ONE node,
63
+ * and a rename is only correct when every reference moves with it — offering
64
+ * one here would rewrite `metadata.name` and break every `!ref` and CEL read
65
+ * of it. Renaming belongs to a refactor that owns the reference graph.
66
+ *
67
+ * Browser-safe: pure string predicates, no I/O, no Node built-ins.
68
+ */
69
+ import { DiagnosticSeverity } from "./types.js";
70
+ /**
71
+ * CEL keywords. A name matching one is unreachable — `true` lexes as a
72
+ * literal, `in` as an operator — so it is reserved everywhere a name becomes a
73
+ * CEL identifier, which is every surface this file governs.
74
+ */
75
+ export declare const CEL_RESERVED_WORDS: readonly string[];
76
+ /**
77
+ * Which half of the convention a name falls under.
78
+ *
79
+ * `type` — a name writable in a `kind:` / `extends:` / type slot: a module, a
80
+ * kind, an import alias, or a resource whose capability is `Telo.Type` (a
81
+ * named shape has no runtime instance, so its name denotes a type even though
82
+ * it is declared as a resource).
83
+ *
84
+ * `value` — a name read through a CEL scope: a resource instance, a step, a
85
+ * `variables` / `secrets` / `ports` declaration, a CEL binding.
86
+ */
87
+ export type NameLevel = "type" | "value";
88
+ /** Document kinds whose `metadata.name` is type-level. Everything else
89
+ * declaring a name is a resource instance, whose level is decided by its
90
+ * kind's capability (see {@link NameLevel}). */
91
+ export declare const TYPE_LEVEL_DOC_KINDS: ReadonlySet<string>;
92
+ export interface NameViolation {
93
+ /** Which tier failed. Callers use it to suppress a tier another check
94
+ * already reports better at that surface (the bindings site owns
95
+ * `reserved`, where it can also say what is being shadowed). */
96
+ tier: "grammar" | "reserved" | "case";
97
+ code: "INVALID_NAME" | "INVALID_TYPE_NAME" | "NAME_CASE_CONVENTION";
98
+ severity: DiagnosticSeverity;
99
+ /** Complete sentence(s), subject included — so every surface words the same
100
+ * rule the same way. */
101
+ message: string;
102
+ }
103
+ /**
104
+ * Check one author-written name.
105
+ *
106
+ * `surface` is the noun phrase naming what was declared ("resource name",
107
+ * "step name", "import alias"), used as the message's subject.
108
+ *
109
+ * Returns the first violation only: the tiers are ordered by how badly the
110
+ * name is broken, and telling an author their unparseable name is also
111
+ * miscased buries the part that matters.
112
+ */
113
+ export declare function checkName(name: string, level: NameLevel, surface: string): NameViolation | undefined;
114
+ //# sourceMappingURL=identifier-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifier-name.d.ts","sourceRoot":"","sources":["../src/identifier-name.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAsB/C,CAAC;AAQF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzC;;iDAEiD;AACjD,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAMnD,CAAC;AAEH,MAAM,WAAW,aAAa;IAC5B;;qEAEiE;IACjE,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;IACtC,IAAI,EAAE,cAAc,GAAG,mBAAmB,GAAG,sBAAsB,CAAC;IACpE,QAAQ,EAAE,kBAAkB,CAAC;IAC7B;6BACyB;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,MAAM,GACd,aAAa,GAAG,SAAS,CA6C3B"}