@telorun/analyzer 0.35.0 → 0.37.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.
- package/dist/alias-resolver.d.ts +31 -2
- package/dist/alias-resolver.d.ts.map +1 -1
- package/dist/alias-resolver.js +39 -13
- package/dist/analysis-registry.d.ts +6 -1
- package/dist/analysis-registry.d.ts.map +1 -1
- package/dist/analysis-registry.js +7 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +39 -7
- package/dist/flatten-for-analyzer.d.ts +20 -1
- package/dist/flatten-for-analyzer.d.ts.map +1 -1
- package/dist/flatten-for-analyzer.js +45 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/sources/manifest-cache.d.ts +15 -0
- package/dist/sources/manifest-cache.d.ts.map +1 -1
- package/dist/sources/manifest-cache.js +43 -0
- package/package.json +2 -2
- package/src/alias-resolver.ts +46 -13
- package/src/analysis-registry.ts +9 -1
- package/src/analyzer.ts +42 -7
- package/src/flatten-for-analyzer.ts +52 -5
- package/src/index.ts +3 -0
- package/src/sources/manifest-cache.ts +42 -0
package/dist/alias-resolver.d.ts
CHANGED
|
@@ -7,7 +7,16 @@ export declare class AliasResolver {
|
|
|
7
7
|
* transitively RE-EXPORTS (`exports.kinds: [Alias.Kind]`), which don't live in the
|
|
8
8
|
* import's own module. Resolved before the normal `<module>.<suffix>` construction. */
|
|
9
9
|
private readonly reExportedKinds;
|
|
10
|
-
|
|
10
|
+
/** Register an import alias gated to the target's `exports.kinds`. Only listed kinds
|
|
11
|
+
* resolve; an empty list exports nothing. `exportedKinds` is `undefined` for exactly one
|
|
12
|
+
* case — the target declares no `exports.kinds` at all (the legacy permissive default,
|
|
13
|
+
* kept so already-published module versions stay importable), which is the site to delete
|
|
14
|
+
* when kinds go private. For an alias crossing no import boundary use
|
|
15
|
+
* `registerUngatedAlias`. Mirrors `ModuleContext.registerImport`. */
|
|
16
|
+
registerImport(alias: string, targetModule: string, exportedKinds?: readonly string[]): void;
|
|
17
|
+
/** Register an alias that crosses no import boundary and is therefore never gated —
|
|
18
|
+
* `Self`, a library resolving its own kinds. See `ModuleContext.registerUngatedAlias`. */
|
|
19
|
+
registerUngatedAlias(alias: string, targetModule: string): void;
|
|
11
20
|
/** Register that `<alias>.<suffix>` re-exports the kind canonically named `canonicalKind`
|
|
12
21
|
* (owned by a module the alias's target imports, possibly several hops away). */
|
|
13
22
|
registerKindReExport(alias: string, suffix: string, canonicalKind: string): void;
|
|
@@ -17,7 +26,27 @@ export declare class AliasResolver {
|
|
|
17
26
|
* upstream by `flattenForAnalyzer` (only exported instances are forwarded), so a name
|
|
18
27
|
* that isn't exported simply won't be found. */
|
|
19
28
|
moduleForAlias(alias: string): string | undefined;
|
|
20
|
-
/**
|
|
29
|
+
/** Resolve an alias-qualified kind, reporting WHY it failed. One parse, one gate check —
|
|
30
|
+
* callers that need the reason and callers that only need the name share this, so they
|
|
31
|
+
* cannot drift on dot-splitting, re-export precedence, or gate order.
|
|
32
|
+
*
|
|
33
|
+
* - `ok` — resolved to a canonical `<module>.<Kind>`.
|
|
34
|
+
* - `gated` — the alias is known and the target owns the name, but its `exports.kinds`
|
|
35
|
+
* does not list it. A distinct outcome so callers can say "not exported"
|
|
36
|
+
* instead of the misleading "no such kind".
|
|
37
|
+
* - `unknown` — unqualified kind, or an alias this scope never imported. */
|
|
38
|
+
resolveKindResult(kind: string): {
|
|
39
|
+
status: "ok";
|
|
40
|
+
kind: string;
|
|
41
|
+
} | {
|
|
42
|
+
status: "gated";
|
|
43
|
+
module: string;
|
|
44
|
+
exported: string[];
|
|
45
|
+
} | {
|
|
46
|
+
status: "unknown";
|
|
47
|
+
};
|
|
48
|
+
/** Resolves "Http.Api" → "http-server.Api". Undefined if the alias is unknown OR the kind
|
|
49
|
+
* is gated out — use `resolveKindResult` when the difference matters. */
|
|
21
50
|
resolveKind(kind: string): string | undefined;
|
|
22
51
|
hasAlias(alias: string): boolean;
|
|
23
52
|
knownAliases(): string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alias-resolver.d.ts","sourceRoot":"","sources":["../src/alias-resolver.ts"],"names":[],"mappings":"AAAA;gFACgF;AAChF,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAChE;;4FAEwF;IACxF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6B;IAE7D,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"alias-resolver.d.ts","sourceRoot":"","sources":["../src/alias-resolver.ts"],"names":[],"mappings":"AAAA;gFACgF;AAChF,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAChE;;4FAEwF;IACxF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6B;IAE7D;;;;;0EAKsE;IACtE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI;IAO5F;+FAC2F;IAC3F,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAI/D;sFACkF;IAClF,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI;IAIhF;;;;qDAIiD;IACjD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIjD;;;;;;;;iFAQ6E;IAC7E,iBAAiB,CACf,IAAI,EAAE,MAAM,GAEV;QAAE,MAAM,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAC9B;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,GACvD;QAAE,MAAM,EAAE,SAAS,CAAA;KAAE;IAoBzB;8EAC0E;IAC1E,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAK7C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIhC,YAAY,IAAI,MAAM,EAAE;IAIxB;;qEAEiE;IACjE,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE;CAO3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC1C,aAAa,GAAG,SAAS,CAI3B"}
|
package/dist/alias-resolver.js
CHANGED
|
@@ -7,12 +7,23 @@ export class AliasResolver {
|
|
|
7
7
|
* transitively RE-EXPORTS (`exports.kinds: [Alias.Kind]`), which don't live in the
|
|
8
8
|
* import's own module. Resolved before the normal `<module>.<suffix>` construction. */
|
|
9
9
|
reExportedKinds = new Map();
|
|
10
|
+
/** Register an import alias gated to the target's `exports.kinds`. Only listed kinds
|
|
11
|
+
* resolve; an empty list exports nothing. `exportedKinds` is `undefined` for exactly one
|
|
12
|
+
* case — the target declares no `exports.kinds` at all (the legacy permissive default,
|
|
13
|
+
* kept so already-published module versions stay importable), which is the site to delete
|
|
14
|
+
* when kinds go private. For an alias crossing no import boundary use
|
|
15
|
+
* `registerUngatedAlias`. Mirrors `ModuleContext.registerImport`. */
|
|
10
16
|
registerImport(alias, targetModule, exportedKinds) {
|
|
11
17
|
this.importAliases.set(alias, targetModule);
|
|
12
|
-
if (exportedKinds
|
|
18
|
+
if (exportedKinds !== undefined) {
|
|
13
19
|
this.importedKinds.set(alias, new Set(exportedKinds));
|
|
14
20
|
}
|
|
15
21
|
}
|
|
22
|
+
/** Register an alias that crosses no import boundary and is therefore never gated —
|
|
23
|
+
* `Self`, a library resolving its own kinds. See `ModuleContext.registerUngatedAlias`. */
|
|
24
|
+
registerUngatedAlias(alias, targetModule) {
|
|
25
|
+
this.importAliases.set(alias, targetModule);
|
|
26
|
+
}
|
|
16
27
|
/** Register that `<alias>.<suffix>` re-exports the kind canonically named `canonicalKind`
|
|
17
28
|
* (owned by a module the alias's target imports, possibly several hops away). */
|
|
18
29
|
registerKindReExport(alias, suffix, canonicalKind) {
|
|
@@ -26,28 +37,43 @@ export class AliasResolver {
|
|
|
26
37
|
moduleForAlias(alias) {
|
|
27
38
|
return this.importAliases.get(alias);
|
|
28
39
|
}
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
/** Resolve an alias-qualified kind, reporting WHY it failed. One parse, one gate check —
|
|
41
|
+
* callers that need the reason and callers that only need the name share this, so they
|
|
42
|
+
* cannot drift on dot-splitting, re-export precedence, or gate order.
|
|
43
|
+
*
|
|
44
|
+
* - `ok` — resolved to a canonical `<module>.<Kind>`.
|
|
45
|
+
* - `gated` — the alias is known and the target owns the name, but its `exports.kinds`
|
|
46
|
+
* does not list it. A distinct outcome so callers can say "not exported"
|
|
47
|
+
* instead of the misleading "no such kind".
|
|
48
|
+
* - `unknown` — unqualified kind, or an alias this scope never imported. */
|
|
49
|
+
resolveKindResult(kind) {
|
|
50
|
+
if (!kind)
|
|
51
|
+
return { status: "unknown" };
|
|
34
52
|
const dot = kind.indexOf(".");
|
|
35
53
|
if (dot === -1)
|
|
36
|
-
return
|
|
54
|
+
return { status: "unknown" };
|
|
37
55
|
const prefix = kind.slice(0, dot);
|
|
38
56
|
const suffix = kind.slice(dot + 1);
|
|
39
57
|
// Re-export takes precedence: a re-exported kind resolves to its true owning module,
|
|
40
|
-
// not `${prefix-target}.${suffix}
|
|
58
|
+
// not `${prefix-target}.${suffix}`. It is listed in the re-exporting library's own
|
|
59
|
+
// `exports.kinds` (that is how it got here), so this is not a gate bypass.
|
|
41
60
|
const reExported = this.reExportedKinds.get(`${prefix}.${suffix}`);
|
|
42
61
|
if (reExported)
|
|
43
|
-
return reExported;
|
|
62
|
+
return { status: "ok", kind: reExported };
|
|
44
63
|
const realModule = this.importAliases.get(prefix);
|
|
45
64
|
if (!realModule)
|
|
46
|
-
return
|
|
65
|
+
return { status: "unknown" };
|
|
47
66
|
const allowed = this.importedKinds.get(prefix);
|
|
48
|
-
if (allowed !== undefined && !allowed.has(suffix))
|
|
49
|
-
return
|
|
50
|
-
|
|
67
|
+
if (allowed !== undefined && !allowed.has(suffix)) {
|
|
68
|
+
return { status: "gated", module: realModule, exported: [...allowed] };
|
|
69
|
+
}
|
|
70
|
+
return { status: "ok", kind: `${realModule}.${suffix}` };
|
|
71
|
+
}
|
|
72
|
+
/** Resolves "Http.Api" → "http-server.Api". Undefined if the alias is unknown OR the kind
|
|
73
|
+
* is gated out — use `resolveKindResult` when the difference matters. */
|
|
74
|
+
resolveKind(kind) {
|
|
75
|
+
const r = this.resolveKindResult(kind);
|
|
76
|
+
return r.status === "ok" ? r.kind : undefined;
|
|
51
77
|
}
|
|
52
78
|
hasAlias(alias) {
|
|
53
79
|
return this.importAliases.has(alias);
|
|
@@ -29,7 +29,12 @@ export declare class AnalysisRegistry {
|
|
|
29
29
|
private readonly aliasesByModule;
|
|
30
30
|
registerDefinition(def: ResourceDefinition): void;
|
|
31
31
|
registerModuleIdentity(namespace: string | null, name: string): void;
|
|
32
|
-
|
|
32
|
+
/** `kinds` is the target's `exports.kinds` gate — only listed kinds resolve, and an
|
|
33
|
+
* empty list exports nothing. Omit it only for a target declaring no `exports.kinds`
|
|
34
|
+
* (the legacy permissive default); see `AliasResolver.registerImport`. */
|
|
35
|
+
registerImport(alias: string, target: string, kinds?: readonly string[]): void;
|
|
36
|
+
/** An alias crossing no import boundary, never gated — `Self`, the `Telo` built-ins. */
|
|
37
|
+
registerUngatedAlias(alias: string, target: string): void;
|
|
33
38
|
resolveKind(kind: string): string | undefined;
|
|
34
39
|
/**
|
|
35
40
|
* Iterates a resource's reference and scope fields as declared by its definition.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analysis-registry.d.ts","sourceRoot":"","sources":["../src/analysis-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKzE,OAAO,EAAqC,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEhG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD;;wDAEwD;AACxD,MAAM,WAAW,YAAY;IAC3B;0CACsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;8EAG0E;IAC1E,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;GAIG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;IAEpE,kBAAkB,CAAC,GAAG,EAAE,kBAAkB,GAAG,IAAI;IAIjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIpE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"analysis-registry.d.ts","sourceRoot":"","sources":["../src/analysis-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,9 +20,16 @@ export class AnalysisRegistry {
|
|
|
20
20
|
registerModuleIdentity(namespace, name) {
|
|
21
21
|
this.defs.registerModuleIdentity(namespace, name);
|
|
22
22
|
}
|
|
23
|
+
/** `kinds` is the target's `exports.kinds` gate — only listed kinds resolve, and an
|
|
24
|
+
* empty list exports nothing. Omit it only for a target declaring no `exports.kinds`
|
|
25
|
+
* (the legacy permissive default); see `AliasResolver.registerImport`. */
|
|
23
26
|
registerImport(alias, target, kinds) {
|
|
24
27
|
this.aliases.registerImport(alias, target, kinds);
|
|
25
28
|
}
|
|
29
|
+
/** An alias crossing no import boundary, never gated — `Self`, the `Telo` built-ins. */
|
|
30
|
+
registerUngatedAlias(alias, target) {
|
|
31
|
+
this.aliases.registerUngatedAlias(alias, target);
|
|
32
|
+
}
|
|
26
33
|
resolveKind(kind) {
|
|
27
34
|
return this.aliases.resolveKind(kind);
|
|
28
35
|
}
|
package/dist/analyzer.d.ts.map
CHANGED
|
@@ -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;AAKzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAoB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAuxB/F,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,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,GAC1B,kBAAkB,EAAE;
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAoB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAuxB/F,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,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,GAC1B,kBAAkB,EAAE;IAm1BvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAMvB,SAAS,CACP,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,EAI1B,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,GACtC,gBAAgB,EAAE;IAmBrB,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
|
@@ -735,15 +735,14 @@ export class StaticAnalyzer {
|
|
|
735
735
|
// Lets same-library `extends:` work (e.g. `extends: Self.Encoder` for a
|
|
736
736
|
// concrete kind whose abstract lives in the same Telo.Library) without
|
|
737
737
|
// requiring a self-import (which would loop the loader). Resolves
|
|
738
|
-
// through the same alias machinery as user-declared Telo.Imports
|
|
739
|
-
// honours the library's `exports.kinds` list, no special cases.
|
|
738
|
+
// through the same alias machinery as user-declared Telo.Imports.
|
|
740
739
|
if (moduleName) {
|
|
741
740
|
// `Self` resolves the library's own kinds UNGATED — a library may reference
|
|
742
741
|
// its own kinds regardless of `exports.kinds`, which gates importers, not
|
|
743
742
|
// internal use. This is what lets a library declare an instance of a kind it
|
|
744
743
|
// does not export (e.g. console's `writeLine`) to enforce a singleton.
|
|
745
744
|
if (rootModules.has(moduleName)) {
|
|
746
|
-
aliases.
|
|
745
|
+
aliases.registerUngatedAlias("Self", moduleName);
|
|
747
746
|
}
|
|
748
747
|
else {
|
|
749
748
|
let libResolver = aliasesByModule.get(moduleName);
|
|
@@ -751,14 +750,19 @@ export class StaticAnalyzer {
|
|
|
751
750
|
libResolver = new AliasResolver();
|
|
752
751
|
aliasesByModule.set(moduleName, libResolver);
|
|
753
752
|
}
|
|
754
|
-
libResolver.
|
|
753
|
+
libResolver.registerUngatedAlias("Self", moduleName);
|
|
755
754
|
}
|
|
756
755
|
}
|
|
757
756
|
}
|
|
758
757
|
if (m.kind === "Telo.Import") {
|
|
759
758
|
const alias = m.metadata.name;
|
|
760
759
|
const source = m.source;
|
|
761
|
-
|
|
760
|
+
// The target's `exports.kinds` gate, stamped onto this import by
|
|
761
|
+
// `stampExportedKinds` (the `Telo.Import` doc has no `exports` field of its own —
|
|
762
|
+
// the target `Telo.Library` doc it comes from is dropped for non-root modules).
|
|
763
|
+
// Undefined means the target declares no gate, i.e. unrestricted.
|
|
764
|
+
const exportedKinds = m.metadata
|
|
765
|
+
?.exportedKinds;
|
|
762
766
|
const resolvedModuleName = m.metadata.resolvedModuleName;
|
|
763
767
|
const resolvedNamespace = m.metadata.resolvedNamespace;
|
|
764
768
|
const ownModule = m.metadata?.module;
|
|
@@ -833,7 +837,7 @@ export class StaticAnalyzer {
|
|
|
833
837
|
aliasesByModule.set(ownModule, libResolver);
|
|
834
838
|
}
|
|
835
839
|
if (!libResolver.hasAlias("Self")) {
|
|
836
|
-
libResolver.
|
|
840
|
+
libResolver.registerUngatedAlias("Self", ownModule);
|
|
837
841
|
}
|
|
838
842
|
}
|
|
839
843
|
// Register definitions from Telo.Definition AND Telo.Abstract resources.
|
|
@@ -1042,7 +1046,35 @@ export class StaticAnalyzer {
|
|
|
1042
1046
|
// reference-resolution paths: own-module scope first, root/consumer aliases last.
|
|
1043
1047
|
const ownModule = m.metadata?.module;
|
|
1044
1048
|
const scopeResolver = scopeResolverForModule(ownModule, rootModules, aliasesByModule);
|
|
1045
|
-
|
|
1049
|
+
// Either scope resolving it wins; otherwise a gate rejection is more specific than
|
|
1050
|
+
// "unknown alias", so surface that.
|
|
1051
|
+
const scoped = scopeResolver?.resolveKindResult(m.kind);
|
|
1052
|
+
const rooted = aliases.resolveKindResult(m.kind);
|
|
1053
|
+
const kindResult = scoped?.status === "ok"
|
|
1054
|
+
? scoped
|
|
1055
|
+
: rooted.status === "ok"
|
|
1056
|
+
? rooted
|
|
1057
|
+
: scoped?.status === "gated"
|
|
1058
|
+
? scoped
|
|
1059
|
+
: rooted;
|
|
1060
|
+
// The gate is checked BEFORE any definition lookup. `defs` is keyed
|
|
1061
|
+
// `<metadata.module>.<Kind>`, so a library whose `metadata.name` equals the alias it
|
|
1062
|
+
// is imported under (e.g. module `Foo` imported as `Foo`) makes the raw `defs.resolve(m.kind)`
|
|
1063
|
+
// below hit directly — which would accept a kind the module does not export, leaving
|
|
1064
|
+
// the analyzer more permissive than the kernel.
|
|
1065
|
+
if (kindResult.status === "gated") {
|
|
1066
|
+
diagnostics.push({
|
|
1067
|
+
severity: DiagnosticSeverity.Error,
|
|
1068
|
+
code: "KIND_NOT_EXPORTED",
|
|
1069
|
+
source: SOURCE,
|
|
1070
|
+
message: `Kind '${m.kind}' is not exported by module '${kindResult.module}'. ` +
|
|
1071
|
+
`Add '${m.kind.slice(m.kind.indexOf(".") + 1)}' to that module's exports.kinds ` +
|
|
1072
|
+
`to make it importable. Exported kinds: ${kindResult.exported.join(", ") || "(none)"}.`,
|
|
1073
|
+
data: { resource, filePath, path: "kind" },
|
|
1074
|
+
});
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
const resolvedKind = kindResult.status === "ok" ? kindResult.kind : undefined;
|
|
1046
1078
|
const definition = defs.resolve(m.kind) ?? (resolvedKind ? defs.resolve(resolvedKind) : undefined);
|
|
1047
1079
|
if (!definition) {
|
|
1048
1080
|
const suggestedKind = computeSuggestKind(m.kind, aliases, defs);
|
|
@@ -82,7 +82,7 @@ export declare function forwardReExportManifests(result: ResourceManifest[], spe
|
|
|
82
82
|
* shared by the CLI graph path and the editor's workspace projection. */
|
|
83
83
|
export declare function resolveExportedKinds(modules: ReadonlyArray<{
|
|
84
84
|
module: string;
|
|
85
|
-
exportsKinds: readonly string[];
|
|
85
|
+
exportsKinds: readonly string[] | undefined;
|
|
86
86
|
}>, aliasToModule: (module: string, alias: string) => string | undefined): Map<string, Map<string, string>>;
|
|
87
87
|
/** Stamp `metadata.reExportedKinds` (suffix → canonical kind) onto every `Telo.Import` whose
|
|
88
88
|
* target re-exports kinds, so the analyzer can register the re-export mappings. Only entries
|
|
@@ -94,6 +94,25 @@ export declare function stampReExportedKinds(imports: ReadonlyArray<{
|
|
|
94
94
|
manifest: ResourceManifest;
|
|
95
95
|
targetModule: string;
|
|
96
96
|
}>, exportedKinds: Map<string, Map<string, string>>): void;
|
|
97
|
+
/** Stamp `metadata.exportedKinds` (the target's `exports.kinds` gate as bare suffixes) onto
|
|
98
|
+
* every `Telo.Import` whose target declares one, so the analyzer enforces the same gate the
|
|
99
|
+
* kernel does. Stamped on `metadata` for the same reason as `reExportedKinds` — the
|
|
100
|
+
* `Telo.Import` schema forbids extra top-level fields.
|
|
101
|
+
*
|
|
102
|
+
* The gate comes from the DECLARED entries, parsed exactly as the kernel parses them
|
|
103
|
+
* (`parseExportEntry` — `Alias.Kind` yields `Kind`), never from the resolved re-export
|
|
104
|
+
* table. That table drops a re-export whose source module declares no `exports.kinds`, so
|
|
105
|
+
* deriving the gate from it would reject `exports.kinds: [Cee.Thing]` — a gate the kernel
|
|
106
|
+
* honours — as "not exported".
|
|
107
|
+
*
|
|
108
|
+
* A target that declares no `exports.kinds` is absent from `gatedModules` and goes unstamped,
|
|
109
|
+
* which the analyzer reads as unrestricted. That is the legacy permissive default, kept so
|
|
110
|
+
* already-published module versions stay importable; making kinds private by default means
|
|
111
|
+
* stamping every loaded target instead, which breaks those versions (see import-controller). */
|
|
112
|
+
export declare function stampExportedKinds(imports: ReadonlyArray<{
|
|
113
|
+
manifest: ResourceManifest;
|
|
114
|
+
targetModule: string;
|
|
115
|
+
}>, declaredKinds: ReadonlyMap<string, readonly string[]>): void;
|
|
97
116
|
/** Project a LoadedModule (owner + partials) to a flat ResourceManifest[]
|
|
98
117
|
* with `metadata.module` stamped on non-module docs. The kernel's runtime
|
|
99
118
|
* entry load uses this to convert a `Loader.loadModule` result into the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flatten-for-analyzer.d.ts","sourceRoot":"","sources":["../src/flatten-for-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAInE;;yEAEyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;4CAI4C;AAC5C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAGjE;AAED;;;;;;;;;;;;;;;;;;;2CAmB2C;AAC3C,wBAAgB,gCAAgC,CAC9C,eAAe,EAAE,gBAAgB,EAAE,EACnC,MAAM,EAAE,OAAO,GACd,gBAAgB,EAAE,CA6BpB;AAED;;;;;;;;;;;;;;;;;;;6EAmB6E;AAC7E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,gBAAgB,EAAE,CAiDzE;AAED;;uBAEuB;AACvB,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;qEAEqE;AACrE,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,GAC/C,YAAY,EAAE,CAShB;AAED;;;;;;uFAMuF;AACvF,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,EAAE,EAC1B,KAAK,EAAE,SAAS,YAAY,EAAE,EAC9B,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GACnE,IAAI,CAgDN;AAED;;;;0EAI0E;AAC1E,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"flatten-for-analyzer.d.ts","sourceRoot":"","sources":["../src/flatten-for-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAInE;;yEAEyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;4CAI4C;AAC5C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAGjE;AAED;;;;;;;;;;;;;;;;;;;2CAmB2C;AAC3C,wBAAgB,gCAAgC,CAC9C,eAAe,EAAE,gBAAgB,EAAE,EACnC,MAAM,EAAE,OAAO,GACd,gBAAgB,EAAE,CA6BpB;AAED;;;;;;;;;;;;;;;;;;;6EAmB6E;AAC7E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,gBAAgB,EAAE,CAiDzE;AAED;;uBAEuB;AACvB,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;qEAEqE;AACrE,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,GAC/C,YAAY,EAAE,CAShB;AAED;;;;;;uFAMuF;AACvF,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,EAAE,EAC1B,KAAK,EAAE,SAAS,YAAY,EAAE,EAC9B,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GACnE,IAAI,CAgDN;AAED;;;;0EAI0E;AAC1E,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;CAAE,CAAC,EACvF,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GACnE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA2ClC;AAED;;;;;4DAK4D;AAC5D,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,CAAC;IAAE,QAAQ,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,EAC5E,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC9C,IAAI,CAWN;AAED;;;;;;;;;;;;;;iGAciG;AACjG,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,aAAa,CAAC;IAAE,QAAQ,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,EAC5E,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,GACpD,IAAI,CAMN;AAgDD;;;;;6BAK6B;AAC7B,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,YAAY,GAAG,gBAAgB,EAAE,CAEzE"}
|
|
@@ -205,6 +205,10 @@ export function forwardReExportManifests(result, specs, aliasToModule) {
|
|
|
205
205
|
* shared by the CLI graph path and the editor's workspace projection. */
|
|
206
206
|
export function resolveExportedKinds(modules, aliasToModule) {
|
|
207
207
|
const out = new Map();
|
|
208
|
+
// Modules declaring NO `exports.kinds` export every kind they define (the legacy permissive
|
|
209
|
+
// default), so they have no table to look a re-exported suffix up in. `[]` is a real gate
|
|
210
|
+
// that exports nothing — the two must stay distinguishable here.
|
|
211
|
+
const ungated = new Set(modules.filter((m) => m.exportsKinds === undefined).map((m) => m.module));
|
|
208
212
|
const tableFor = (m) => {
|
|
209
213
|
let t = out.get(m);
|
|
210
214
|
if (!t)
|
|
@@ -215,7 +219,7 @@ export function resolveExportedKinds(modules, aliasToModule) {
|
|
|
215
219
|
let changed = false;
|
|
216
220
|
for (const { module, exportsKinds } of modules) {
|
|
217
221
|
const table = tableFor(module);
|
|
218
|
-
for (const entry of exportsKinds) {
|
|
222
|
+
for (const entry of exportsKinds ?? []) {
|
|
219
223
|
const { name: suffix, alias } = parseExportEntry(entry);
|
|
220
224
|
if (table.has(suffix))
|
|
221
225
|
continue;
|
|
@@ -225,7 +229,14 @@ export function resolveExportedKinds(modules, aliasToModule) {
|
|
|
225
229
|
continue;
|
|
226
230
|
}
|
|
227
231
|
const source = aliasToModule(module, alias);
|
|
228
|
-
|
|
232
|
+
// Re-exporting FROM an ungated module resolves straight to it: the kernel derives
|
|
233
|
+
// the gate from the raw entry list and allows this, so dropping it here would make
|
|
234
|
+
// the analyzer reject a manifest the runtime accepts — the exact shape the migration
|
|
235
|
+
// path relies on (a gated wrapper over an already-published, ungated module).
|
|
236
|
+
const canonical = source
|
|
237
|
+
? (out.get(source)?.get(suffix) ??
|
|
238
|
+
(ungated.has(source) ? `${source}.${suffix}` : undefined))
|
|
239
|
+
: undefined;
|
|
229
240
|
if (canonical) {
|
|
230
241
|
table.set(suffix, canonical);
|
|
231
242
|
changed = true;
|
|
@@ -258,11 +269,38 @@ export function stampReExportedKinds(imports, exportedKinds) {
|
|
|
258
269
|
manifest.metadata.reExportedKinds = reExported;
|
|
259
270
|
}
|
|
260
271
|
}
|
|
272
|
+
/** Stamp `metadata.exportedKinds` (the target's `exports.kinds` gate as bare suffixes) onto
|
|
273
|
+
* every `Telo.Import` whose target declares one, so the analyzer enforces the same gate the
|
|
274
|
+
* kernel does. Stamped on `metadata` for the same reason as `reExportedKinds` — the
|
|
275
|
+
* `Telo.Import` schema forbids extra top-level fields.
|
|
276
|
+
*
|
|
277
|
+
* The gate comes from the DECLARED entries, parsed exactly as the kernel parses them
|
|
278
|
+
* (`parseExportEntry` — `Alias.Kind` yields `Kind`), never from the resolved re-export
|
|
279
|
+
* table. That table drops a re-export whose source module declares no `exports.kinds`, so
|
|
280
|
+
* deriving the gate from it would reject `exports.kinds: [Cee.Thing]` — a gate the kernel
|
|
281
|
+
* honours — as "not exported".
|
|
282
|
+
*
|
|
283
|
+
* A target that declares no `exports.kinds` is absent from `gatedModules` and goes unstamped,
|
|
284
|
+
* which the analyzer reads as unrestricted. That is the legacy permissive default, kept so
|
|
285
|
+
* already-published module versions stay importable; making kinds private by default means
|
|
286
|
+
* stamping every loaded target instead, which breaks those versions (see import-controller). */
|
|
287
|
+
export function stampExportedKinds(imports, declaredKinds) {
|
|
288
|
+
for (const { manifest, targetModule } of imports) {
|
|
289
|
+
const declared = declaredKinds.get(targetModule);
|
|
290
|
+
if (!declared)
|
|
291
|
+
continue;
|
|
292
|
+
manifest.metadata.exportedKinds = [...declared];
|
|
293
|
+
}
|
|
294
|
+
}
|
|
261
295
|
/** CLI/kernel adapter: collect re-export specs + alias map from a LoadedGraph. */
|
|
262
296
|
function forwardReExports(graph, result) {
|
|
263
297
|
const ownerSourceOf = new Map();
|
|
264
298
|
const specs = [];
|
|
265
299
|
const kindModules = [];
|
|
300
|
+
// Modules that DECLARE `exports.kinds` → the gate, as bare suffixes parsed the same way
|
|
301
|
+
// the kernel parses them. A module that declares none is absent, leaving importers ungated
|
|
302
|
+
// (see stampExportedKinds).
|
|
303
|
+
const declaredKinds = new Map();
|
|
266
304
|
for (const [source, mod] of graph.modules) {
|
|
267
305
|
if (source === graph.rootSource)
|
|
268
306
|
continue; // root is an Application — no exports
|
|
@@ -272,7 +310,10 @@ function forwardReExports(graph, result) {
|
|
|
272
310
|
continue;
|
|
273
311
|
ownerSourceOf.set(moduleName, mod.owner.source);
|
|
274
312
|
specs.push(...reExportSpecsFromExports(moduleName, libDoc.exports?.resources));
|
|
275
|
-
kindModules.push({ module: moduleName, exportsKinds: libDoc.exports?.kinds
|
|
313
|
+
kindModules.push({ module: moduleName, exportsKinds: libDoc.exports?.kinds });
|
|
314
|
+
if (libDoc.exports?.kinds !== undefined) {
|
|
315
|
+
declaredKinds.set(moduleName, libDoc.exports.kinds.map((e) => parseExportEntry(e).name));
|
|
316
|
+
}
|
|
276
317
|
}
|
|
277
318
|
const aliasToModule = (module, alias) => {
|
|
278
319
|
const ownerSource = ownerSourceOf.get(module);
|
|
@@ -295,6 +336,7 @@ function forwardReExports(graph, result) {
|
|
|
295
336
|
imports.push({ manifest: m, targetModule: target });
|
|
296
337
|
}
|
|
297
338
|
stampReExportedKinds(imports, exportedKinds);
|
|
339
|
+
stampExportedKinds(imports, declaredKinds);
|
|
298
340
|
}
|
|
299
341
|
/** Project a LoadedModule (owner + partials) to a flat ResourceManifest[]
|
|
300
342
|
* with `metadata.module` stamped on non-module docs. The kernel's runtime
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { AnalysisRegistry } from "./analysis-registry.js";
|
|
|
2
2
|
export type { RefFieldInfo } from "./analysis-registry.js";
|
|
3
3
|
export { StaticAnalyzer } from "./analyzer.js";
|
|
4
4
|
export type { GraphLoadError, ImportEdge, LoadedFile, LoadedGraph, LoadedModule, ParseError, } from "./loaded-types.js";
|
|
5
|
-
export { flattenForAnalyzer, flattenLoadedModule, forwardReExportManifests, parseExportEntry, reExportSpecsFromExports, resolveExportedKinds, selectModuleManifestsForAnalysis, stampReExportedKinds, type ParsedExportEntry, type ReExportSpec, } from "./flatten-for-analyzer.js";
|
|
5
|
+
export { flattenForAnalyzer, flattenLoadedModule, forwardReExportManifests, parseExportEntry, reExportSpecsFromExports, resolveExportedKinds, selectModuleManifestsForAnalysis, stampExportedKinds, stampReExportedKinds, type ParsedExportEntry, type ReExportSpec, } from "./flatten-for-analyzer.js";
|
|
6
6
|
export { buildEvalPaths, evalPathCovers } from "./eval-paths.js";
|
|
7
7
|
export { ancestorChain, controllerBearingAncestor, effectiveAuthorSchema, hasOwnControllerOrTemplate, inheritedCapability, isInheritedDelegation, resolveParent, } from "./extends-resolution.js";
|
|
8
8
|
export type { DefResolver } from "./extends-resolution.js";
|
|
@@ -30,7 +30,7 @@ export { parseModuleRef, isRegistryRef } from "./sources/module-ref.js";
|
|
|
30
30
|
export type { ParsedModuleRef } from "./sources/module-ref.js";
|
|
31
31
|
export { OCI_SCHEME, isOciRef, parseOciRef } from "./sources/oci-ref.js";
|
|
32
32
|
export type { ParsedOciRef } from "./sources/oci-ref.js";
|
|
33
|
-
export { MANIFEST_CACHE_BASE_URL, ManifestCacheSource, manifestCacheKey, manifestCacheUrl, ociManifestCacheCoords, } from "./sources/manifest-cache.js";
|
|
33
|
+
export { MANIFEST_CACHE_BASE_URL, ManifestCacheSource, isHttpsModuleRef, manifestCacheKey, manifestCacheUrl, ociManifestCacheCoords, urlManifestCacheCoords, } from "./sources/manifest-cache.js";
|
|
34
34
|
export type { ManifestCacheCoords } from "./sources/manifest-cache.js";
|
|
35
35
|
export { withSyntheticPositions } from "./with-synthetic-positions.js";
|
|
36
36
|
export { DEFAULT_MANIFEST_FILENAME, DiagnosticSeverity } from "./types.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { AnalysisRegistry } from "./analysis-registry.js";
|
|
2
2
|
export { StaticAnalyzer } from "./analyzer.js";
|
|
3
|
-
export { flattenForAnalyzer, flattenLoadedModule, forwardReExportManifests, parseExportEntry, reExportSpecsFromExports, resolveExportedKinds, selectModuleManifestsForAnalysis, stampReExportedKinds, } from "./flatten-for-analyzer.js";
|
|
3
|
+
export { flattenForAnalyzer, flattenLoadedModule, forwardReExportManifests, parseExportEntry, reExportSpecsFromExports, resolveExportedKinds, selectModuleManifestsForAnalysis, stampExportedKinds, stampReExportedKinds, } from "./flatten-for-analyzer.js";
|
|
4
4
|
export { buildEvalPaths, evalPathCovers } from "./eval-paths.js";
|
|
5
5
|
export { ancestorChain, controllerBearingAncestor, effectiveAuthorSchema, hasOwnControllerOrTemplate, inheritedCapability, isInheritedDelegation, resolveParent, } from "./extends-resolution.js";
|
|
6
6
|
export { buildReferenceFieldMap, isRefEntry, isScopeEntry } from "./reference-field-map.js";
|
|
@@ -18,6 +18,6 @@ export { defaultSources } from "./sources/default-sources.js";
|
|
|
18
18
|
export { splitIntegrity, foldIntegrity, verifyIntegrity, verifiedFetch, sha256Base64Url, IntegrityError, } from "./sources/integrity.js";
|
|
19
19
|
export { parseModuleRef, isRegistryRef } from "./sources/module-ref.js";
|
|
20
20
|
export { OCI_SCHEME, isOciRef, parseOciRef } from "./sources/oci-ref.js";
|
|
21
|
-
export { MANIFEST_CACHE_BASE_URL, ManifestCacheSource, manifestCacheKey, manifestCacheUrl, ociManifestCacheCoords, } from "./sources/manifest-cache.js";
|
|
21
|
+
export { MANIFEST_CACHE_BASE_URL, ManifestCacheSource, isHttpsModuleRef, manifestCacheKey, manifestCacheUrl, ociManifestCacheCoords, urlManifestCacheCoords, } from "./sources/manifest-cache.js";
|
|
22
22
|
export { withSyntheticPositions } from "./with-synthetic-positions.js";
|
|
23
23
|
export { DEFAULT_MANIFEST_FILENAME, DiagnosticSeverity } from "./types.js";
|
|
@@ -23,6 +23,21 @@ export declare function manifestCacheKey(coords: ManifestCacheCoords): string |
|
|
|
23
23
|
* not addressable — the cache is keyed by the human version tag the tracker
|
|
24
24
|
* enumerated). The inline `#sha256-…` fragment is tolerated and ignored. */
|
|
25
25
|
export declare function ociManifestCacheCoords(ref: string): ManifestCacheCoords | null;
|
|
26
|
+
/** True for a direct `https://` module manifest ref. Plaintext `http://` is
|
|
27
|
+
* deliberately excluded — the hub serves cached third-party manifests onward,
|
|
28
|
+
* so it never ingests over an unauthenticated channel. */
|
|
29
|
+
export declare function isHttpsModuleRef(ref: string): boolean;
|
|
30
|
+
/** Cache coordinates for a direct `https://host/path/telo.yaml` ref.
|
|
31
|
+
*
|
|
32
|
+
* Unlike OCI, a URL carries no version — a URL addresses one file whose
|
|
33
|
+
* version lives *inside* it (`metadata.version`) — so the caller passes the
|
|
34
|
+
* version it read from the manifest. Returns `null` for a non-https ref, a
|
|
35
|
+
* URL that carries a query or userinfo (both would let two distinct URLs
|
|
36
|
+
* collide onto one key, or smuggle a host), or an unparseable URL.
|
|
37
|
+
*
|
|
38
|
+
* A trailing `telo.yaml` segment is dropped: the key appends the manifest
|
|
39
|
+
* filename itself, so carrying it in `path` would duplicate it. */
|
|
40
|
+
export declare function urlManifestCacheCoords(ref: string, version: string): ManifestCacheCoords | null;
|
|
26
41
|
/** Full cache URL for a ref or coordinates, or `null` when not addressable. */
|
|
27
42
|
export declare function manifestCacheUrl(refOrCoords: string | ManifestCacheCoords, baseUrl?: string): string | null;
|
|
28
43
|
/** Resolves `oci://` module refs against the hub's static manifest cache with a
|
|
@@ -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,+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;;;;+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"}
|
|
@@ -40,6 +40,49 @@ export function ociManifestCacheCoords(ref) {
|
|
|
40
40
|
return null;
|
|
41
41
|
return { transport: "oci", host: parsed.host, path: parsed.repo, version: parsed.reference };
|
|
42
42
|
}
|
|
43
|
+
/** True for a direct `https://` module manifest ref. Plaintext `http://` is
|
|
44
|
+
* deliberately excluded — the hub serves cached third-party manifests onward,
|
|
45
|
+
* so it never ingests over an unauthenticated channel. */
|
|
46
|
+
export function isHttpsModuleRef(ref) {
|
|
47
|
+
return splitIntegrity(ref).base.startsWith("https://");
|
|
48
|
+
}
|
|
49
|
+
/** Cache coordinates for a direct `https://host/path/telo.yaml` ref.
|
|
50
|
+
*
|
|
51
|
+
* Unlike OCI, a URL carries no version — a URL addresses one file whose
|
|
52
|
+
* version lives *inside* it (`metadata.version`) — so the caller passes the
|
|
53
|
+
* version it read from the manifest. Returns `null` for a non-https ref, a
|
|
54
|
+
* URL that carries a query or userinfo (both would let two distinct URLs
|
|
55
|
+
* collide onto one key, or smuggle a host), or an unparseable URL.
|
|
56
|
+
*
|
|
57
|
+
* A trailing `telo.yaml` segment is dropped: the key appends the manifest
|
|
58
|
+
* filename itself, so carrying it in `path` would duplicate it. */
|
|
59
|
+
export function urlManifestCacheCoords(ref, version) {
|
|
60
|
+
const { base } = splitIntegrity(ref);
|
|
61
|
+
if (!isHttpsModuleRef(base))
|
|
62
|
+
return null;
|
|
63
|
+
let parsed;
|
|
64
|
+
try {
|
|
65
|
+
parsed = new URL(base);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
// `username`/`password` would mean the authority is not what it looks like
|
|
71
|
+
// (`https://evil.com@internal/…`); a query would make the key ambiguous.
|
|
72
|
+
if (parsed.search !== "" || parsed.username !== "" || parsed.password !== "")
|
|
73
|
+
return null;
|
|
74
|
+
const segments = parsed.pathname.split("/").filter((s) => s !== "");
|
|
75
|
+
if (segments[segments.length - 1] === DEFAULT_MANIFEST_FILENAME)
|
|
76
|
+
segments.pop();
|
|
77
|
+
if (segments.length === 0 || parsed.hostname === "")
|
|
78
|
+
return null;
|
|
79
|
+
return {
|
|
80
|
+
transport: "url",
|
|
81
|
+
host: parsed.host,
|
|
82
|
+
path: segments.map((s) => decodeURIComponent(s)).join("/"),
|
|
83
|
+
version,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
43
86
|
/** Full cache URL for a ref or coordinates, or `null` when not addressable. */
|
|
44
87
|
export function manifestCacheUrl(refOrCoords, baseUrl = MANIFEST_CACHE_BASE_URL) {
|
|
45
88
|
const coords = typeof refOrCoords === "string" ? ociManifestCacheCoords(refOrCoords) : refOrCoords;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/analyzer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "Telo Analyzer - Static manifest validator for Telo manifests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@types/node": "^20.0.0",
|
|
49
49
|
"typescript": "^5.0.0",
|
|
50
50
|
"vitest": "^2.1.8",
|
|
51
|
-
"@telorun/sdk": "0.
|
|
51
|
+
"@telorun/sdk": "0.47.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"@telorun/sdk": "*"
|
package/src/alias-resolver.ts
CHANGED
|
@@ -8,13 +8,25 @@ export class AliasResolver {
|
|
|
8
8
|
* import's own module. Resolved before the normal `<module>.<suffix>` construction. */
|
|
9
9
|
private readonly reExportedKinds = new Map<string, string>();
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/** Register an import alias gated to the target's `exports.kinds`. Only listed kinds
|
|
12
|
+
* resolve; an empty list exports nothing. `exportedKinds` is `undefined` for exactly one
|
|
13
|
+
* case — the target declares no `exports.kinds` at all (the legacy permissive default,
|
|
14
|
+
* kept so already-published module versions stay importable), which is the site to delete
|
|
15
|
+
* when kinds go private. For an alias crossing no import boundary use
|
|
16
|
+
* `registerUngatedAlias`. Mirrors `ModuleContext.registerImport`. */
|
|
17
|
+
registerImport(alias: string, targetModule: string, exportedKinds?: readonly string[]): void {
|
|
12
18
|
this.importAliases.set(alias, targetModule);
|
|
13
|
-
if (exportedKinds
|
|
19
|
+
if (exportedKinds !== undefined) {
|
|
14
20
|
this.importedKinds.set(alias, new Set(exportedKinds));
|
|
15
21
|
}
|
|
16
22
|
}
|
|
17
23
|
|
|
24
|
+
/** Register an alias that crosses no import boundary and is therefore never gated —
|
|
25
|
+
* `Self`, a library resolving its own kinds. See `ModuleContext.registerUngatedAlias`. */
|
|
26
|
+
registerUngatedAlias(alias: string, targetModule: string): void {
|
|
27
|
+
this.importAliases.set(alias, targetModule);
|
|
28
|
+
}
|
|
29
|
+
|
|
18
30
|
/** Register that `<alias>.<suffix>` re-exports the kind canonically named `canonicalKind`
|
|
19
31
|
* (owned by a module the alias's target imports, possibly several hops away). */
|
|
20
32
|
registerKindReExport(alias: string, suffix: string, canonicalKind: string): void {
|
|
@@ -30,24 +42,45 @@ export class AliasResolver {
|
|
|
30
42
|
return this.importAliases.get(alias);
|
|
31
43
|
}
|
|
32
44
|
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
45
|
+
/** Resolve an alias-qualified kind, reporting WHY it failed. One parse, one gate check —
|
|
46
|
+
* callers that need the reason and callers that only need the name share this, so they
|
|
47
|
+
* cannot drift on dot-splitting, re-export precedence, or gate order.
|
|
48
|
+
*
|
|
49
|
+
* - `ok` — resolved to a canonical `<module>.<Kind>`.
|
|
50
|
+
* - `gated` — the alias is known and the target owns the name, but its `exports.kinds`
|
|
51
|
+
* does not list it. A distinct outcome so callers can say "not exported"
|
|
52
|
+
* instead of the misleading "no such kind".
|
|
53
|
+
* - `unknown` — unqualified kind, or an alias this scope never imported. */
|
|
54
|
+
resolveKindResult(
|
|
55
|
+
kind: string,
|
|
56
|
+
):
|
|
57
|
+
| { status: "ok"; kind: string }
|
|
58
|
+
| { status: "gated"; module: string; exported: string[] }
|
|
59
|
+
| { status: "unknown" } {
|
|
60
|
+
if (!kind) return { status: "unknown" };
|
|
38
61
|
const dot = kind.indexOf(".");
|
|
39
|
-
if (dot === -1) return
|
|
62
|
+
if (dot === -1) return { status: "unknown" };
|
|
40
63
|
const prefix = kind.slice(0, dot);
|
|
41
64
|
const suffix = kind.slice(dot + 1);
|
|
42
65
|
// Re-export takes precedence: a re-exported kind resolves to its true owning module,
|
|
43
|
-
// not `${prefix-target}.${suffix}
|
|
66
|
+
// not `${prefix-target}.${suffix}`. It is listed in the re-exporting library's own
|
|
67
|
+
// `exports.kinds` (that is how it got here), so this is not a gate bypass.
|
|
44
68
|
const reExported = this.reExportedKinds.get(`${prefix}.${suffix}`);
|
|
45
|
-
if (reExported) return reExported;
|
|
69
|
+
if (reExported) return { status: "ok", kind: reExported };
|
|
46
70
|
const realModule = this.importAliases.get(prefix);
|
|
47
|
-
if (!realModule) return
|
|
71
|
+
if (!realModule) return { status: "unknown" };
|
|
48
72
|
const allowed = this.importedKinds.get(prefix);
|
|
49
|
-
if (allowed !== undefined && !allowed.has(suffix))
|
|
50
|
-
|
|
73
|
+
if (allowed !== undefined && !allowed.has(suffix)) {
|
|
74
|
+
return { status: "gated", module: realModule, exported: [...allowed] };
|
|
75
|
+
}
|
|
76
|
+
return { status: "ok", kind: `${realModule}.${suffix}` };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Resolves "Http.Api" → "http-server.Api". Undefined if the alias is unknown OR the kind
|
|
80
|
+
* is gated out — use `resolveKindResult` when the difference matters. */
|
|
81
|
+
resolveKind(kind: string): string | undefined {
|
|
82
|
+
const r = this.resolveKindResult(kind);
|
|
83
|
+
return r.status === "ok" ? r.kind : undefined;
|
|
51
84
|
}
|
|
52
85
|
|
|
53
86
|
hasAlias(alias: string): boolean {
|
package/src/analysis-registry.ts
CHANGED
|
@@ -44,10 +44,18 @@ export class AnalysisRegistry {
|
|
|
44
44
|
this.defs.registerModuleIdentity(namespace, name);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
/** `kinds` is the target's `exports.kinds` gate — only listed kinds resolve, and an
|
|
48
|
+
* empty list exports nothing. Omit it only for a target declaring no `exports.kinds`
|
|
49
|
+
* (the legacy permissive default); see `AliasResolver.registerImport`. */
|
|
50
|
+
registerImport(alias: string, target: string, kinds?: readonly string[]): void {
|
|
48
51
|
this.aliases.registerImport(alias, target, kinds);
|
|
49
52
|
}
|
|
50
53
|
|
|
54
|
+
/** An alias crossing no import boundary, never gated — `Self`, the `Telo` built-ins. */
|
|
55
|
+
registerUngatedAlias(alias: string, target: string): void {
|
|
56
|
+
this.aliases.registerUngatedAlias(alias, target);
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
resolveKind(kind: string): string | undefined {
|
|
52
60
|
return this.aliases.resolveKind(kind);
|
|
53
61
|
}
|
package/src/analyzer.ts
CHANGED
|
@@ -896,29 +896,33 @@ export class StaticAnalyzer {
|
|
|
896
896
|
// Lets same-library `extends:` work (e.g. `extends: Self.Encoder` for a
|
|
897
897
|
// concrete kind whose abstract lives in the same Telo.Library) without
|
|
898
898
|
// requiring a self-import (which would loop the loader). Resolves
|
|
899
|
-
// through the same alias machinery as user-declared Telo.Imports
|
|
900
|
-
// honours the library's `exports.kinds` list, no special cases.
|
|
899
|
+
// through the same alias machinery as user-declared Telo.Imports.
|
|
901
900
|
if (moduleName) {
|
|
902
901
|
// `Self` resolves the library's own kinds UNGATED — a library may reference
|
|
903
902
|
// its own kinds regardless of `exports.kinds`, which gates importers, not
|
|
904
903
|
// internal use. This is what lets a library declare an instance of a kind it
|
|
905
904
|
// does not export (e.g. console's `writeLine`) to enforce a singleton.
|
|
906
905
|
if (rootModules.has(moduleName)) {
|
|
907
|
-
aliases.
|
|
906
|
+
aliases.registerUngatedAlias("Self", moduleName);
|
|
908
907
|
} else {
|
|
909
908
|
let libResolver = aliasesByModule.get(moduleName);
|
|
910
909
|
if (!libResolver) {
|
|
911
910
|
libResolver = new AliasResolver();
|
|
912
911
|
aliasesByModule.set(moduleName, libResolver);
|
|
913
912
|
}
|
|
914
|
-
libResolver.
|
|
913
|
+
libResolver.registerUngatedAlias("Self", moduleName);
|
|
915
914
|
}
|
|
916
915
|
}
|
|
917
916
|
}
|
|
918
917
|
if (m.kind === "Telo.Import") {
|
|
919
918
|
const alias = m.metadata.name as string;
|
|
920
919
|
const source = (m as any).source as string | undefined;
|
|
921
|
-
|
|
920
|
+
// The target's `exports.kinds` gate, stamped onto this import by
|
|
921
|
+
// `stampExportedKinds` (the `Telo.Import` doc has no `exports` field of its own —
|
|
922
|
+
// the target `Telo.Library` doc it comes from is dropped for non-root modules).
|
|
923
|
+
// Undefined means the target declares no gate, i.e. unrestricted.
|
|
924
|
+
const exportedKinds = (m.metadata as { exportedKinds?: string[] } | undefined)
|
|
925
|
+
?.exportedKinds;
|
|
922
926
|
const resolvedModuleName = (m.metadata as any).resolvedModuleName as string | undefined;
|
|
923
927
|
const resolvedNamespace = (m.metadata as any).resolvedNamespace as
|
|
924
928
|
| string
|
|
@@ -1001,7 +1005,7 @@ export class StaticAnalyzer {
|
|
|
1001
1005
|
aliasesByModule.set(ownModule, libResolver);
|
|
1002
1006
|
}
|
|
1003
1007
|
if (!libResolver.hasAlias("Self")) {
|
|
1004
|
-
libResolver.
|
|
1008
|
+
libResolver.registerUngatedAlias("Self", ownModule);
|
|
1005
1009
|
}
|
|
1006
1010
|
}
|
|
1007
1011
|
|
|
@@ -1227,7 +1231,38 @@ export class StaticAnalyzer {
|
|
|
1227
1231
|
// reference-resolution paths: own-module scope first, root/consumer aliases last.
|
|
1228
1232
|
const ownModule = (m.metadata as { module?: string } | undefined)?.module;
|
|
1229
1233
|
const scopeResolver = scopeResolverForModule(ownModule, rootModules, aliasesByModule);
|
|
1230
|
-
|
|
1234
|
+
// Either scope resolving it wins; otherwise a gate rejection is more specific than
|
|
1235
|
+
// "unknown alias", so surface that.
|
|
1236
|
+
const scoped = scopeResolver?.resolveKindResult(m.kind);
|
|
1237
|
+
const rooted = aliases.resolveKindResult(m.kind);
|
|
1238
|
+
const kindResult =
|
|
1239
|
+
scoped?.status === "ok"
|
|
1240
|
+
? scoped
|
|
1241
|
+
: rooted.status === "ok"
|
|
1242
|
+
? rooted
|
|
1243
|
+
: scoped?.status === "gated"
|
|
1244
|
+
? scoped
|
|
1245
|
+
: rooted;
|
|
1246
|
+
|
|
1247
|
+
// The gate is checked BEFORE any definition lookup. `defs` is keyed
|
|
1248
|
+
// `<metadata.module>.<Kind>`, so a library whose `metadata.name` equals the alias it
|
|
1249
|
+
// is imported under (e.g. module `Foo` imported as `Foo`) makes the raw `defs.resolve(m.kind)`
|
|
1250
|
+
// below hit directly — which would accept a kind the module does not export, leaving
|
|
1251
|
+
// the analyzer more permissive than the kernel.
|
|
1252
|
+
if (kindResult.status === "gated") {
|
|
1253
|
+
diagnostics.push({
|
|
1254
|
+
severity: DiagnosticSeverity.Error,
|
|
1255
|
+
code: "KIND_NOT_EXPORTED",
|
|
1256
|
+
source: SOURCE,
|
|
1257
|
+
message:
|
|
1258
|
+
`Kind '${m.kind}' is not exported by module '${kindResult.module}'. ` +
|
|
1259
|
+
`Add '${m.kind.slice(m.kind.indexOf(".") + 1)}' to that module's exports.kinds ` +
|
|
1260
|
+
`to make it importable. Exported kinds: ${kindResult.exported.join(", ") || "(none)"}.`,
|
|
1261
|
+
data: { resource, filePath, path: "kind" },
|
|
1262
|
+
});
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
const resolvedKind = kindResult.status === "ok" ? kindResult.kind : undefined;
|
|
1231
1266
|
const definition =
|
|
1232
1267
|
defs.resolve(m.kind) ?? (resolvedKind ? defs.resolve(resolvedKind) : undefined);
|
|
1233
1268
|
if (!definition) {
|
|
@@ -239,10 +239,16 @@ export function forwardReExportManifests(
|
|
|
239
239
|
* maps one of that module's import aliases to the imported module's name. Graph-agnostic —
|
|
240
240
|
* shared by the CLI graph path and the editor's workspace projection. */
|
|
241
241
|
export function resolveExportedKinds(
|
|
242
|
-
modules: ReadonlyArray<{ module: string; exportsKinds: readonly string[] }>,
|
|
242
|
+
modules: ReadonlyArray<{ module: string; exportsKinds: readonly string[] | undefined }>,
|
|
243
243
|
aliasToModule: (module: string, alias: string) => string | undefined,
|
|
244
244
|
): Map<string, Map<string, string>> {
|
|
245
245
|
const out = new Map<string, Map<string, string>>();
|
|
246
|
+
// Modules declaring NO `exports.kinds` export every kind they define (the legacy permissive
|
|
247
|
+
// default), so they have no table to look a re-exported suffix up in. `[]` is a real gate
|
|
248
|
+
// that exports nothing — the two must stay distinguishable here.
|
|
249
|
+
const ungated = new Set(
|
|
250
|
+
modules.filter((m) => m.exportsKinds === undefined).map((m) => m.module),
|
|
251
|
+
);
|
|
246
252
|
const tableFor = (m: string): Map<string, string> => {
|
|
247
253
|
let t = out.get(m);
|
|
248
254
|
if (!t) out.set(m, (t = new Map()));
|
|
@@ -252,7 +258,7 @@ export function resolveExportedKinds(
|
|
|
252
258
|
let changed = false;
|
|
253
259
|
for (const { module, exportsKinds } of modules) {
|
|
254
260
|
const table = tableFor(module);
|
|
255
|
-
for (const entry of exportsKinds) {
|
|
261
|
+
for (const entry of exportsKinds ?? []) {
|
|
256
262
|
const { name: suffix, alias } = parseExportEntry(entry);
|
|
257
263
|
if (table.has(suffix)) continue;
|
|
258
264
|
if (!alias) {
|
|
@@ -261,7 +267,14 @@ export function resolveExportedKinds(
|
|
|
261
267
|
continue;
|
|
262
268
|
}
|
|
263
269
|
const source = aliasToModule(module, alias);
|
|
264
|
-
|
|
270
|
+
// Re-exporting FROM an ungated module resolves straight to it: the kernel derives
|
|
271
|
+
// the gate from the raw entry list and allows this, so dropping it here would make
|
|
272
|
+
// the analyzer reject a manifest the runtime accepts — the exact shape the migration
|
|
273
|
+
// path relies on (a gated wrapper over an already-published, ungated module).
|
|
274
|
+
const canonical = source
|
|
275
|
+
? (out.get(source)?.get(suffix) ??
|
|
276
|
+
(ungated.has(source) ? `${source}.${suffix}` : undefined))
|
|
277
|
+
: undefined;
|
|
265
278
|
if (canonical) {
|
|
266
279
|
table.set(suffix, canonical);
|
|
267
280
|
changed = true;
|
|
@@ -295,11 +308,41 @@ export function stampReExportedKinds(
|
|
|
295
308
|
}
|
|
296
309
|
}
|
|
297
310
|
|
|
311
|
+
/** Stamp `metadata.exportedKinds` (the target's `exports.kinds` gate as bare suffixes) onto
|
|
312
|
+
* every `Telo.Import` whose target declares one, so the analyzer enforces the same gate the
|
|
313
|
+
* kernel does. Stamped on `metadata` for the same reason as `reExportedKinds` — the
|
|
314
|
+
* `Telo.Import` schema forbids extra top-level fields.
|
|
315
|
+
*
|
|
316
|
+
* The gate comes from the DECLARED entries, parsed exactly as the kernel parses them
|
|
317
|
+
* (`parseExportEntry` — `Alias.Kind` yields `Kind`), never from the resolved re-export
|
|
318
|
+
* table. That table drops a re-export whose source module declares no `exports.kinds`, so
|
|
319
|
+
* deriving the gate from it would reject `exports.kinds: [Cee.Thing]` — a gate the kernel
|
|
320
|
+
* honours — as "not exported".
|
|
321
|
+
*
|
|
322
|
+
* A target that declares no `exports.kinds` is absent from `gatedModules` and goes unstamped,
|
|
323
|
+
* which the analyzer reads as unrestricted. That is the legacy permissive default, kept so
|
|
324
|
+
* already-published module versions stay importable; making kinds private by default means
|
|
325
|
+
* stamping every loaded target instead, which breaks those versions (see import-controller). */
|
|
326
|
+
export function stampExportedKinds(
|
|
327
|
+
imports: ReadonlyArray<{ manifest: ResourceManifest; targetModule: string }>,
|
|
328
|
+
declaredKinds: ReadonlyMap<string, readonly string[]>,
|
|
329
|
+
): void {
|
|
330
|
+
for (const { manifest, targetModule } of imports) {
|
|
331
|
+
const declared = declaredKinds.get(targetModule);
|
|
332
|
+
if (!declared) continue;
|
|
333
|
+
(manifest.metadata as Record<string, unknown>).exportedKinds = [...declared];
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
298
337
|
/** CLI/kernel adapter: collect re-export specs + alias map from a LoadedGraph. */
|
|
299
338
|
function forwardReExports(graph: LoadedGraph, result: ResourceManifest[]): void {
|
|
300
339
|
const ownerSourceOf = new Map<string, string>();
|
|
301
340
|
const specs: ReExportSpec[] = [];
|
|
302
|
-
const kindModules: Array<{ module: string; exportsKinds: string[] }> = [];
|
|
341
|
+
const kindModules: Array<{ module: string; exportsKinds: string[] | undefined }> = [];
|
|
342
|
+
// Modules that DECLARE `exports.kinds` → the gate, as bare suffixes parsed the same way
|
|
343
|
+
// the kernel parses them. A module that declares none is absent, leaving importers ungated
|
|
344
|
+
// (see stampExportedKinds).
|
|
345
|
+
const declaredKinds = new Map<string, readonly string[]>();
|
|
303
346
|
for (const [source, mod] of graph.modules) {
|
|
304
347
|
if (source === graph.rootSource) continue; // root is an Application — no exports
|
|
305
348
|
const libDoc = mod.owner.manifests.find((m) => m && isModuleKind(m.kind)) as
|
|
@@ -309,7 +352,10 @@ function forwardReExports(graph: LoadedGraph, result: ResourceManifest[]): void
|
|
|
309
352
|
if (!libDoc || !moduleName) continue;
|
|
310
353
|
ownerSourceOf.set(moduleName, mod.owner.source);
|
|
311
354
|
specs.push(...reExportSpecsFromExports(moduleName, libDoc.exports?.resources));
|
|
312
|
-
kindModules.push({ module: moduleName, exportsKinds: libDoc.exports?.kinds
|
|
355
|
+
kindModules.push({ module: moduleName, exportsKinds: libDoc.exports?.kinds });
|
|
356
|
+
if (libDoc.exports?.kinds !== undefined) {
|
|
357
|
+
declaredKinds.set(moduleName, libDoc.exports.kinds.map((e) => parseExportEntry(e).name));
|
|
358
|
+
}
|
|
313
359
|
}
|
|
314
360
|
const aliasToModule = (module: string, alias: string): string | undefined => {
|
|
315
361
|
const ownerSource = ownerSourceOf.get(module);
|
|
@@ -331,6 +377,7 @@ function forwardReExports(graph: LoadedGraph, result: ResourceManifest[]): void
|
|
|
331
377
|
if (target) imports.push({ manifest: m, targetModule: target });
|
|
332
378
|
}
|
|
333
379
|
stampReExportedKinds(imports, exportedKinds);
|
|
380
|
+
stampExportedKinds(imports, declaredKinds);
|
|
334
381
|
}
|
|
335
382
|
|
|
336
383
|
/** Project a LoadedModule (owner + partials) to a flat ResourceManifest[]
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ export {
|
|
|
17
17
|
reExportSpecsFromExports,
|
|
18
18
|
resolveExportedKinds,
|
|
19
19
|
selectModuleManifestsForAnalysis,
|
|
20
|
+
stampExportedKinds,
|
|
20
21
|
stampReExportedKinds,
|
|
21
22
|
type ParsedExportEntry,
|
|
22
23
|
type ReExportSpec,
|
|
@@ -80,9 +81,11 @@ export type { ParsedOciRef } from "./sources/oci-ref.js";
|
|
|
80
81
|
export {
|
|
81
82
|
MANIFEST_CACHE_BASE_URL,
|
|
82
83
|
ManifestCacheSource,
|
|
84
|
+
isHttpsModuleRef,
|
|
83
85
|
manifestCacheKey,
|
|
84
86
|
manifestCacheUrl,
|
|
85
87
|
ociManifestCacheCoords,
|
|
88
|
+
urlManifestCacheCoords,
|
|
86
89
|
} from "./sources/manifest-cache.js";
|
|
87
90
|
export type { ManifestCacheCoords } from "./sources/manifest-cache.js";
|
|
88
91
|
export { withSyntheticPositions } from "./with-synthetic-positions.js";
|
|
@@ -57,6 +57,48 @@ export function ociManifestCacheCoords(ref: string): ManifestCacheCoords | null
|
|
|
57
57
|
|
|
58
58
|
type ParsedOciRefShape = ReturnType<typeof parseOciRef>;
|
|
59
59
|
|
|
60
|
+
/** True for a direct `https://` module manifest ref. Plaintext `http://` is
|
|
61
|
+
* deliberately excluded — the hub serves cached third-party manifests onward,
|
|
62
|
+
* so it never ingests over an unauthenticated channel. */
|
|
63
|
+
export function isHttpsModuleRef(ref: string): boolean {
|
|
64
|
+
return splitIntegrity(ref).base.startsWith("https://");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Cache coordinates for a direct `https://host/path/telo.yaml` ref.
|
|
68
|
+
*
|
|
69
|
+
* Unlike OCI, a URL carries no version — a URL addresses one file whose
|
|
70
|
+
* version lives *inside* it (`metadata.version`) — so the caller passes the
|
|
71
|
+
* version it read from the manifest. Returns `null` for a non-https ref, a
|
|
72
|
+
* URL that carries a query or userinfo (both would let two distinct URLs
|
|
73
|
+
* collide onto one key, or smuggle a host), or an unparseable URL.
|
|
74
|
+
*
|
|
75
|
+
* A trailing `telo.yaml` segment is dropped: the key appends the manifest
|
|
76
|
+
* filename itself, so carrying it in `path` would duplicate it. */
|
|
77
|
+
export function urlManifestCacheCoords(ref: string, version: string): ManifestCacheCoords | null {
|
|
78
|
+
const { base } = splitIntegrity(ref);
|
|
79
|
+
if (!isHttpsModuleRef(base)) return null;
|
|
80
|
+
let parsed: URL;
|
|
81
|
+
try {
|
|
82
|
+
parsed = new URL(base);
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
// `username`/`password` would mean the authority is not what it looks like
|
|
87
|
+
// (`https://evil.com@internal/…`); a query would make the key ambiguous.
|
|
88
|
+
if (parsed.search !== "" || parsed.username !== "" || parsed.password !== "") return null;
|
|
89
|
+
|
|
90
|
+
const segments = parsed.pathname.split("/").filter((s) => s !== "");
|
|
91
|
+
if (segments[segments.length - 1] === DEFAULT_MANIFEST_FILENAME) segments.pop();
|
|
92
|
+
if (segments.length === 0 || parsed.hostname === "") return null;
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
transport: "url",
|
|
96
|
+
host: parsed.host,
|
|
97
|
+
path: segments.map((s) => decodeURIComponent(s)).join("/"),
|
|
98
|
+
version,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
60
102
|
/** Full cache URL for a ref or coordinates, or `null` when not addressable. */
|
|
61
103
|
export function manifestCacheUrl(
|
|
62
104
|
refOrCoords: string | ManifestCacheCoords,
|