@telorun/analyzer 0.43.0 → 0.45.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 (47) hide show
  1. package/dist/analysis-registry.d.ts +4 -4
  2. package/dist/analysis-registry.d.ts.map +1 -1
  3. package/dist/analysis-registry.js +3 -3
  4. package/dist/analyzer.d.ts.map +1 -1
  5. package/dist/analyzer.js +84 -1
  6. package/dist/builtins.js +7 -7
  7. package/dist/definition-registry.d.ts +49 -22
  8. package/dist/definition-registry.d.ts.map +1 -1
  9. package/dist/definition-registry.js +67 -61
  10. package/dist/index.d.ts +4 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +2 -0
  13. package/dist/loaded-types.d.ts +13 -6
  14. package/dist/loaded-types.d.ts.map +1 -1
  15. package/dist/manifest-loader.d.ts.map +1 -1
  16. package/dist/manifest-loader.js +1 -0
  17. package/dist/module-version-order.d.ts +36 -0
  18. package/dist/module-version-order.d.ts.map +1 -0
  19. package/dist/module-version-order.js +91 -0
  20. package/dist/reconcile-module-versions.d.ts +2 -2
  21. package/dist/reconcile-module-versions.d.ts.map +1 -1
  22. package/dist/reconcile-module-versions.js +69 -79
  23. package/dist/reference-field-map.d.ts +2 -1
  24. package/dist/reference-field-map.d.ts.map +1 -1
  25. package/dist/resolve-schema-ref-kinds.d.ts +62 -0
  26. package/dist/resolve-schema-ref-kinds.d.ts.map +1 -0
  27. package/dist/resolve-schema-ref-kinds.js +81 -0
  28. package/dist/sources/manifest-cache.d.ts +14 -4
  29. package/dist/sources/manifest-cache.d.ts.map +1 -1
  30. package/dist/sources/manifest-cache.js +15 -6
  31. package/dist/sources/versioned-ref.d.ts +34 -0
  32. package/dist/sources/versioned-ref.d.ts.map +1 -0
  33. package/dist/sources/versioned-ref.js +60 -0
  34. package/package.json +2 -2
  35. package/src/analysis-registry.ts +4 -4
  36. package/src/analyzer.ts +85 -2
  37. package/src/builtins.ts +7 -7
  38. package/src/definition-registry.ts +67 -66
  39. package/src/index.ts +10 -0
  40. package/src/loaded-types.ts +13 -6
  41. package/src/manifest-loader.ts +1 -0
  42. package/src/module-version-order.ts +91 -0
  43. package/src/reconcile-module-versions.ts +77 -75
  44. package/src/reference-field-map.ts +2 -1
  45. package/src/resolve-schema-ref-kinds.ts +118 -0
  46. package/src/sources/manifest-cache.ts +26 -8
  47. package/src/sources/versioned-ref.ts +77 -0
@@ -46,18 +46,25 @@ export interface LoadedModule {
46
46
  partials: LoadedFile[];
47
47
  }
48
48
  /** Resolved Telo.Import edge: where the import points and what library
49
- * identity it resolves to. Carrying name/namespace on the edge means
50
- * `flattenForAnalyzer` can stamp `metadata.resolvedModuleName` /
51
- * `resolvedNamespace` from this single source rather than re-deriving
52
- * the target from manifest metadata, which would silently miss whenever
53
- * a future projection forgets to stamp `metadata.source` consistently. */
49
+ * identity it resolves to. Carrying the name on the edge means
50
+ * `flattenForAnalyzer` can stamp `metadata.resolvedModuleName` from this
51
+ * single source rather than re-deriving the target from manifest metadata,
52
+ * which would silently miss whenever a future projection forgets to stamp
53
+ * `metadata.source` consistently. */
54
54
  export interface ImportEdge {
55
55
  /** Canonical resolved URL of the target — a key into `modules`. */
56
56
  targetSource: string;
57
+ /** The import's `source` exactly as authored — a registry ref, an `oci://`
58
+ * or `https://` ref, or a relative path. Version reconciliation keys on
59
+ * this (minus its version), since it names the module's location
60
+ * independently of what the module declares about itself. */
61
+ targetRef: string;
57
62
  /** Target library's `metadata.name`, or `null` when the target had no
58
63
  * Telo.Library doc (an error case captured in `LoadedGraph.errors`). */
59
64
  targetModuleName: string | null;
60
- /** Target library's `metadata.namespace` (or `null` when unset). */
65
+ /** DEPRECATED. Target library's `metadata.namespace`, or `null` when it
66
+ * declares none. Feeds only the legacy `<namespace>/<module>#<Kind>` form
67
+ * of `x-telo-ref`; nothing else reads it. */
61
68
  targetNamespace: string | null;
62
69
  }
