@telorun/analyzer 0.73.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 (138) hide show
  1. package/dist/analyzer.d.ts.map +1 -1
  2. package/dist/analyzer.js +26 -7
  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 +73 -16
  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/forwarded-declaration.d.ts +26 -0
  19. package/dist/forwarded-declaration.d.ts.map +1 -0
  20. package/dist/forwarded-declaration.js +31 -0
  21. package/dist/index.d.ts +9 -3
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +4 -1
  24. package/dist/kernel-globals.d.ts.map +1 -1
  25. package/dist/kernel-globals.js +21 -5
  26. package/dist/module-alias-scope.d.ts +11 -0
  27. package/dist/module-alias-scope.d.ts.map +1 -1
  28. package/dist/module-alias-scope.js +10 -0
  29. package/dist/module-file-claims.d.ts +17 -1
  30. package/dist/module-file-claims.d.ts.map +1 -1
  31. package/dist/module-file-claims.js +38 -3
  32. package/dist/module-named-files.d.ts +72 -0
  33. package/dist/module-named-files.d.ts.map +1 -0
  34. package/dist/module-named-files.js +115 -0
  35. package/dist/native-entries.d.ts +83 -0
  36. package/dist/native-entries.d.ts.map +1 -0
  37. package/dist/native-entries.js +169 -0
  38. package/dist/normalize-inline-resources.d.ts +41 -13
  39. package/dist/normalize-inline-resources.d.ts.map +1 -1
  40. package/dist/normalize-inline-resources.js +236 -54
  41. package/dist/reference-field-map.d.ts +59 -1
  42. package/dist/reference-field-map.d.ts.map +1 -1
  43. package/dist/reference-field-map.js +200 -34
  44. package/dist/release/payload-digest.d.ts +2 -1
  45. package/dist/release/payload-digest.d.ts.map +1 -1
  46. package/dist/release/payload-digest.js +2 -1
  47. package/dist/resolve-ref-sentinels.d.ts.map +1 -1
  48. package/dist/resolve-ref-sentinels.js +8 -5
  49. package/dist/resolve-throws-union.d.ts +25 -4
  50. package/dist/resolve-throws-union.d.ts.map +1 -1
  51. package/dist/resolve-throws-union.js +62 -32
  52. package/dist/resolve-zone-requirements.d.ts.map +1 -1
  53. package/dist/resolve-zone-requirements.js +2 -1
  54. package/dist/rewrite-synthetic-origins.d.ts +6 -1
  55. package/dist/rewrite-synthetic-origins.d.ts.map +1 -1
  56. package/dist/rewrite-synthetic-origins.js +107 -13
  57. package/dist/schema-compat.d.ts.map +1 -1
  58. package/dist/schema-compat.js +18 -5
  59. package/dist/schema-walk.d.ts +47 -13
  60. package/dist/schema-walk.d.ts.map +1 -1
  61. package/dist/schema-walk.js +150 -30
  62. package/dist/scope-declarations.d.ts +59 -0
  63. package/dist/scope-declarations.d.ts.map +1 -0
  64. package/dist/scope-declarations.js +45 -0
  65. package/dist/source-entries.d.ts +119 -0
  66. package/dist/source-entries.d.ts.map +1 -0
  67. package/dist/source-entries.js +349 -0
  68. package/dist/step-bodies.d.ts +24 -0
  69. package/dist/step-bodies.d.ts.map +1 -0
  70. package/dist/step-bodies.js +28 -0
  71. package/dist/telo-version.d.ts +1 -1
  72. package/dist/telo-version.js +1 -1
  73. package/dist/validate-cel-context.d.ts.map +1 -1
  74. package/dist/validate-cel-context.js +3 -2
  75. package/dist/validate-module-artifact.d.ts +4 -2
  76. package/dist/validate-module-artifact.d.ts.map +1 -1
  77. package/dist/validate-module-artifact.js +43 -4
  78. package/dist/validate-native-entries.d.ts +15 -0
  79. package/dist/validate-native-entries.d.ts.map +1 -0
  80. package/dist/validate-native-entries.js +103 -0
  81. package/dist/validate-observed-state.d.ts +7 -1
  82. package/dist/validate-observed-state.d.ts.map +1 -1
  83. package/dist/validate-observed-state.js +24 -6
  84. package/dist/validate-reference-forms.d.ts.map +1 -1
  85. package/dist/validate-reference-forms.js +2 -2
  86. package/dist/validate-references.d.ts.map +1 -1
  87. package/dist/validate-references.js +11 -5
  88. package/dist/validate-scope-reach.d.ts +31 -0
  89. package/dist/validate-scope-reach.d.ts.map +1 -0
  90. package/dist/validate-scope-reach.js +180 -0
  91. package/dist/validate-source-entries.d.ts +16 -0
  92. package/dist/validate-source-entries.d.ts.map +1 -0
  93. package/dist/validate-source-entries.js +90 -0
  94. package/dist/validate-throws-coverage.d.ts.map +1 -1
  95. package/dist/validate-throws-coverage.js +21 -39
  96. package/dist/value-type-keyword.d.ts +22 -5
  97. package/dist/value-type-keyword.d.ts.map +1 -1
  98. package/dist/value-type-keyword.js +16 -7
  99. package/package.json +3 -3
  100. package/src/analyzer.ts +33 -9
  101. package/src/artifact-axes.ts +24 -0
  102. package/src/artifact-layer-index.ts +9 -6
  103. package/src/artifact-selector.ts +110 -35
  104. package/src/builtins.ts +75 -16
  105. package/src/call-graph.ts +30 -0
  106. package/src/catch-scope.ts +1 -1
  107. package/src/forwarded-declaration.ts +32 -0
  108. package/src/index.ts +36 -1
  109. package/src/kernel-globals.ts +28 -5
  110. package/src/migrations/CLAUDE.md +35 -0
  111. package/src/module-alias-scope.ts +17 -0
  112. package/src/module-file-claims.ts +63 -4
  113. package/src/module-named-files.ts +162 -0
  114. package/src/native-entries.ts +227 -0
  115. package/src/normalize-inline-resources.ts +326 -70
  116. package/src/reference-field-map.ts +257 -52
  117. package/src/release/CLAUDE.md +32 -0
  118. package/src/release/payload-digest.ts +2 -1
  119. package/src/resolve-ref-sentinels.ts +7 -6
  120. package/src/resolve-throws-union.ts +87 -32
  121. package/src/resolve-zone-requirements.ts +2 -1
  122. package/src/rewrite-synthetic-origins.ts +107 -16
  123. package/src/schema-compat.ts +18 -4
  124. package/src/schema-walk.ts +162 -34
  125. package/src/scope-declarations.ts +98 -0
  126. package/src/source-entries.ts +502 -0
  127. package/src/step-bodies.ts +55 -0
  128. package/src/telo-version.ts +1 -1
  129. package/src/validate-cel-context.ts +3 -2
  130. package/src/validate-module-artifact.ts +46 -3
  131. package/src/validate-native-entries.ts +147 -0
  132. package/src/validate-observed-state.ts +36 -7
  133. package/src/validate-reference-forms.ts +2 -3
  134. package/src/validate-references.ts +10 -6
  135. package/src/validate-scope-reach.ts +213 -0
  136. package/src/validate-source-entries.ts +120 -0
  137. package/src/validate-throws-coverage.ts +24 -31
  138. package/src/value-type-keyword.ts +35 -9