63
70
  /** An entry plus every transitively-imported library. Returned by
@@ -1 +1 @@
1
- {"version":3,"file":"loaded-types.d.ts","sourceRoot":"","sources":["../src/loaded-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD;;;;4EAI4E;AAC5E,MAAM,WAAW,UAAU;IACzB;+DAC2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf;gEAC4D;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb;;+EAE2E;IAC3E,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB;+EAC2E;IAC3E,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,6EAA6E;IAC7E,SAAS,EAAE,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC1C,0EAA0E;IAC1E,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,0EAA0E;IAC1E,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;mCACmC;AACnC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB;oEACgE;IAChE,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB;AAED;;;;;2EAK2E;AAC3E,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB;6EACyE;IACzE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,oEAAoE;IACpE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;0BAC0B;AAC1B,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,YAAY,CAAC;IACpB;;wBAEoB;IACpB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACnC;;;;2BAIuB;IACvB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAClD;;;iFAG6E;IAC7E,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;;;iDAG6C;IAC7C,kBAAkB,EAAE,kBAAkB,EAAE,CAAC;IACzC;;;8EAG0E;IAC1E,gBAAgB,EAAE,kBAAkB,EAAE,CAAC;IACvC;yCACqC;IACrC,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B;8BAC0B;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ;;qEAEiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;gFAE4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,KAAK,CAAC;CACd"}
1
+ {"version":3,"file":"loaded-types.d.ts","sourceRoot":"","sources":["../src/loaded-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD;;;;4EAI4E;AAC5E,MAAM,WAAW,UAAU;IACzB;+DAC2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf;gEAC4D;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb;;+EAE2E;IAC3E,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB;+EAC2E;IAC3E,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,6EAA6E;IAC7E,SAAS,EAAE,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC1C,0EAA0E;IAC1E,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,0EAA0E;IAC1E,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;mCACmC;AACnC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB;oEACgE;IAChE,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB;AAED;;;;;sCAKsC;AACtC,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB;;;kEAG8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB;6EACyE;IACzE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;kDAE8C;IAC9C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;0BAC0B;AAC1B,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,YAAY,CAAC;IACpB;;wBAEoB;IACpB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACnC;;;;2BAIuB;IACvB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAClD;;;iFAG6E;IAC7E,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;;;iDAG6C;IAC7C,kBAAkB,EAAE,kBAAkB,EAAE,CAAC;IACzC;;;8EAG0E;IAC1E,gBAAgB,EAAE,kBAAkB,EAAE,CAAC;IACvC;yCACqC;IACrC,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B;8BAC0B;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ;;qEAEiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;gFAE4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,KAAK,CAAC;CACd"}
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-loader.d.ts","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EACX,YAAY,EACb,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AAwCpB,qBAAa,MAAM;IACjB;;;yEAGqE;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAE3D;;;;;8BAK0B;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAEzD,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IAErC;;;;mBAIe;gBACH,OAAO,GAAE,cAAc,EAAO,EAAE,OAAO,GAAE,iBAAsB;IAK3E,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAKtC,OAAO,CAAC,IAAI;IAMN,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUrD;;;;2CAIuC;IACvC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAM7C;;;+BAG2B;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAyCvE;;;;gEAI4D;IAC5D,OAAO,CAAC,oBAAoB;IAa5B;gFAC4E;IAC5E,OAAO,CAAC,cAAc;IAQtB;;wEAEoE;IAC9D,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAsB3E;;;qCAGiC;IAC3B,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAgI9E;;;;;;;;sBAQkB;IAClB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAOlE,OAAO,CAAC,6BAA6B;IAarC,OAAO,CAAC,mCAAmC;IAc3C,OAAO,CAAC,2BAA2B;YAkCrB,eAAe;IAmB7B;;;0CAGsC;IAChC,gBAAgB,CACpB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CA0B5D"}
1
+ {"version":3,"file":"manifest-loader.d.ts","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EACX,YAAY,EACb,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AAwCpB,qBAAa,MAAM;IACjB;;;yEAGqE;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAE3D;;;;;8BAK0B;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAEzD,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IAErC;;;;mBAIe;gBACH,OAAO,GAAE,cAAc,EAAO,EAAE,OAAO,GAAE,iBAAsB;IAK3E,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAKtC,OAAO,CAAC,IAAI;IAMN,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUrD;;;;2CAIuC;IACvC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAM7C;;;+BAG2B;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAyCvE;;;;gEAI4D;IAC5D,OAAO,CAAC,oBAAoB;IAa5B;gFAC4E;IAC5E,OAAO,CAAC,cAAc;IAQtB;;wEAEoE;IAC9D,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAsB3E;;;qCAGiC;IAC3B,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAiI9E;;;;;;;;sBAQkB;IAClB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAOlE,OAAO,CAAC,6BAA6B;IAarC,OAAO,CAAC,mCAAmC;IAc3C,OAAO,CAAC,2BAA2B;YAkCrB,eAAe;IAmB7B;;;0CAGsC;IAChC,gBAAgB,CACpB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CA0B5D"}
@@ -274,6 +274,7 @@ export class Loader {
274
274
  }
275
275
  aliases.set(alias, {
276
276
  targetSource: targetCanonical,
277
+ targetRef: importSource,
277
278
  targetModuleName,
278
279
  targetNamespace,
279
280
  });
@@ -0,0 +1,36 @@
1
+ /** SemVer precedence for module versions — the single ordering rule shared by
2
+ * version reconciliation and by any host deciding whether an import is behind.
3
+ *
4
+ * Pure and dependency-free (no `semver` package), so the analyzer stays
5
+ * browser-safe and the editor can reach the same rule the kernel-side analysis
6
+ * uses. A caller that cannot parse a version must not guess: an OCI digest, a
7
+ * moving tag like `latest`, and a malformed pin all come back `null` rather
8
+ * than being ordered by some weaker fallback. */
9
+ export interface ParsedModuleVersion {
10
+ major: number;
11
+ minor: number;
12
+ patch: number;
13
+ /** Dot-separated prerelease identifiers, or `null` for a release version. */
14
+ pre: string[] | null;
15
+ }
16
+ /** Parse `X.Y.Z`, `vX.Y.Z`, or `X.Y.Z-pre.1`. Returns `null` for anything that
17
+ * isn't a plain three-part numeric core — an unparseable version is never
18
+ * silently ordered. */
19
+ export declare function parseModuleVersion(raw: string | undefined): ParsedModuleVersion | null;
20
+ /** SemVer precedence: numeric core, then a release outranks a prerelease, then
21
+ * prerelease identifiers compared field-by-field (numeric < non-numeric per
22
+ * spec, shorter set loses when all shared fields are equal). */
23
+ export declare function compareParsedModuleVersions(a: ParsedModuleVersion, b: ParsedModuleVersion): number;
24
+ /** Negative / zero / positive when both versions parse, `null` when either does
25
+ * not. The string-in convenience over {@link compareParsedModuleVersions}. */
26
+ export declare function compareModuleVersions(a: string, b: string): number | null;
27
+ /** True when `candidate` is strictly newer than `current` — the test for
28
+ * whether an import is behind. False when they are equal, when `current` is
29
+ * ahead (a version index can lag the module's own origin, and "upgrading" to
30
+ * what it knows would be a downgrade), or when either side is unparseable. */
31
+ export declare function isNewerModuleVersion(candidate: string, current: string): boolean;
32
+ /** True when two tags name the same version, tolerating a `v` prefix on either
33
+ * side. Falls back to exact equality for unparseable tags, so a digest still
34
+ * matches itself. */
35
+ export declare function isSameModuleVersion(a: string, b: string): boolean;
36
+ //# sourceMappingURL=module-version-order.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-version-order.d.ts","sourceRoot":"","sources":["../src/module-version-order.ts"],"names":[],"mappings":"AAAA;;;;;;;kDAOkD;AAElD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACtB;AAED;;wBAEwB;AACxB,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,mBAAmB,GAAG,IAAI,CAatF;AAED;;iEAEiE;AACjE,wBAAgB,2BAA2B,CACzC,CAAC,EAAE,mBAAmB,EACtB,CAAC,EAAE,mBAAmB,GACrB,MAAM,CAyBR;AAED;+EAC+E;AAC/E,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKzE;AAED;;;+EAG+E;AAC/E,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhF;AAED;;sBAEsB;AACtB,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEjE"}
@@ -0,0 +1,91 @@
1
+ /** SemVer precedence for module versions — the single ordering rule shared by
2
+ * version reconciliation and by any host deciding whether an import is behind.
3
+ *
4
+ * Pure and dependency-free (no `semver` package), so the analyzer stays
5
+ * browser-safe and the editor can reach the same rule the kernel-side analysis
6
+ * uses. A caller that cannot parse a version must not guess: an OCI digest, a
7
+ * moving tag like `latest`, and a malformed pin all come back `null` rather
8
+ * than being ordered by some weaker fallback. */
9
+ /** Parse `X.Y.Z`, `vX.Y.Z`, or `X.Y.Z-pre.1`. Returns `null` for anything that
10
+ * isn't a plain three-part numeric core — an unparseable version is never
11
+ * silently ordered. */
12
+ export function parseModuleVersion(raw) {
13
+ if (typeof raw !== "string")
14
+ return null;
15
+ const v = raw.startsWith("v") ? raw.slice(1) : raw;
16
+ const [core, ...preParts] = v.split("-");
17
+ const pre = preParts.length > 0 ? preParts.join("-") : null;
18
+ const segments = core.split(".");
19
+ if (segments.length !== 3)
20
+ return null;
21
+ const [major, minor, patch] = segments.map((s) => {
22
+ if (!/^\d+$/.test(s))
23
+ return NaN;
24
+ return Number(s);
25
+ });
26
+ if ([major, minor, patch].some((n) => Number.isNaN(n)))
27
+ return null;
28
+ return { major, minor, patch, pre: pre === null ? null : pre.split(".") };
29
+ }
30
+ /** SemVer precedence: numeric core, then a release outranks a prerelease, then
31
+ * prerelease identifiers compared field-by-field (numeric < non-numeric per
32
+ * spec, shorter set loses when all shared fields are equal). */
33
+ export function compareParsedModuleVersions(a, b) {
34
+ if (a.major !== b.major)
35
+ return a.major - b.major;
36
+ if (a.minor !== b.minor)
37
+ return a.minor - b.minor;
38
+ if (a.patch !== b.patch)
39
+ return a.patch - b.patch;
40
+ if (a.pre === null && b.pre === null)
41
+ return 0;
42
+ if (a.pre === null)
43
+ return 1;
44
+ if (b.pre === null)
45
+ return -1;
46
+ const len = Math.max(a.pre.length, b.pre.length);
47
+ for (let i = 0; i < len; i++) {
48
+ const ai = a.pre[i];
49
+ const bi = b.pre[i];
50
+ if (ai === undefined)
51
+ return -1;
52
+ if (bi === undefined)
53
+ return 1;
54
+ const an = /^\d+$/.test(ai);
55
+ const bn = /^\d+$/.test(bi);
56
+ if (an && bn) {
57
+ const d = Number(ai) - Number(bi);
58
+ if (d !== 0)
59
+ return d;
60
+ }
61
+ else if (an !== bn) {
62
+ return an ? -1 : 1;
63
+ }
64
+ else if (ai !== bi) {
65
+ return ai < bi ? -1 : 1;
66
+ }
67
+ }
68
+ return 0;
69
+ }
70
+ /** Negative / zero / positive when both versions parse, `null` when either does
71
+ * not. The string-in convenience over {@link compareParsedModuleVersions}. */
72
+ export function compareModuleVersions(a, b) {
73
+ const left = parseModuleVersion(a);
74
+ const right = parseModuleVersion(b);
75
+ if (!left || !right)
76
+ return null;
77
+ return compareParsedModuleVersions(left, right);
78
+ }
79
+ /** True when `candidate` is strictly newer than `current` — the test for
80
+ * whether an import is behind. False when they are equal, when `current` is
81
+ * ahead (a version index can lag the module's own origin, and "upgrading" to
82
+ * what it knows would be a downgrade), or when either side is unparseable. */
83
+ export function isNewerModuleVersion(candidate, current) {
84
+ return (compareModuleVersions(candidate, current) ?? 0) > 0;
85
+ }
86
+ /** True when two tags name the same version, tolerating a `v` prefix on either
87
+ * side. Falls back to exact equality for unparseable tags, so a digest still
88
+ * matches itself. */
89
+ export function isSameModuleVersion(a, b) {
90
+ return a === b || compareModuleVersions(a, b) === 0;
91
+ }
@@ -13,8 +13,8 @@ export interface VersionReconciliation {
13
13
  diagnostics: AnalysisDiagnostic[];
14
14
  }
15
15
  /**
16
- * Reconcile a loaded import graph so each module identity (`namespace/name`)
17
- * resolves to a single version. Within a shared major the highest version wins
16
+ * Reconcile a loaded import graph so each module location (an import ref minus
17
+ * its version) resolves to a single version. Within a shared major the highest version wins
18
18
  * (a non-lossy hoist, given Telo's additive-only pre-1.0 policy); a major
19
19
  * mismatch is a hard conflict. Mutates `importEdges` in place — every edge that
20
20
  * pointed at a losing source is repointed at the winner — so `flattenForAnalyzer`
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile-module-versions.d.ts","sourceRoot":"","sources":["../src/reconcile-module-versions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;0CAI0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;2EACuE;IACvE,WAAW,EAAE,kBAAkB,EAAE,CAAC;CACnC;AAwKD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAClC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,GAChD,qBAAqB,CAiDvB"}
1
+ {"version":3,"file":"reconcile-module-versions.d.ts","sourceRoot":"","sources":["../src/reconcile-module-versions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAOlE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;0CAI0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;2EACuE;IACvE,WAAW,EAAE,kBAAkB,EAAE,CAAC;CACnC;AAgJD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAClC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,GAChD,qBAAqB,CAsEvB"}
@@ -1,92 +1,59 @@
1
1
  import { isModuleKind } from "./module-kinds.js";
2
+ import { compareParsedModuleVersions, parseModuleVersion, } from "./module-version-order.js";
2
3
  import { DiagnosticSeverity } from "./types.js";
3
4
  const SOURCE = "telo-analyzer";
4
- /** Parse `X.Y.Z`, `vX.Y.Z`, or `X.Y.Z-pre.1`. Returns `null` for anything that
5
- * isn't a plain three-part numeric core an unparseable version forces the
6
- * group onto the conflict path (we never silently hoist across a version we
7
- * can't reason about). Pure: no dependency on the `semver` package, so the
8
- * analyzer stays browser-safe and dependency-free. */
9
- function parseVersion(raw) {
10
- if (typeof raw !== "string")
5
+ /** The location identity of an import ref: the ref with its version stripped.
6
+ * Two refs share an identity when they address the same module at different
7
+ * versions, whatever transport owns them:
8
+ *
9
+ * "std/kv-store@0.3.0" → "std/kv-store"
10
+ * "oci://ghcr.io/acme/s3@1.2.0" → "oci://ghcr.io/acme/s3"
11
+ * "https://x.com/lib/telo.yaml" → itself (a URL carries no version)
12
+ *
13
+ * Returns `null` for a relative path, which addresses one file on the
14
+ * publisher's disk and is therefore not a cross-import key: two local libraries
15
+ * that merely agree on `metadata.name` are distinct modules, and reconciling
16
+ * them would drop one and break its kinds. The same local file reached via two
17
+ * paths is already collapsed by canonical-source dedup.
18
+ *
19
+ * **What this key cannot relate.** It compares ref *spellings*, so it groups by
20
+ * origin exactly and nothing else. Two consequences, both accepted:
21
+ *
22
+ * - A module imported once by a registry ref and once by a relative path is two
23
+ * groups, so a version skew between them is not hoisted. Keying on what the
24
+ * module declares about itself would catch that case, but only by trusting a
25
+ * self-declared identity — which is what this change removes, and which
26
+ * cannot tell two same-named modules from different origins apart.
27
+ * - A bare `std/kv-store@0.4.0` and the equivalent direct
28
+ * `https://<registry>/std/kv-store/0.4.0/telo.yaml` are two groups. Relating
29
+ * them needs the configured registry base, which this pure, browser-safe
30
+ * function does not have. */
31
+ function refIdentity(ref) {
32
+ const base = ref.split("#")[0];
33
+ if (!base || base.startsWith(".") || base.startsWith("/") || base.startsWith("file:")) {
11
34
  return null;
12
- const v = raw.startsWith("v") ? raw.slice(1) : raw;
13
- const [core, ...preParts] = v.split("-");
14
- const pre = preParts.length > 0 ? preParts.join("-") : null;
15
- const segments = core.split(".");
16
- if (segments.length !== 3)
17
- return null;
18
- const [major, minor, patch] = segments.map((s) => {
19
- if (!/^\d+$/.test(s))
20
- return NaN;
21
- return Number(s);
22
- });
23
- if ([major, minor, patch].some((n) => Number.isNaN(n)))
24
- return null;
25
- return { major, minor, patch, pre: pre === null ? null : pre.split(".") };
26
- }
27
- /** SemVer precedence: numeric core, then a release outranks a prerelease, then
28
- * prerelease identifiers compared field-by-field (numeric < non-numeric per
29
- * spec, shorter set loses when all shared fields are equal). */
30
- function compareVersions(a, b) {
31
- if (a.major !== b.major)
32
- return a.major - b.major;
33
- if (a.minor !== b.minor)
34
- return a.minor - b.minor;
35
- if (a.patch !== b.patch)
36
- return a.patch - b.patch;
37
- if (a.pre === null && b.pre === null)
38
- return 0;
39
- if (a.pre === null)
40
- return 1;
41
- if (b.pre === null)
42
- return -1;
43
- const len = Math.max(a.pre.length, b.pre.length);
44
- for (let i = 0; i < len; i++) {
45
- const ai = a.pre[i];
46
- const bi = b.pre[i];
47
- if (ai === undefined)
48
- return -1;
49
- if (bi === undefined)
50
- return 1;
51
- const an = /^\d+$/.test(ai);
52
- const bn = /^\d+$/.test(bi);
53
- if (an && bn) {
54
- const d = Number(ai) - Number(bi);
55
- if (d !== 0)
56
- return d;
57
- }
58
- else if (an !== bn) {
59
- return an ? -1 : 1;
60
- }
61
- else if (ai !== bi) {
62
- return ai < bi ? -1 : 1;
63
- }
64
35
  }
65
- return 0;
36
+ const lastSlash = base.lastIndexOf("/");
37
+ const at = base.lastIndexOf("@");
38
+ return at > lastSlash && at > 0 ? base.slice(0, at) : base;
66
39
  }
67
- /** Read a loaded module's `namespace/name` identity, version, and raw owner
68
- * text. Returns `null` for modules without a namespace: only a registry
69
- * identity (`<namespace>/<name>`) is a stable cross-import key. Two namespace-
70
- * less local libraries that merely share a `metadata.name` are distinct modules
71
- * reached via distinct source URLs — reconciling them would drop one and break
72
- * its kinds; the same local file reached via two paths is already collapsed by
73
- * canonical-source dedup, so there is nothing left to reconcile here. */
74
- function moduleIdentityOf(mod) {
40
+ /** Read a loaded module's version and raw owner text under the location
41
+ * identity the import edge reached it by. The identity comes from the ref, not
42
+ * from anything the module declares about itself a module's own metadata
43
+ * cannot distinguish two same-named modules published to different origins. */
44
+ function moduleIdentityOf(mod, identity) {
75
45
  const doc = mod.owner.manifests.find((m) => m && isModuleKind(m.kind));
76
46
  if (!doc)
77
47
  return null;
78
48
  const meta = doc.metadata;
79
- const name = meta?.name;
80
- if (typeof name !== "string" || name.length === 0)
81
- return null;
82
- if (typeof meta.namespace !== "string" || meta.namespace.length === 0)
49
+ if (typeof meta?.name !== "string" || meta.name.length === 0)
83
50
  return null;
84
51
  const version = typeof meta.version === "string" ? meta.version : "";
85
52
  return {
86
53
  source: mod.owner.source,
87
- identity: `${meta.namespace}/${name}`,
54
+ identity,
88
55
  version,
89
- parsed: parseVersion(version),
56
+ parsed: parseModuleVersion(version),
90
57
  text: mod.owner.text,
91
58
  };
92
59
  }
@@ -106,7 +73,7 @@ function resolveGroup(members) {
106
73
  return best;
107
74
  if (!best.parsed)
108
75
  return cur;
109
- const cmp = compareVersions(cur.parsed, best.parsed);
76
+ const cmp = compareParsedModuleVersions(cur.parsed, best.parsed);
110
77
  if (cmp > 0)
111
78
  return cur;
112
79
  if (cmp === 0 && cur.source < best.source)
@@ -154,8 +121,8 @@ function hoistDiagnostic(identity, importerSource, alias, loser, winner, conflic
154
121
  return null;
155
122
  }
156
123
  /**
157
- * Reconcile a loaded import graph so each module identity (`namespace/name`)
158
- * resolves to a single version. Within a shared major the highest version wins
124
+ * Reconcile a loaded import graph so each module location (an import ref minus
125
+ * its version) resolves to a single version. Within a shared major the highest version wins
159
126
  * (a non-lossy hoist, given Telo's additive-only pre-1.0 policy); a major
160
127
  * mismatch is a hard conflict. Mutates `importEdges` in place — every edge that
161
128
  * pointed at a losing source is repointed at the winner — so `flattenForAnalyzer`
@@ -165,10 +132,33 @@ function hoistDiagnostic(identity, importerSource, alias, loser, winner, conflic
165
132
  export function reconcileModuleVersions(modules, importEdges) {
166
133
  const overrides = new Map();
167
134
  const diagnostics = [];
135
+ // Location identity per resolved module, taken from the ref that reached it.
136
+ // Two refs at different versions resolve to different canonical sources, so a
137
+ // source normally maps to exactly one identity; the entry module has no
138
+ // inbound edge and needs none (it is never reconciled against itself).
139
+ //
140
+ // One source CAN be reached by two spellings of the same location (a bare
141
+ // registry ref and the direct URL it resolves to). Both name the same module
142
+ // at the same version, so either identity groups it correctly — but the choice
143
+ // must not depend on edge iteration order, or the same graph could reconcile
144
+ // differently across runs. First edge wins.
145
+ const identityBySource = new Map();
146
+ for (const aliasMap of importEdges.values()) {
147
+ for (const edge of aliasMap.values()) {
148
+ if (identityBySource.has(edge.targetSource))
149
+ continue;
150
+ const identity = refIdentity(edge.targetRef);
151
+ if (identity)
152
+ identityBySource.set(edge.targetSource, identity);
153
+ }
154
+ }
168
155
  const groups = new Map();
169
156
  const infoBySource = new Map();
170
- for (const mod of modules.values()) {
171
- const info = moduleIdentityOf(mod);
157
+ for (const [source, mod] of modules) {
158
+ const identity = identityBySource.get(source);
159
+ if (!identity)
160
+ continue;
161
+ const info = moduleIdentityOf(mod, identity);
172
162
  if (!info)
173
163
  continue;
174
164
  infoBySource.set(info.source, info);
@@ -1,6 +1,7 @@
1
1
  /** An entry for a field that carries one or more x-telo-ref constraints. */
2
2
  export interface RefFieldEntry {
3
- /** One or more canonical ref strings ("namespace/module#TypeName" or "telo#TypeName").
3
+ /** One or more canonical kind keys ("<module>.<Kind>"), or the deprecated
4
+ * identity form ("<namespace>/<module>#<Kind>") for a legacy published module.
4
5
  * Multiple entries arise from anyOf branches. */
5
6
  refs: string[];
6
7
  /** True when the field path traversed through at least one array (path contains "[]"). */
@@ -1 +1 @@
1
- {"version":3,"file":"reference-field-map.d.ts","sourceRoot":"","sources":["../src/reference-field-map.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B;sDACkD;IAClD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,0FAA0F;IAC1F,OAAO,EAAE,OAAO,CAAC;IACjB;8DAC0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B;;;;;;;;;;kCAU8B;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B;2CACuC;IACvC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED;8CAC8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC;;qFAEiF;IACjF,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAEnF;0FAC0F;AAC1F,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE3D,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,aAAa,CAEvE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,eAAe,CAE3E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,oBAAoB,CAErF;AAED,oGAAoG;AACpG,eAAO,MAAM,cAAc,aAAwC,CAAC;AAEpE;;;;;;;;;2EAS2E;AAC3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAItE;AAED;;;;sEAIsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;wDAUwD;AACxD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAoCpF;AAED;+DAC+D;AAC/D,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAExE;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,iBAAiB,CAQrF;AASD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE,CAa/D;AAED;;;8CAG8C;AAC9C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,UAAU,EAAE,MAAM,GACjB,iBAAiB,CAInB"}
1
+ {"version":3,"file":"reference-field-map.d.ts","sourceRoot":"","sources":["../src/reference-field-map.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B;;sDAEkD;IAClD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,0FAA0F;IAC1F,OAAO,EAAE,OAAO,CAAC;IACjB;8DAC0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B;;;;;;;;;;kCAU8B;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B;2CACuC;IACvC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED;8CAC8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC;;qFAEiF;IACjF,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAEnF;0FAC0F;AAC1F,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE3D,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,aAAa,CAEvE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,eAAe,CAE3E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,oBAAoB,CAErF;AAED,oGAAoG;AACpG,eAAO,MAAM,cAAc,aAAwC,CAAC;AAEpE;;;;;;;;;2EAS2E;AAC3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAItE;AAED;;;;sEAIsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;wDAUwD;AACxD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAoCpF;AAED;+DAC+D;AAC/D,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAExE;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,iBAAiB,CAQrF;AASD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE,CAa/D;AAED;;;8CAG8C;AAC9C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,UAAU,EAAE,MAAM,GACjB,iBAAiB,CAInB"}
@@ -0,0 +1,62 @@
1
+ import type { ResourceManifest } from "@telorun/sdk";
2
+ import type { AliasResolver } from "./alias-resolver.js";
3
+ /** Why an `x-telo-ref` constraint did not canonicalize.
4
+ *
5
+ * - `legacy` — the deprecated `<namespace>/<module>#<Kind>` identity form,
6
+ * still resolved through the identity table.
7
+ * - `unknown` — the prefix is not an alias in the declaring module's scope: a
8
+ * typo, a missing `imports:` entry — or a value that was already
9
+ * canonical, which the caller separates by asking the registry.
10
+ * - `gated` — the alias is known and the target owns the kind, but its
11
+ * `exports.kinds` does not list it. */
12
+ export type RefConstraintReason = "legacy" | "unknown" | "gated";
13
+ /** An `x-telo-ref` constraint that did not canonicalize. */
14
+ export interface RefConstraintIssue {
15
+ /** The constraint string exactly as authored. */
16
+ ref: string;
17
+ /** Dotted path to the annotated schema node within the doc, e.g.
18
+ * `schema.properties.store`. Points the author at the slot, not just the doc. */
19
+ path: string;
20
+ /** The `Telo.Definition` / `Telo.Abstract` doc that declares the slot. */
21
+ manifest: ResourceManifest;
22
+ reason: RefConstraintReason;
23
+ /** For `gated`: the target module and the kinds it does export. */
24
+ gate?: {
25
+ module: string;
26
+ exported: string[];
27
+ };
28
+ /** Aliases the declaring scope does know — the "did you mean" material for
29
+ * an `unknown` prefix. */
30
+ knownAliases?: string[];
31
+ }
32
+ /** True for the legacy identity form, which is split on `#` by the identity table. */
33
+ export declare function isLegacyRefIdentity(ref: string): boolean;
34
+ /**
35
+ * Rewrites the `x-telo-ref` constraints on one definition doc from the alias
36
+ * form (`KvStore.Store`, `Self.Store`, `Telo.Invocable`) to the canonical
37
+ * `<module>.<Kind>` key the definition registry is keyed by.
38
+ *
39
+ * `resolver` must be the scope of the module that DECLARES the definition, not
40
+ * the consumer's: an imported library names its dependencies by its own aliases.
41
+ * This mirrors how `extends:` and `capability:` are pre-resolved before
42
+ * registration, so the registry never needs module context to answer a ref query.
43
+ *
44
+ * Every constraint that does not canonicalize is returned, tagged with why. That
45
+ * matters more than it looks: an unresolved constraint leaves a string naming no
46
+ * registered kind, and the reference check treats an unknown target as partial
47
+ * context and skips it — so an unreported one would silently let the slot accept
48
+ * anything. The authored value is left in place either way, so a diagnostic
49
+ * quotes what the author actually wrote.
50
+ *
51
+ * An `unknown` result also covers an already-canonical value (`kv-store.Store`
52
+ * names a module, not an alias), which is what keeps the rewrite idempotent —
53
+ * the caller drops those by checking the definition registry once every kind is
54
+ * registered.
55
+ *
56
+ * The walk covers the whole doc rather than a fixed field list: a constraint can
57
+ * sit in `schema`, `inputType`, `outputType`, or a `$defs` entry nested in any of
58
+ * them, and rewriting one that appears in a template body's inline schema is
59
+ * correct too.
60
+ */
61
+ export declare function resolveSchemaRefKinds(definition: ResourceManifest, resolver: Pick<AliasResolver, "resolveKindResult" | "knownAliases">): RefConstraintIssue[];
62
+ //# sourceMappingURL=resolve-schema-ref-kinds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-schema-ref-kinds.d.ts","sourceRoot":"","sources":["../src/resolve-schema-ref-kinds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD;;;;;;;;sDAQsD;AACtD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAEjE,4DAA4D;AAC5D,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ;sFACkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,mEAAmE;IACnE,IAAI,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC9C;+BAC2B;IAC3B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,gBAAgB,EAC5B,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,mBAAmB,GAAG,cAAc,CAAC,GAClE,kBAAkB,EAAE,CAiDtB"}
@@ -0,0 +1,81 @@
1
+ const REF_ANNOTATION = "x-telo-ref";
2
+ /** True for the legacy identity form, which is split on `#` by the identity table. */
3
+ export function isLegacyRefIdentity(ref) {
4
+ return ref.includes("#");
5
+ }
6
+ /**
7
+ * Rewrites the `x-telo-ref` constraints on one definition doc from the alias
8
+ * form (`KvStore.Store`, `Self.Store`, `Telo.Invocable`) to the canonical
9
+ * `<module>.<Kind>` key the definition registry is keyed by.
10
+ *
11
+ * `resolver` must be the scope of the module that DECLARES the definition, not
12
+ * the consumer's: an imported library names its dependencies by its own aliases.
13
+ * This mirrors how `extends:` and `capability:` are pre-resolved before
14
+ * registration, so the registry never needs module context to answer a ref query.
15
+ *
16
+ * Every constraint that does not canonicalize is returned, tagged with why. That
17
+ * matters more than it looks: an unresolved constraint leaves a string naming no
18
+ * registered kind, and the reference check treats an unknown target as partial
19
+ * context and skips it — so an unreported one would silently let the slot accept
20
+ * anything. The authored value is left in place either way, so a diagnostic
21
+ * quotes what the author actually wrote.
22
+ *
23
+ * An `unknown` result also covers an already-canonical value (`kv-store.Store`
24
+ * names a module, not an alias), which is what keeps the rewrite idempotent —
25
+ * the caller drops those by checking the definition registry once every kind is
26
+ * registered.
27
+ *
28
+ * The walk covers the whole doc rather than a fixed field list: a constraint can
29
+ * sit in `schema`, `inputType`, `outputType`, or a `$defs` entry nested in any of
30
+ * them, and rewriting one that appears in a template body's inline schema is
31
+ * correct too.
32
+ */
33
+ export function resolveSchemaRefKinds(definition, resolver) {
34
+ const issues = [];
35
+ const record = (ref, path) => {
36
+ if (isLegacyRefIdentity(ref)) {
37
+ issues.push({ ref, path, manifest: definition, reason: "legacy" });
38
+ return;
39
+ }
40
+ const result = resolver.resolveKindResult(ref);
41
+ if (result.status === "ok")
42
+ return;
43
+ issues.push(result.status === "gated"
44
+ ? {
45
+ ref,
46
+ path,
47
+ manifest: definition,
48
+ reason: "gated",
49
+ gate: { module: result.module, exported: result.exported },
50
+ }
51
+ : {
52
+ ref,
53
+ path,
54
+ manifest: definition,
55
+ reason: "unknown",
56
+ knownAliases: resolver.knownAliases(),
57
+ });
58
+ };
59
+ const walk = (value, path) => {
60
+ if (value === null || typeof value !== "object")
61
+ return;
62
+ if (Array.isArray(value)) {
63
+ value.forEach((item, i) => walk(item, `${path}[${i}]`));
64
+ return;
65
+ }
66
+ const obj = value;
67
+ const ref = obj[REF_ANNOTATION];
68
+ if (typeof ref === "string" && ref) {
69
+ const result = isLegacyRefIdentity(ref) ? null : resolver.resolveKindResult(ref);
70
+ if (result?.status === "ok")
71
+ obj[REF_ANNOTATION] = result.kind;
72
+ else
73
+ record(ref, path);
74
+ }
75
+ for (const key of Object.keys(obj)) {
76
+ walk(obj[key], path ? `${path}.${key}` : key);
77
+ }
78
+ };
79
+ walk(definition, "");
80
+ return issues;
81
+ }
@@ -7,16 +7,26 @@ export declare const MANIFEST_CACHE_BASE_URL = "https://manifests.telo.sh";
7
7
  * stores per version — `{ transport, host, path, version }` — so the tracker's
8
8
  * write key and the editor's read key come from the same function and never
9
9
  * drift. `path` is the slash-separated repo/module path (multi-segment OCI
10
- * repos nest as prefixes). */
10
+ * repos nest as prefixes), empty for a module addressed at a host's root. */
11
11
  export interface ManifestCacheCoords {
12
12
  transport: string;
13
13
  host: string;
14
14
  path: string;
15
- version: string;
15
+ /** Omit only when the ref carries no version to key by — a direct `https://`
16
+ * URL addresses exactly one file, so its path alone is already unambiguous.
17
+ * Every ref whose grammar has a version segment must supply it, or two
18
+ * versions of one module would collide on a single key. */
19
+ version?: string;
20
+ /** File within the version directory. Defaults to the module manifest, which
21
+ * is the only file the hub's bucket stores; the local install cache also
22
+ * holds each `include:` partial, which is named here. */
23
+ file?: string;
16
24
  }
17
25
  /** Deterministic cache key for one module version:
18
- * `<transport>/<host>/<path…>/<version>/telo.yaml`. Returns `null` when any
19
- * coordinate is empty or would traverse out of the key space. */
26
+ * `<transport>/<host>/<path…>/<version>/<file>`, where `version` is omitted
27
+ * when the coordinates carry none and `file` defaults to the module manifest.
28
+ * Returns `null` when any coordinate is empty or would traverse out of the key
29
+ * space. */
20
30
  export declare function manifestCacheKey(coords: ManifestCacheCoords): string | null;
21
31
  /** Cache coordinates for an `oci://host/repo@tag` ref. Returns `null` when the
22
32
  * ref carries no explicit tag (a defaulted `latest` or a `sha256:` digest is
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-cache.d.ts","sourceRoot":"","sources":["../../src/sources/manifest-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKlD;;iFAEiF;AACjF,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAEnE;;;;+BAI+B;AAC/B,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAOD;;kEAEkE;AAClE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,GAAG,IAAI,CAO3E;AAED;;;6EAG6E;AAC7E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAY9E;AAID;;2DAE2D;AAC3D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;;;oEASoE;AACpE,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAuB/F;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,GAAG,mBAAmB,EACzC,OAAO,GAAE,MAAgC,GACxC,MAAM,GAAG,IAAI,CAKf;AAED;;;;;+DAK+D;AAC/D,qBAAa,mBAAoB,YAAW,cAAc;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,MAAgC;IAEtE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAQxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAalE;;;iEAG6D;IAC7D,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;CAOxD"}
1
+ {"version":3,"file":"manifest-cache.d.ts","sourceRoot":"","sources":["../../src/sources/manifest-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKlD;;iFAEiF;AACjF,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAEnE;;;;8EAI8E;AAC9E,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;gEAG4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;8DAE0D;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAOD;;;;aAIa;AACb,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,GAAG,IAAI,CAe3E;AAED;;;6EAG6E;AAC7E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAY9E;AAID;;2DAE2D;AAC3D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;;;oEASoE;AACpE,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAuB/F;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,GAAG,mBAAmB,EACzC,OAAO,GAAE,MAAgC,GACxC,MAAM,GAAG,IAAI,CAKf;AAED;;;;;+DAK+D;AAC/D,qBAAa,mBAAoB,YAAW,cAAc;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,MAAgC;IAEtE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAQxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAalE;;;iEAG6D;IAC7D,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;CAOxD"}