@@ -1 +1 @@
1
- {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAiBzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAsC9B,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAWvF,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AA4DjE,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AAoGpB;0CAC0C;AAC1C,eAAO,MAAM,eAAe,GAAI,OAAO;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,KAAG,OACT,CAAC;AAErD;8EAC8E;AAC9E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGhD;AAqUD,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACtC;AA6DD,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAkFD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,OAAO,GAAE,qBAA0B;IAI/C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,EAC3B,eAAe,CAAC,EAAE,eAAe,GAChC,kBAAkB,EAAE;IAmlEvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,EAC3B,eAAe,CAAC,EAAE,eAAe,GAChC,kBAAkB,EAAE;IAMvB,SAAS,CACP,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,EAI1B,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,GACtC,gBAAgB,EAAE;IAyBrB,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,GACzB;QAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAsB5F"}
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAiBzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAuC9B,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAWvF,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AA4DjE,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AAwGpB;0CAC0C;AAC1C,eAAO,MAAM,eAAe,GAAI,OAAO;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,KAAG,OACT,CAAC;AAErD;8EAC8E;AAC9E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGhD;AAqUD,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACtC;AA6DD,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAkFD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,OAAO,GAAE,qBAA0B;IAI/C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,EAC3B,eAAe,CAAC,EAAE,eAAe,GAChC,kBAAkB,EAAE;IAsmEvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,EAC3B,eAAe,CAAC,EAAE,eAAe,GAChC,kBAAkB,EAAE;IAMvB,SAAS,CACP,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,EAI1B,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,GACtC,gBAAgB,EAAE;IAyBrB,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,GACzB;QAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAsB5F"}
package/dist/analyzer.js CHANGED
@@ -9,7 +9,7 @@ import { analyzerContractScope, resolveContract, } from "./invocation-contract.j
9
9
  import { buildCallGraph } from "./call-graph.js";
10
10
  import { buildDependencyGraph, formatCycle } from "./dependency-graph.js";
11
11
  import { buildKernelGlobalsIndex, KERNEL_GLOBAL_NAMES, } from "./kernel-globals.js";
12
- import { buildObservedStateIndex, buildObservedStateResourcesSchema, collectRunReachableNames, observedStateRead, validateObservedStateDeclarations, } from "./validate-observed-state.js";
12
+ import { buildObservedStateIndex, buildObservedStateResourcesSchema, collectRunReachableNames, forwardedResourceKey, observedStateRead, validateObservedStateDeclarations, } from "./validate-observed-state.js";
13
13
  import { computeSuggestKind } from "./kind-suggest.js";
14
14
  import { visitManifest } from "./manifest-visitor.js";
15
15
  import { declaringModuleScope, moduleAliasScope } from "./module-alias-scope.js";
@@ -54,6 +54,8 @@ import { validateIdentifierNames } from "./validate-identifier-names.js";
54
54
  import { validateExtends } from "./validate-extends.js";
55
55
  import { validateLogging } from "./validate-logging.js";
56
56
  import { validateModuleArtifact } from "./validate-module-artifact.js";
57
+ import { validateNativeEntries } from "./validate-native-entries.js";
58
+ import { validateSourceEntries } from "./validate-source-entries.js";
57
59
  import { validateIncludePlacement } from "./validate-include-placement.js";
58
60
  import { validateModuleMetadata } from "./validate-module-metadata.js";
59
61
  import { validateRequires } from "./validate-requires.js";
@@ -69,6 +71,8 @@ import { validateResourceInputs } from "./validate-resource-inputs.js";
69
71
  import { validateExports } from "./validate-exports.js";
70
72
  import { validateTemplateBody } from "./validate-template-body.js";
71
73
  import { validateUnusedDeclarations } from "./validate-unused-declarations.js";
74
+ import { validateScopedNameReach } from "./validate-scope-reach.js";
75
+ import { isForwardedDeclaration } from "./forwarded-declaration.js";
72
76
  import { validateThrowsCoverage } from "./validate-throws-coverage.js";
73
77
  import { readStepSlot } from "./step-slot.js";
74
78
  const SELF_PREFIX = "Self.";
@@ -277,7 +281,7 @@ function validateStepInvokeReferences(allManifests, defs, aliases) {
277
281
  const meta = r.metadata;
278
282
  if (typeof meta?.name !== "string" || REF_VALIDATION_SKIP_KINDS.has(r.kind))
279
283
  continue;
280
- if (meta.forwardedExport === true) {
284
+ if (isForwardedDeclaration(r)) {
281
285
  if (typeof meta.module === "string")
282
286
  loadedModules.add(meta.module);
283
287
  continue;
@@ -343,7 +347,7 @@ function validateStepInvokeReferences(allManifests, defs, aliases) {
343
347
  const meta = m.metadata;
344
348
  if (typeof meta?.name !== "string" ||
345
349
  REF_VALIDATION_SKIP_KINDS.has(m.kind) ||
346
- meta.forwardedExport === true)
350
+ isForwardedDeclaration(m))
347
351
  continue;
348
352
  const def = defs.resolve(aliases.resolveKind(m.kind) ?? m.kind);
349
353
  const defSchema = def?.schema;
@@ -1302,6 +1306,11 @@ export class StaticAnalyzer {
1302
1306
  // would otherwise fail on a consumer's machine — or, for a mistyped platform
1303
1307
  // axis, silently offer one platform's binary to every host.
1304
1308
  diagnostics.push(...validateModuleArtifact(allManifests));
1309
+ // The module doc's `native:` block: each rule decides whether a platform's
1310
+ // file can ever match a host, or whether two layers would write one path.
1311
+ diagnostics.push(...validateNativeEntries(allManifests, rootModules));
1312
+ // The module doc's `sources:` block: where each staged file comes from.
1313
+ diagnostics.push(...validateSourceEntries(allManifests, rootModules));
1305
1314
  // The descriptive `metadata:` surface. Nothing in the kernel branches on
1306
1315
  // these fields, which is precisely why they need a check: a mistyped one
1307
1316
  // has no runtime failure mode that would ever surface it.
@@ -1552,9 +1561,8 @@ export class StaticAnalyzer {
1552
1561
  // `kind`/CEL are authored in that module's scope (e.g. `Self.X` → that module, not the
1553
1562
  // consumer). Re-validating against the consumer's scope yields false UNDEFINED_KIND /
1554
1563
  // scope-mismatch errors, so skip — they participate here only as resolution targets.
1555
- if (m.metadata?.forwardedExport === true) {
1564
+ if (isForwardedDeclaration(m))
1556
1565
  continue;
1557
- }
1558
1566
  // A kind-only stand-in for a `resources:` entry is a DECLARATION, not an
1559
1567
  // instantiation: its kind is routinely an abstract and its configuration
1560
1568
  // is the importer's to supply, so validating it here would report a
@@ -2209,8 +2217,16 @@ export class StaticAnalyzer {
2209
2217
  continue;
2210
2218
  // An import's exported instance is indexed under `<Alias>.<name>`,
2211
2219
  // the two-level shape it publishes under, so a cross-module read
2212
- // is checked exactly like a local one.
2213
- const reported = observedState.get(read.alias ? `${read.alias}.${read.name}` : read.name);
2220
+ // is checked exactly like a local one. A bare name read inside a
2221
+ // dependency's own manifest names that dependency's resource.
2222
+ const readerModule = isForwardedDeclaration(m)
2223
+ ? m.metadata?.module
2224
+ : undefined;
2225
+ const reported = observedState.get(read.alias
2226
+ ? `${read.alias}.${read.name}`
2227
+ : readerModule !== undefined
2228
+ ? forwardedResourceKey(readerModule, read.name)
2229
+ : read.name);
2214
2230
  if (celRuleApplies && celEvalModeAt(celSites, path) === "compile") {
2215
2231
  diagnostics.push({
2216
2232
  severity: DiagnosticSeverity.Error,
@@ -2446,6 +2462,9 @@ export class StaticAnalyzer {
2446
2462
  diagnostics.push(...validateExports(allManifests, defs, aliases, rootModules));
2447
2463
  // A library's declared resource inputs, and every import that supplies them.
2448
2464
  diagnostics.push(...validateResourceInputs(allManifests, defs, aliases, rootModules, (supplied, required, isDeclaration) => kindSatisfies(supplied, required, defs, isDeclaration)));
2465
+ // An inline declaration referencing a name declared by a scope it was
2466
+ // written inside but is created outside of.
2467
+ diagnostics.push(...validateScopedNameReach(allManifests, defs, aliases, aliasesByModule, rootModules, (expr) => celAccessChains(this.celEnv, expr)));
2449
2468
  // Reroute diagnostics on synthetic (inline-extracted) resources back to
2450
2469
  // the chain root so position-index lookups land on the parent doc.
2451
2470
  return rewriteSyntheticOrigins(suppressUnreadableModuleDiagnostics(diagnostics, unreadableFiles), allManifests);
@@ -0,0 +1,14 @@
1
+ /** The selector platform axes, in canonical order. Closed as a set of axis
2
+ * names; the set of values stays open. */
3
+ export declare const PLATFORM_AXES: readonly ["os", "arch", "libc", "abi"];
4
+ export type PlatformAxis = (typeof PLATFORM_AXES)[number];
5
+ /** A form an axis value must take beyond the shared token grammar. */
6
+ export interface AxisValueForm {
7
+ readonly pattern: RegExp;
8
+ /** The form as a reader writes it, e.g. `<family>-<version>`. */
9
+ readonly form: string;
10
+ readonly examples: readonly string[];
11
+ }
12
+ /** The axes whose values carry a form of their own. */
13
+ export declare const AXIS_VALUE_FORMS: Readonly<Partial<Record<PlatformAxis, AxisValueForm>>>;
14
+ //# sourceMappingURL=artifact-axes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-axes.d.ts","sourceRoot":"","sources":["../src/artifact-axes.ts"],"names":[],"mappings":"AAEA;2CAC2C;AAC3C,eAAO,MAAM,aAAa,wCAAyC,CAAC;AAEpE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED,uDAAuD;AACvD,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,CAMnF,CAAC"}
@@ -0,0 +1,12 @@
1
+ // GENERATED by scripts/generate-artifact-axes.mjs from analyzer/artifact-axes/axes.json — do not edit.
2
+ /** The selector platform axes, in canonical order. Closed as a set of axis
3
+ * names; the set of values stays open. */
4
+ export const PLATFORM_AXES = ["os", "arch", "libc", "abi"];
5
+ /** The axes whose values carry a form of their own. */
6
+ export const AXIS_VALUE_FORMS = {
7
+ abi: {
8
+ pattern: new RegExp("^[a-z][a-z0-9_]*-[0-9]+(\\.[0-9]+)*$"),
9
+ form: "<family>-<version>",
10
+ examples: ["node-137", "telo-2"],
11
+ },
12
+ };
@@ -30,7 +30,7 @@
30
30
  import { type ArtifactSelector, type LayerRole, type PlatformTarget } from "./artifact-selector.js";
31
31
  export interface ArtifactLayer {
32
32
  role: LayerRole;
33
- /** Present on the code-bearing roles (`controller`, `library`) only. */
33
+ /** Present on the selector-keyed roles (`controller`, `library`, `native`) only. */
34
34
  selector?: ArtifactSelector;
35
35
  /** OCI blob digest — addresses the layer and verifies the transfer. */
36
36
  blob: string;
@@ -48,7 +48,7 @@ export declare class LayerIndexError extends Error {
48
48
  */
49
49
  export declare function parseLayerIndex(value: unknown, describe?: string): ArtifactLayer[];
50
50
  /** The singleton layer for a role, or undefined when the artifact has none. */
51
- export declare function singletonLayer(layers: readonly ArtifactLayer[], role: Exclude<LayerRole, "controller" | "library">): ArtifactLayer | undefined;
51
+ export declare function singletonLayer(layers: readonly ArtifactLayer[], role: Exclude<LayerRole, "controller" | "library" | "native">): ArtifactLayer | undefined;
52
52
  /** The layer of one code role carrying exactly `selector`, or undefined.
53
53
  *
54
54
  * By exact key rather than by re-matching a host: the candidate being resolved
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-layer-index.d.ts","sourceRoot":"","sources":["../src/artifact-layer-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAOL,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,wBAAwB,CAAC;AAQhC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,yBAAyB;gBAE1B,MAAM,EAAE,MAAM;CAI3B;AAiBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAW,GAAG,aAAa,EAAE,CAmEpF;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,YAAY,GAAG,SAAS,CAAC,GACjD,aAAa,GAAG,SAAS,CAE3B;AAED;;;;uBAIuB;AACvB,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,YAAY,GAAG,SAAS,CAAC,EAClD,QAAQ,EAAE,gBAAgB,GACzB,aAAa,GAAG,SAAS,CAK3B;AAED;;;6BAG6B;AAC7B,wBAAgB,eAAe,CAC7B,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,MAAM,EAAE,cAAc,GACrB,aAAa,EAAE,CAOjB"}
1
+ {"version":3,"file":"artifact-layer-index.d.ts","sourceRoot":"","sources":["../src/artifact-layer-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAML,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,wBAAwB,CAAC;AAQhC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,yBAAyB;gBAE1B,MAAM,EAAE,MAAM;CAI3B;AAiBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAW,GAAG,aAAa,EAAE,CAuEpF;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,YAAY,GAAG,SAAS,GAAG,QAAQ,CAAC,GAC5D,aAAa,GAAG,SAAS,CAE3B;AAED;;;;uBAIuB;AACvB,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,YAAY,GAAG,SAAS,CAAC,EAClD,QAAQ,EAAE,gBAAgB,GACzB,aAAa,GAAG,SAAS,CAK3B;AAED;;;6BAG6B;AAC7B,wBAAgB,eAAe,CAC7B,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,MAAM,EAAE,cAAc,GACrB,aAAa,EAAE,CAOjB"}
@@ -27,7 +27,7 @@
27
27
  * Browser-safe: `telo check`, the editor and the hub validate an index through
28
28
  * this module; only the kernel fetches and extracts.
29
29
  */
30
- import { LAYER_ROLES, isLayerRole, normalizeSelector, roleCarriesSelector, selectorKey, selectorMatches, } from "./artifact-selector.js";
30
+ import { isLayerRole, normalizeSelector, roleCarriesSelector, selectorKey, selectorMatches, } from "./artifact-selector.js";
31
31
  /** OCI content digest: `sha256:` + 64 lowercase hex. */
32
32
  const BLOB_DIGEST = /^sha256:[0-9a-f]{64}$/;
33
33
  /** Telo content digest: `sha256-` + unpadded base64url of 32 bytes. */
@@ -70,9 +70,11 @@ export function parseLayerIndex(value, describe = "layers") {
70
70
  }
71
71
  const entry = raw;
72
72
  if (typeof entry.role !== "string" || entry.role === "") {
73
- throw new LayerIndexError(`${where}: role is required and must be one of ${LAYER_ROLES.map((r) => `'${r}'`).join(", ")}; ` +
73
+ throw new LayerIndexError(`${where}: role is required and must be a non-empty string; ` +
74
74
  `got ${entry.role === undefined ? "nothing" : `'${String(entry.role)}'`}.`);
75
75
  }
76
+ const blob = digest("blob", entry.blob, where);
77
+ const integrity = digest("integrity", entry.integrity, where);
76
78
  // A role this runtime does not know is SKIPPED, never rejected. Roles are
77
79
  // added over time, and a runtime that cannot name one cannot need it — while
78
80
  // throwing would make the whole manifest unreadable, so a module gaining a
@@ -88,6 +90,9 @@ export function parseLayerIndex(value, describe = "layers") {
88
90
  throw new LayerIndexError(`${where}: a ${role} layer must declare a selector.`);
89
91
  }
90
92
  selector = normalizeSelector(entry.selector, where);
93
+ // An unknown axis skips the entry as an unknown role does (spec §3.1).
94
+ if (selector === undefined)
95
+ return;
91
96
  // Scoped by role: a module's `js` controller layer and its `js` library
92
97
  // layer are different layers with the same selector, and only a collision
93
98
  // *within* one role means two layers claim one address.
@@ -110,8 +115,8 @@ export function parseLayerIndex(value, describe = "layers") {
110
115
  layers.push({
111
116
  role,
112
117
  ...(selector ? { selector } : {}),
113
- blob: digest("blob", entry.blob, where),
114
- integrity: digest("integrity", entry.integrity, where),
118
+ blob,
119
+ integrity,
115
120
  });
116
121
  });
117
122
  return layers;
@@ -3,7 +3,8 @@
3
3
  * controller candidate is chosen by, and the key a controller layer of a module
4
4
  * artifact is stored under.
5
5
  *
6
- * A selector is `format` plus the optional platform axes `os` / `arch` / `libc`.
6
+ * A selector is `format` plus the optional platform axes, whose vocabulary is
7
+ * data (`analyzer/artifact-axes/axes.json`, generated into `PLATFORM_AXES`).
7
8
  * Matching is one rule, applied per axis: an axis the selector omits accepts
8
9
  * anything, an axis it states must be equal. That is what lets a `js` controller
9
10
  * be platform-neutral and a `napi` controller be pinned to one triple, with no
@@ -24,9 +25,11 @@
24
25
  * canonical OCI/GOOS names at the kernel boundary, since these values are
25
26
  * published into OCI descriptors.
26
27
  */
27
- /** The role a layer plays in a module artifact. `controller` and `library` layers
28
- * carry a selector; `assets` and `common` are singletons and carry none. */
29
- export type LayerRole = "controller" | "library" | "assets" | "common";
28
+ import { PLATFORM_AXES, type PlatformAxis } from "./artifact-axes.js";
29
+ /** The role a layer plays in a module artifact. `controller`, `library` and
30
+ * `native` layers carry a selector; `assets` and `common` are singletons and
31
+ * carry none. */
32
+ export type LayerRole = "controller" | "library" | "native" | "assets" | "common";
30
33
  export declare const LAYER_ROLES: readonly LayerRole[];
31
34
  export declare function isLayerRole(value: unknown): value is LayerRole;
32
35
  /** The roles that hold executable code, and are therefore per format rather than
@@ -36,39 +39,41 @@ export declare function isLayerRole(value: unknown): value is LayerRole;
36
39
  * A singleton would be wrong the moment a second runtime ships. */
37
40
  export declare const CODE_LAYER_ROLES: readonly LayerRole[];
38
41
  export declare function roleCarriesSelector(role: LayerRole): boolean;
39
- /** The platform axes, in canonical order. Not a closed vocabulary of *values* —
40
- * new architectures appear without a Telo release — only of axis names. */
41
- export declare const PLATFORM_AXES: readonly ["os", "arch", "libc"];
42
- export type PlatformAxis = (typeof PLATFORM_AXES)[number];
43
- export interface ArtifactSelector {
42
+ export { PLATFORM_AXES, type PlatformAxis };
43
+ export interface ArtifactSelector extends Partial<Record<PlatformAxis, string>> {
44
44
  /** Bundled controller format: the PURL name segment (`js`, `napi`, `wasm`, …). */
45
45
  format: string;
46
- os?: string;
47
- arch?: string;
48
- libc?: string;
49
46
  }
50
47
  /** What a selector is matched against: the host the kernel runs on, or the
51
- * target `telo install --platform` is warming a cache for. An axis left
52
- * undetermined (a host whose libc cannot be detected) matches no selector that
53
- * constrains it — refusing to load is the safe direction for a native binary. */
54
- export interface PlatformTarget {
48
+ * target `telo install` is warming a cache for. An axis left undetermined (a
49
+ * host whose libc cannot be detected) matches no selector that constrains it —
50
+ * refusing to load is the safe direction for a native binary. */
51
+ export interface PlatformTarget extends Partial<Record<PlatformAxis, string>> {
55
52
  format?: string;
56
- os?: string;
57
- arch?: string;
58
- libc?: string;
59
53
  }
60
54
  export declare class ArtifactSelectorError extends Error {
61
55
  readonly code = "INVALID_ARTIFACT_SELECTOR";
62
56
  constructor(detail: string);
63
57
  }
58
+ /** Validate and normalize one selector value: the shared token grammar, plus the
59
+ * axis's own value form where the vocabulary declares one. */
60
+ export declare function normalizeAxisValue(axis: string, raw: unknown, describe: string): string;
64
61
  /**
65
62
  * Build a selector from a controller candidate's format and qualifier map.
66
63
  * Qualifier keys other than the platform axes are ignored — `path` and the
67
64
  * sibling list live in the same map and are not part of the selector.
68
65
  */
69
66
  export declare function selectorFromQualifiers(format: unknown, qualifiers: Readonly<Record<string, unknown>> | undefined, describe?: string): ArtifactSelector;
70
- /** Validate and normalize a selector read off a published layer index. */
71
- export declare function normalizeSelector(value: unknown, describe?: string): ArtifactSelector;
67
+ /**
68
+ * Validate and normalize a selector read off a published layer index.
69
+ *
70
+ * Returns undefined when the selector carries an axis this runtime does not
71
+ * know: the layer is for a newer runtime, and the caller skips it whole. The
72
+ * unknown axis is never dropped — two layers differing only in it would then
73
+ * claim one address. The known axes are still validated, since their grammar
74
+ * does not change with the axis set.
75
+ */
76
+ export declare function normalizeSelector(value: unknown, describe?: string): ArtifactSelector | undefined;
72
77
  /**
73
78
  * The canonical stable key for a selector: sorted `axis=value` pairs joined by
74
79
  * `;`. Used to group entry points into layers at publish time and to detect two
@@ -85,4 +90,25 @@ export declare function describeSelector(selector: ArtifactSelector): string;
85
90
  * not be handed a `libc=gnu` binary on the assumption it will run.
86
91
  */
87
92
  export declare function selectorMatches(selector: ArtifactSelector, target: PlatformTarget): boolean;
93
+ /**
94
+ * The axes `selector` constrains and `target` leaves undetermined, when those
95
+ * alone keep the two from matching — what a warm skips for want of a value rather
96
+ * than because the selector is for another platform. `undefined` when the
97
+ * selector matches, or differs on a determined axis.
98
+ */
99
+ export declare function undeterminedAxesBlockingMatch(selector: ArtifactSelector, target: PlatformTarget): PlatformAxis[] | undefined;
100
+ /** A selector that no host can match as its author meant it. */
101
+ export interface SelectorContradiction {
102
+ /** Suffix of the diagnostic code; each surface prefixes its own. */
103
+ readonly rule: "NAPI_ABI_FORBIDDEN" | "LIBC_OFF_LINUX";
104
+ readonly axis: PlatformAxis;
105
+ readonly detail: string;
106
+ }
107
+ /**
108
+ * The combinations of axes the grammar accepts and no host can mean: an N-API
109
+ * addon stating a runtime ABI, and a libc on an os that has none. Shared by every
110
+ * surface that authors a selector — `native:` entries, controller candidates and
111
+ * `exports.code:` entries — so each reports the same rule under its own prefix.
112
+ */
113
+ export declare function selectorContradictions(selector: ArtifactSelector): SelectorContradiction[];
88
114
  //# sourceMappingURL=artifact-selector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-selector.d.ts","sourceRoot":"","sources":["../src/artifact-selector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;6EAC6E;AAC7E,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEvE,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAAkD,CAAC;AAE/F,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAE9D;AAED;;;;oEAIoE;AACpE,eAAO,MAAM,gBAAgB,EAAE,SAAS,SAAS,EAA8B,CAAC;AAEhF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAE5D;AAED;4EAC4E;AAC5E,eAAO,MAAM,aAAa,iCAAkC,CAAC;AAE7D,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;kFAGkF;AAClF,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,+BAA+B;gBAEhC,MAAM,EAAE,MAAM;CAI3B;AAsBD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,EACzD,QAAQ,SAAwB,GAC/B,gBAAgB,CAUlB;AAED,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,QAAQ,SAAmB,GAC1B,gBAAgB,CAelB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAO9D;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAKnE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAQ3F"}
1
+ {"version":3,"file":"artifact-selector.d.ts","sourceRoot":"","sources":["../src/artifact-selector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAoB,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAExF;;kBAEkB;AAClB,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAElF,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAM3C,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAE9D;AAED;;;;oEAIoE;AACpE,eAAO,MAAM,gBAAgB,EAAE,SAAS,SAAS,EAA8B,CAAC;AAOhF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAE5D;AAED,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,CAAC;AAE5C,MAAM,WAAW,gBAAiB,SAAQ,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC7E,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;kEAGkE;AAClE,MAAM,WAAW,cAAe,SAAQ,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,+BAA+B;gBAEhC,MAAM,EAAE,MAAM;CAI3B;AAMD;+DAC+D;AAC/D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAqBvF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,EACzD,QAAQ,SAAwB,GAC/B,gBAAgB,CAUlB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,QAAQ,SAAmB,GAC1B,gBAAgB,GAAG,SAAS,CAU9B;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAO9D;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAKnE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAQ3F;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,cAAc,GACrB,YAAY,EAAE,GAAG,SAAS,CAU5B;AAED,gEAAgE;AAChE,MAAM,WAAW,qBAAqB;IACpC,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,oBAAoB,GAAG,gBAAgB,CAAC;IACvD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,qBAAqB,EAAE,CAqB1F"}
@@ -3,7 +3,8 @@
3
3
  * controller candidate is chosen by, and the key a controller layer of a module
4
4
  * artifact is stored under.
5
5
  *
6
- * A selector is `format` plus the optional platform axes `os` / `arch` / `libc`.
6
+ * A selector is `format` plus the optional platform axes, whose vocabulary is
7
+ * data (`analyzer/artifact-axes/axes.json`, generated into `PLATFORM_AXES`).
7
8
  * Matching is one rule, applied per axis: an axis the selector omits accepts
8
9
  * anything, an axis it states must be equal. That is what lets a `js` controller
9
10
  * be platform-neutral and a `napi` controller be pinned to one triple, with no
@@ -24,7 +25,14 @@
24
25
  * canonical OCI/GOOS names at the kernel boundary, since these values are
25
26
  * published into OCI descriptors.
26
27
  */
27
- export const LAYER_ROLES = ["controller", "library", "assets", "common"];
28
+ import { AXIS_VALUE_FORMS, PLATFORM_AXES } from "./artifact-axes.js";
29
+ export const LAYER_ROLES = [
30
+ "controller",
31
+ "library",
32
+ "native",
33
+ "assets",
34
+ "common",
35
+ ];
28
36
  export function isLayerRole(value) {
29
37
  return typeof value === "string" && LAYER_ROLES.includes(value);
30
38
  }
@@ -34,12 +42,14 @@ export function isLayerRole(value) {
34
42
  * different files, and a consumer resolves the one its own runtime can import.
35
43
  * A singleton would be wrong the moment a second runtime ships. */
36
44
  export const CODE_LAYER_ROLES = ["controller", "library"];
45
+ /** Every role keyed by a selector, one layer per selector: the code roles, plus
46
+ * `native` — a platform-specific file the runtime does not import as code,
47
+ * declared in the module doc's `native:` block. */
48
+ const SELECTOR_LAYER_ROLES = [...CODE_LAYER_ROLES, "native"];
37
49
  export function roleCarriesSelector(role) {
38
- return CODE_LAYER_ROLES.includes(role);
50
+ return SELECTOR_LAYER_ROLES.includes(role);
39
51
  }
40
- /** The platform axes, in canonical order. Not a closed vocabulary of *values* —
41
- * new architectures appear without a Telo release — only of axis names. */
42
- export const PLATFORM_AXES = ["os", "arch", "libc"];
52
+ export { PLATFORM_AXES };
43
53
  export class ArtifactSelectorError extends Error {
44
54
  code = "INVALID_ARTIFACT_SELECTOR";
45
55
  constructor(detail) {
@@ -50,7 +60,9 @@ export class ArtifactSelectorError extends Error {
50
60
  /** Canonical token shape for every selector value. Lowercase, so the same
51
61
  * platform written two ways is one layer rather than two. */
52
62
  const TOKEN = /^[a-z0-9][a-z0-9_.-]*$/;
53
- function normalizeToken(axis, raw, describe) {
63
+ /** Validate and normalize one selector value: the shared token grammar, plus the
64
+ * axis's own value form where the vocabulary declares one. */
65
+ export function normalizeAxisValue(axis, raw, describe) {
54
66
  if (typeof raw !== "string") {
55
67
  throw new ArtifactSelectorError(`${describe}: ${axis} must be a string, got ${raw === null ? "null" : typeof raw}.`);
56
68
  }
@@ -59,6 +71,11 @@ function normalizeToken(axis, raw, describe) {
59
71
  throw new ArtifactSelectorError(`${describe}: ${axis} value '${raw}' is not a canonical token. ` +
60
72
  `Use lowercase letters, digits, '.', '-' or '_', starting with a letter or digit.`);
61
73
  }
74
+ const valueForm = AXIS_VALUE_FORMS[axis];
75
+ if (valueForm && !valueForm.pattern.test(value)) {
76
+ throw new ArtifactSelectorError(`${describe}: ${axis} value '${raw}' must have the form ${valueForm.form}, ` +
77
+ `e.g. ${valueForm.examples.map((e) => `'${e}'`).join(" or ")}.`);
78
+ }
62
79
  return value;
63
80
  }
64
81
  /**
@@ -68,28 +85,33 @@ function normalizeToken(axis, raw, describe) {
68
85
  */
69
86
  export function selectorFromQualifiers(format, qualifiers, describe = "controller selector") {
70
87
  const selector = {
71
- format: normalizeToken("format", format, describe),
88
+ format: normalizeAxisValue("format", format, describe),
72
89
  };
73
90
  for (const axis of PLATFORM_AXES) {
74
91
  const raw = qualifiers?.[axis];
75
92
  if (raw === undefined || raw === "")
76
93
  continue;
77
- selector[axis] = normalizeToken(axis, raw, describe);
94
+ selector[axis] = normalizeAxisValue(axis, raw, describe);
78
95
  }
79
96
  return selector;
80
97
  }
81
- /** Validate and normalize a selector read off a published layer index. */
98
+ /**
99
+ * Validate and normalize a selector read off a published layer index.
100
+ *
101
+ * Returns undefined when the selector carries an axis this runtime does not
102
+ * know: the layer is for a newer runtime, and the caller skips it whole. The
103
+ * unknown axis is never dropped — two layers differing only in it would then
104
+ * claim one address. The known axes are still validated, since their grammar
105
+ * does not change with the axis set.
106
+ */
82
107
  export function normalizeSelector(value, describe = "layer selector") {
83
108
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
84
109
  throw new ArtifactSelectorError(`${describe}: expected an object of selector axes.`);
85
110
  }
86
111
  const record = value;
87
- const unknown = Object.keys(record).filter((k) => k !== "format" && !PLATFORM_AXES.includes(k));
88
- if (unknown.length > 0) {
89
- throw new ArtifactSelectorError(`${describe}: unknown selector ${unknown.length === 1 ? "axis" : "axes"} ` +
90
- `${unknown.map((k) => `'${k}'`).join(", ")}. Known axes: format, ${PLATFORM_AXES.join(", ")}.`);
91
- }
92
- return selectorFromQualifiers(record.format, record, describe);
112
+ const selector = selectorFromQualifiers(record.format, record, describe);
113
+ const carriesUnknownAxis = Object.keys(record).some((k) => k !== "format" && !PLATFORM_AXES.includes(k));
114
+ return carriesUnknownAxis ? undefined : selector;
93
115
  }
94
116
  /**
95
117
  * The canonical stable key for a selector: sorted `axis=value` pairs joined by
@@ -129,3 +151,50 @@ export function selectorMatches(selector, target) {
129
151
  }
130
152
  return true;
131
153
  }
154
+ /**
155
+ * The axes `selector` constrains and `target` leaves undetermined, when those
156
+ * alone keep the two from matching — what a warm skips for want of a value rather
157
+ * than because the selector is for another platform. `undefined` when the
158
+ * selector matches, or differs on a determined axis.
159
+ */
160
+ export function undeterminedAxesBlockingMatch(selector, target) {
161
+ if (target.format !== undefined && selector.format !== target.format)
162
+ return undefined;
163
+ const axes = [];
164
+ for (const axis of PLATFORM_AXES) {
165
+ const constraint = selector[axis];
166
+ if (constraint === undefined)
167
+ continue;
168
+ if (target[axis] === undefined)
169
+ axes.push(axis);
170
+ else if (target[axis] !== constraint)
171
+ return undefined;
172
+ }
173
+ return axes.length > 0 ? axes : undefined;
174
+ }
175
+ /**
176
+ * The combinations of axes the grammar accepts and no host can mean: an N-API
177
+ * addon stating a runtime ABI, and a libc on an os that has none. Shared by every
178
+ * surface that authors a selector — `native:` entries, controller candidates and
179
+ * `exports.code:` entries — so each reports the same rule under its own prefix.
180
+ */
181
+ export function selectorContradictions(selector) {
182
+ const out = [];
183
+ if (selector.format === "napi" && selector.abi !== undefined) {
184
+ out.push({
185
+ rule: "NAPI_ABI_FORBIDDEN",
186
+ axis: "abi",
187
+ detail: `an N-API addon is ABI-stable across runtime releases and states no abi — remove ` +
188
+ `abi ${selector.abi}, which would keep it from loading anywhere else.`,
189
+ });
190
+ }
191
+ if (selector.libc !== undefined && selector.os !== undefined && selector.os !== "linux") {
192
+ out.push({
193
+ rule: "LIBC_OFF_LINUX",
194
+ axis: "libc",
195
+ detail: `libc is only determined on linux, so a selector for os '${selector.os}' that states ` +
196
+ `libc ${selector.libc} could never match a host. Remove libc.`,
197
+ });
198
+ }
199
+ return out;
200
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAiFA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AA8OvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,EA2pB/C,CAAC"}
1
+ {"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAkFA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAoSvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,EA+pB/C,CAAC"}
package/dist/builtins.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { PLATFORM_AXES } from "./artifact-axes.js";
1
2
  import { manifestFragment, manifestFragmentRef, withSchemaFragments } from "./manifest-schemas.js";
2
3
  /** A slot holding author-written JSON Schema. Localized and hoisted by
3
4
  * {@link withSchemaFragments} on the enclosing schema, which is what makes the
@@ -147,9 +148,68 @@ const LIBRARY_CANDIDATES_SCHEMA = {
147
148
  format: { type: "string" },
148
149
  path: { type: "string" },
149
150
  source: { type: "string" },
150
- os: { type: "string" },
151
- arch: { type: "string" },
152
- libc: { type: "string" },
151
+ ...Object.fromEntries(PLATFORM_AXES.map((axis) => [axis, { type: "string" }])),
152
+ },
153
+ additionalProperties: false,
154
+ },
155
+ };
156
+ /** `native:` — the module's platform-specific files, one entry per logical name
157
+ * per platform tuple. Closed, so a mistyped axis is a schema violation rather
158
+ * than a platform-neutral entry. Grammar and rules live in
159
+ * `analyzer/nodejs/src/native-entries.ts` and `validate-native-entries.ts`. */
160
+ const NATIVE_ENTRIES_SCHEMA = {
161
+ type: "array",
162
+ items: {
163
+ type: "object",
164
+ required: ["name", "format", "os", "arch", "path"],
165
+ properties: {
166
+ name: { type: "string" },
167
+ format: { type: "string" },
168
+ path: { type: "string" },
169
+ ...Object.fromEntries(PLATFORM_AXES.map((axis) => [axis, { type: "string" }])),
170
+ },
171
+ additionalProperties: false,
172
+ },
173
+ };
174
+ /** `sources:` — where every staged file comes from, keyed by source name, each
175
+ * entry keyed by the module-relative path it produces. Closed at every level.
176
+ * An entry is a file (`upstream` + `member`, pinned with `sha256` +
177
+ * `executable`) or a link (`target`); a mix of the two, and every other rule,
178
+ * is reported by `analyzer/nodejs/src/validate-source-entries.ts`. `archive`
179
+ * names the upstream's format. A source built in the repo names its build under
180
+ * `build`, keyed by build system (`cargo: <crate dir>`), with the digest of its
181
+ * build inputs (`inputs`). */
182
+ const SOURCES_SCHEMA = {
183
+ type: "object",
184
+ additionalProperties: {
185
+ type: "object",
186
+ required: ["version", "url", "archive", "notices", "entries"],
187
+ properties: {
188
+ version: { type: "string" },
189
+ url: { type: "string" },
190
+ archive: { enum: ["tar.gz"] },
191
+ notices: { type: "array", minItems: 1, items: { type: "string" } },
192
+ build: {
193
+ type: "object",
194
+ required: ["cargo"],
195
+ properties: { cargo: { type: "string" }, inputs: { type: "string" } },
196
+ additionalProperties: false,
197
+ },
198
+ entries: {
199
+ type: "object",
200
+ additionalProperties: {
201
+ type: "object",
202
+ anyOf: [{ required: ["upstream", "member"] }, { required: ["target"] }],
203
+ properties: {
204
+ upstream: { type: "string" },
205
+ member: { type: "string" },
206
+ sha256: { type: "string" },
207
+ executable: { type: "boolean" },
208
+ target: { type: "string" },
209
+ },
210
+ additionalProperties: false,
211
+ },
212
+ },
153
213
  },
154
214
  additionalProperties: false,
155
215
  },
@@ -158,25 +218,18 @@ const LIBRARY_CANDIDATES_SCHEMA = {
158
218
  * One entry per layer except the manifest layer, which cannot list its own hash
159
219
  * inside itself and is pinned by the importer's `#sha256-...` instead. Shape and
160
220
  * matching rules are normative in kernel/specs/module-artifact.md; the parser
161
- * that enforces them is `artifact-layer-index.ts`. */
221
+ * that enforces them is `artifact-layer-index.ts`. `role` stays open and
222
+ * `selector` unconstrained: an entry for a newer runtime is skipped, not
223
+ * rejected, and its selector is not examined (spec §3.1). A known role's
224
+ * selector is validated by the parser, which `telo check` runs. */
162
225
  const LAYER_INDEX_SCHEMA = {
163
226
  type: "array",
164
227
  items: {
165
228
  type: "object",
166
229
  required: ["role", "blob", "integrity"],
167
230
  properties: {
168
- role: { type: "string", enum: ["controller", "library", "assets", "common"] },
169
- selector: {
170
- type: "object",
171
- required: ["format"],
172
- properties: {
173
- format: { type: "string" },
174
- os: { type: "string" },
175
- arch: { type: "string" },
176
- libc: { type: "string" },
177
- },
178
- additionalProperties: false,
179
- },
231
+ role: { type: "string", minLength: 1 },
232
+ selector: {},
180
233
  blob: { type: "string", pattern: "^sha256:[0-9a-f]{64}$" },
181
234
  integrity: { type: "string", pattern: "^sha256-[A-Za-z0-9_-]{43}$" },
182
235
  },
@@ -716,6 +769,8 @@ export const KERNEL_BUILTINS = [
716
769
  items: { type: "string" },
717
770
  },
718
771
  assets: ASSETS_FILES_SCHEMA,
772
+ native: NATIVE_ENTRIES_SCHEMA,
773
+ sources: SOURCES_SCHEMA,
719
774
  layers: LAYER_INDEX_SCHEMA,
720
775
  filesIntegrity: LEGACY_FILES_INTEGRITY_SCHEMA,
721
776
  // Inline imports — name-keyed map sugar for separate `Telo.Import`
@@ -889,6 +944,8 @@ export const KERNEL_BUILTINS = [
889
944
  items: { type: "string" },
890
945
  },
891
946
  assets: ASSETS_FILES_SCHEMA,
947
+ native: NATIVE_ENTRIES_SCHEMA,
948
+ sources: SOURCES_SCHEMA,
892
949
  layers: LAYER_INDEX_SCHEMA,
893
950
  filesIntegrity: LEGACY_FILES_INTEGRITY_SCHEMA,
894
951
  // Inline imports — same name-keyed map sugar as Telo.Application; the