@telorun/analyzer 0.34.0 → 0.35.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.
@@ -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;AAwxB/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;IAmzBvB,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"}
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;IAgzBvB,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
@@ -22,7 +22,6 @@ import { buildEvalPaths, evalPathsCover } from "./eval-paths.js";
22
22
  import { validateExtends } from "./validate-extends.js";
23
23
  import { validateBaseMapping } from "./validate-base-mapping.js";
24
24
  import { validateNestedInlineResources } from "./validate-nested-inline.js";
25
- import { validateKindDescriptions } from "./validate-kind-descriptions.js";
26
25
  import { validateProviderCoherence } from "./validate-provider-coherence.js";
27
26
  import { validateReferences } from "./validate-references.js";
28
27
  import { validateReferenceForms } from "./validate-reference-forms.js";
@@ -1383,8 +1382,6 @@ export class StaticAnalyzer {
1383
1382
  diagnostics.push(...validateBaseMapping(allManifests, defs, aliases));
1384
1383
  // Validate provider coherence rules for `provide:` template-target definitions.
1385
1384
  diagnostics.push(...validateProviderCoherence(allManifests, defs, aliases));
1386
- // Warn about exported kinds lacking a metadata.description (semantic-search input).
1387
- diagnostics.push(...validateKindDescriptions(allManifests));
1388
1385
  // Validate throws: declarations and catches: coverage (rules 1, 2, 4, 7)
1389
1386
  diagnostics.push(...validateThrowsCoverage(allManifests, defs, aliases, this.celEnv, aliasesByModule, rootModules));
1390
1387
  // Warn about declared variables / secrets / ports that no CEL references.
package/dist/index.d.ts CHANGED
@@ -28,6 +28,10 @@ export { defaultSources } from "./sources/default-sources.js";
28
28
  export { splitIntegrity, foldIntegrity, verifyIntegrity, verifiedFetch, sha256Base64Url, IntegrityError, } from "./sources/integrity.js";
29
29
  export { parseModuleRef, isRegistryRef } from "./sources/module-ref.js";
30
30
  export type { ParsedModuleRef } from "./sources/module-ref.js";
31
+ export { OCI_SCHEME, isOciRef, parseOciRef } from "./sources/oci-ref.js";
32
+ export type { ParsedOciRef } from "./sources/oci-ref.js";
33
+ export { MANIFEST_CACHE_BASE_URL, ManifestCacheSource, manifestCacheKey, manifestCacheUrl, ociManifestCacheCoords, } from "./sources/manifest-cache.js";
34
+ export type { ManifestCacheCoords } from "./sources/manifest-cache.js";
31
35
  export { withSyntheticPositions } from "./with-synthetic-positions.js";
32
36
  export { DEFAULT_MANIFEST_FILENAME, DiagnosticSeverity } from "./types.js";
33
37
  export type { AnalysisDiagnostic, AnalysisOptions, LoaderInitOptions, LoadOptions, ManifestSource, Position, PositionIndex, Range } from "./types.js";
@@ -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
@@ -17,5 +17,7 @@ export { RegistrySource } from "./sources/registry-source.js";
17
17
  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
+ 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";
20
22
  export { withSyntheticPositions } from "./with-synthetic-positions.js";
21
23
  export { DEFAULT_MANIFEST_FILENAME, DiagnosticSeverity } from "./types.js";
@@ -0,0 +1,48 @@
1
+ import type { ManifestSource } from "../types.js";
2
+ /** The hub's static manifest cache: an R2 bucket bound directly to the domain,
3
+ * serving each tracked module version's `telo.yaml` at a deterministic key.
4
+ * Read plane only — never artifact payloads; install/run stay origin-direct. */
5
+ export declare const MANIFEST_CACHE_BASE_URL = "https://manifests.telo.sh";
6
+ /** The location identity of one cached module version. Mirrors what the hub
7
+ * stores per version — `{ transport, host, path, version }` — so the tracker's
8
+ * write key and the editor's read key come from the same function and never
9
+ * drift. `path` is the slash-separated repo/module path (multi-segment OCI
10
+ * repos nest as prefixes). */
11
+ export interface ManifestCacheCoords {
12
+ transport: string;
13
+ host: string;
14
+ path: string;
15
+ version: string;
16
+ }
17
+ /** 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. */
20
+ export declare function manifestCacheKey(coords: ManifestCacheCoords): string | null;
21
+ /** Cache coordinates for an `oci://host/repo@tag` ref. Returns `null` when the
22
+ * ref carries no explicit tag (a defaulted `latest` or a `sha256:` digest is
23
+ * not addressable — the cache is keyed by the human version tag the tracker
24
+ * enumerated). The inline `#sha256-…` fragment is tolerated and ignored. */
25
+ export declare function ociManifestCacheCoords(ref: string): ManifestCacheCoords | null;
26
+ /** Full cache URL for a ref or coordinates, or `null` when not addressable. */
27
+ export declare function manifestCacheUrl(refOrCoords: string | ManifestCacheCoords, baseUrl?: string): string | null;
28
+ /** Resolves `oci://` module refs against the hub's static manifest cache with a
29
+ * plain CORS GET — a browser can't speak the OCI protocol, so this is the
30
+ * browser-safe read path for OCI imports. A pinned ref (`#sha256-…`) is
31
+ * verified against the fetched bytes, so a compromised cache can't mislead
32
+ * analysis; an unpinned ref is analyzed on trust — the security boundary is
33
+ * install/run (origin-direct, re-verified), not edit time. */
34
+ export declare class ManifestCacheSource implements ManifestSource {
35
+ private readonly baseUrl;
36
+ constructor(baseUrl?: string);
37
+ supports(url: string): boolean;
38
+ read(ref: string): Promise<{
39
+ text: string;
40
+ source: string;
41
+ }>;
42
+ /** Mirror of the kernel OCI transport's resolution: normalize the repo to a
43
+ * directory base so `../lib` under `oci://ghcr.io/aws/my-app` →
44
+ * `oci://ghcr.io/aws/lib`. The tag is dropped — the caller re-pins from the
45
+ * sibling's own version. Non-relative refs pass through. */
46
+ resolveRelative(base: string, relative: string): string;
47
+ }
48
+ //# sourceMappingURL=manifest-cache.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,91 @@
1
+ import { DEFAULT_MANIFEST_FILENAME } from "../types.js";
2
+ import { splitIntegrity, verifiedFetch } from "./integrity.js";
3
+ import { OCI_SCHEME, isOciRef, parseOciRef } from "./oci-ref.js";
4
+ /** The hub's static manifest cache: an R2 bucket bound directly to the domain,
5
+ * serving each tracked module version's `telo.yaml` at a deterministic key.
6
+ * Read plane only — never artifact payloads; install/run stay origin-direct. */
7
+ export const MANIFEST_CACHE_BASE_URL = "https://manifests.telo.sh";
8
+ /** True for a segment that would corrupt or escape the cache key space. */
9
+ function invalidSegment(segment) {
10
+ return segment === "" || segment === "." || segment === ".." || segment.includes("\\");
11
+ }
12
+ /** Deterministic cache key for one module version:
13
+ * `<transport>/<host>/<path…>/<version>/telo.yaml`. Returns `null` when any
14
+ * coordinate is empty or would traverse out of the key space. */
15
+ export function manifestCacheKey(coords) {
16
+ const { transport, host, path, version } = coords;
17
+ const segments = [transport, host, ...path.split("/"), version];
18
+ if (segments.some(invalidSegment) || transport.includes("/") || host.includes("/") || version.includes("/")) {
19
+ return null;
20
+ }
21
+ return `${segments.join("/")}/${DEFAULT_MANIFEST_FILENAME}`;
22
+ }
23
+ /** Cache coordinates for an `oci://host/repo@tag` ref. Returns `null` when the
24
+ * ref carries no explicit tag (a defaulted `latest` or a `sha256:` digest is
25
+ * not addressable — the cache is keyed by the human version tag the tracker
26
+ * enumerated). The inline `#sha256-…` fragment is tolerated and ignored. */
27
+ export function ociManifestCacheCoords(ref) {
28
+ if (!isOciRef(ref))
29
+ return null;
30
+ let parsed;
31
+ try {
32
+ parsed = parseOciRef(ref);
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ const { base } = splitIntegrity(ref);
38
+ const explicitTag = base.endsWith(`@${parsed.reference}`) && !parsed.reference.includes(":");
39
+ if (!explicitTag)
40
+ return null;
41
+ return { transport: "oci", host: parsed.host, path: parsed.repo, version: parsed.reference };
42
+ }
43
+ /** Full cache URL for a ref or coordinates, or `null` when not addressable. */
44
+ export function manifestCacheUrl(refOrCoords, baseUrl = MANIFEST_CACHE_BASE_URL) {
45
+ const coords = typeof refOrCoords === "string" ? ociManifestCacheCoords(refOrCoords) : refOrCoords;
46
+ if (!coords)
47
+ return null;
48
+ const key = manifestCacheKey(coords);
49
+ return key ? `${baseUrl.replace(/\/+$/, "")}/${key}` : null;
50
+ }
51
+ /** Resolves `oci://` module refs against the hub's static manifest cache with a
52
+ * plain CORS GET — a browser can't speak the OCI protocol, so this is the
53
+ * browser-safe read path for OCI imports. A pinned ref (`#sha256-…`) is
54
+ * verified against the fetched bytes, so a compromised cache can't mislead
55
+ * analysis; an unpinned ref is analyzed on trust — the security boundary is
56
+ * install/run (origin-direct, re-verified), not edit time. */
57
+ export class ManifestCacheSource {
58
+ baseUrl;
59
+ constructor(baseUrl = MANIFEST_CACHE_BASE_URL) {
60
+ this.baseUrl = baseUrl;
61
+ }
62
+ supports(url) {
63
+ // Claim every oci:// ref, addressable or not — nothing else browser-safe
64
+ // can ever resolve one, so an untagged/digest ref must reach `read()`'s
65
+ // actionable error instead of falling through to a file-not-found from a
66
+ // local/HTTP source.
67
+ return isOciRef(url);
68
+ }
69
+ async read(ref) {
70
+ const { integrity } = splitIntegrity(ref);
71
+ const coords = ociManifestCacheCoords(ref);
72
+ const fetchUrl = coords && manifestCacheUrl(coords, this.baseUrl);
73
+ if (!fetchUrl) {
74
+ throw new Error(`Cannot resolve '${ref}' from the manifest cache — an oci:// import needs an explicit version tag (oci://host/repo@version)`);
75
+ }
76
+ const { text } = await verifiedFetch(fetchUrl, integrity, ref);
77
+ return { text, source: `${OCI_SCHEME}${coords.host}/${coords.path}@${coords.version}` };
78
+ }
79
+ /** Mirror of the kernel OCI transport's resolution: normalize the repo to a
80
+ * directory base so `../lib` under `oci://ghcr.io/aws/my-app` →
81
+ * `oci://ghcr.io/aws/lib`. The tag is dropped — the caller re-pins from the
82
+ * sibling's own version. Non-relative refs pass through. */
83
+ resolveRelative(base, relative) {
84
+ if (!relative.startsWith(".") && !relative.startsWith("/"))
85
+ return relative;
86
+ const { host, repo } = parseOciRef(base);
87
+ const resolved = new URL(relative, `https://${host}/${repo}/`);
88
+ const newRepo = resolved.pathname.replace(/^\/+/, "");
89
+ return `${OCI_SCHEME}${host}/${newRepo}`;
90
+ }
91
+ }
@@ -0,0 +1,21 @@
1
+ export declare const OCI_SCHEME = "oci://";
2
+ /** A parsed `oci://host/repo@reference` module ref.
3
+ *
4
+ * - `host` — the registry host (`ghcr.io`, `123.dkr.ecr.us-east-1.amazonaws.com`).
5
+ * - `repo` — the repository path (`aws/telo-s3`), possibly multi-segment.
6
+ * - `reference` — a tag (`1.2.0`) or a digest (`sha256:...`); the OCI address.
7
+ * - `integrity` — Telo's inline `sha256-<base64url>` hash when the ref is pinned
8
+ * (authoritative across transports; the OCI digest is only corroborating). */
9
+ export interface ParsedOciRef {
10
+ host: string;
11
+ repo: string;
12
+ reference: string;
13
+ integrity?: string;
14
+ }
15
+ /** True when `ref` uses the `oci://` scheme (integrity fragment tolerated). */
16
+ export declare function isOciRef(ref: string): boolean;
17
+ /** Parse `oci://host/repo@reference[#sha256-...]`. Throws on a malformed ref.
18
+ * A `reference` may be a tag or a `sha256:` digest; when absent (`@` omitted)
19
+ * it defaults to `latest`, matching OCI tooling. */
20
+ export declare function parseOciRef(ref: string): ParsedOciRef;
21
+ //# sourceMappingURL=oci-ref.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oci-ref.d.ts","sourceRoot":"","sources":["../../src/sources/oci-ref.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU,WAAW,CAAC;AAEnC;;;;;;iFAMiF;AACjF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,+EAA+E;AAC/E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED;;qDAEqD;AACrD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CA0BrD"}
@@ -0,0 +1,35 @@
1
+ import { splitIntegrity } from "./integrity.js";
2
+ export const OCI_SCHEME = "oci://";
3
+ /** True when `ref` uses the `oci://` scheme (integrity fragment tolerated). */
4
+ export function isOciRef(ref) {
5
+ return splitIntegrity(ref).base.startsWith(OCI_SCHEME);
6
+ }
7
+ /** Parse `oci://host/repo@reference[#sha256-...]`. Throws on a malformed ref.
8
+ * A `reference` may be a tag or a `sha256:` digest; when absent (`@` omitted)
9
+ * it defaults to `latest`, matching OCI tooling. */
10
+ export function parseOciRef(ref) {
11
+ const { base, integrity } = splitIntegrity(ref);
12
+ if (!base.startsWith(OCI_SCHEME)) {
13
+ throw new Error(`Invalid OCI reference '${ref}', expected oci://host/repo@reference`);
14
+ }
15
+ const rest = base.slice(OCI_SCHEME.length);
16
+ const slash = rest.indexOf("/");
17
+ if (slash <= 0) {
18
+ throw new Error(`Invalid OCI reference '${ref}', missing repository path after host`);
19
+ }
20
+ const host = rest.slice(0, slash);
21
+ let repoAndRef = rest.slice(slash + 1);
22
+ let reference = "latest";
23
+ const at = repoAndRef.lastIndexOf("@");
24
+ // A digest reference is `repo@sha256:...` — the `@` before `sha256:` is the
25
+ // separator, not part of the repo. A tag reference has no `@`.
26
+ if (at > 0) {
27
+ reference = repoAndRef.slice(at + 1);
28
+ repoAndRef = repoAndRef.slice(0, at);
29
+ }
30
+ const repo = repoAndRef;
31
+ if (!host || !repo || !reference) {
32
+ throw new Error(`Invalid OCI reference '${ref}', expected oci://host/repo@reference`);
33
+ }
34
+ return { host, repo, reference, integrity };
35
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"validate-base-mapping.d.ts","sourceRoot":"","sources":["../src/validate-base-mapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAezE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,aAAa,GACrB,kBAAkB,EAAE,CAwCtB"}
1
+ {"version":3,"file":"validate-base-mapping.d.ts","sourceRoot":"","sources":["../src/validate-base-mapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAmBzE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,aAAa,GACrB,kBAAkB,EAAE,CAwCtB"}
@@ -1,12 +1,18 @@
1
1
  import { isCompiledValue } from "@telorun/sdk";
2
+ import { isTaggedSentinel } from "@telorun/templating";
2
3
  import { effectiveAuthorSchema, resolveParent } from "./extends-resolution.js";
3
4
  import { DiagnosticSeverity } from "./types.js";
4
5
  const SOURCE = "telo-analyzer";
5
- /** True when a value subtree contains a compiled CEL leaf — such a value can
6
- * produce anything at runtime, so its type is not statically checkable. */
6
+ /** True when a value subtree contains a CEL leaf — either a compiled CEL value
7
+ * (post-precompile) or the raw `!cel` tagged sentinel the analyzer sees at
8
+ * `telo check` time (this validator runs on the un-precompiled tree). Such a
9
+ * value can produce anything at runtime, so its type is not statically
10
+ * checkable. */
7
11
  function containsCel(value) {
8
12
  if (isCompiledValue(value))
9
13
  return true;
14
+ if (isTaggedSentinel(value) && value.engine === "cel")
15
+ return true;
10
16
  if (Array.isArray(value))
11
17
  return value.some(containsCel);
12
18
  if (value && typeof value === "object") {
@@ -1 +1 @@
1
- {"version":3,"file":"validate-throws-coverage.d.ts","sourceRoot":"","sources":["../src/validate-throws-coverage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAA0B,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA4dzE,oDAAoD;AACpD,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,WAAW,EAChB,eAAe,GAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAa,EACvD,WAAW,GAAE,GAAG,CAAC,MAAM,CAAa,GACnC,kBAAkB,EAAE,CAkDtB"}
1
+ {"version":3,"file":"validate-throws-coverage.d.ts","sourceRoot":"","sources":["../src/validate-throws-coverage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAA0B,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAoezE,oDAAoD;AACpD,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,WAAW,EAChB,eAAe,GAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAa,EACvD,WAAW,GAAE,GAAG,CAAC,MAAM,CAAa,GACnC,kBAAkB,EAAE,CAkDtB"}
@@ -1,3 +1,4 @@
1
+ import { isTaggedSentinel } from "@telorun/templating";
1
2
  import { scopeResolverForModule } from "./alias-resolver.js";
2
3
  import { createResolveCtx, resolveThrowsUnion, } from "./resolve-throws-union.js";
3
4
  import { DiagnosticSeverity } from "./types.js";
@@ -87,12 +88,18 @@ function resolveHandlerRef(sibling) {
87
88
  * Parenthesised nestings of `||` over equality/in are flattened.
88
89
  * Any non-matching sub-expression forfeits coverage for the whole `when:`. */
89
90
  function extractCoveredCodes(whenExpr, env) {
90
- const match = whenExpr.match(/\$\{\{\s*([^}]+?)\s*\}\}/);
91
- if (!match)
91
+ // The `when:` value is either a `!cel` TaggedSentinel (carrying the raw CEL
92
+ // source) or an inline `"${{ … }}"` string — both forms are load-equivalent.
93
+ const source = isTaggedSentinel(whenExpr) && whenExpr.engine === "cel"
94
+ ? whenExpr.source
95
+ : typeof whenExpr === "string"
96
+ ? whenExpr.match(/\$\{\{\s*([^}]+?)\s*\}\}/)?.[1]
97
+ : undefined;
98
+ if (!source)
92
99
  return { proven: false, codes: new Set() };
93
100
  let ast;
94
101
  try {
95
- ast = env.parse(match[1].trim()).ast;
102
+ ast = env.parse(source.trim()).ast;
96
103
  }
97
104
  catch {
98
105
  return { proven: false, codes: new Set() };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/analyzer",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "description": "Telo Analyzer - Static manifest validator for Telo manifests.",
5
5
  "keywords": [
6
6
  "telo",
package/src/analyzer.ts CHANGED
@@ -42,7 +42,6 @@ import { buildEvalPaths, evalPathsCover } from "./eval-paths.js";
42
42
  import { validateExtends } from "./validate-extends.js";
43
43
  import { validateBaseMapping } from "./validate-base-mapping.js";
44
44
  import { validateNestedInlineResources } from "./validate-nested-inline.js";
45
- import { validateKindDescriptions } from "./validate-kind-descriptions.js";
46
45
  import { validateProviderCoherence } from "./validate-provider-coherence.js";
47
46
  import { validateReferences } from "./validate-references.js";
48
47
  import { validateReferenceForms } from "./validate-reference-forms.js";
@@ -1654,9 +1653,6 @@ export class StaticAnalyzer {
1654
1653
  // Validate provider coherence rules for `provide:` template-target definitions.
1655
1654
  diagnostics.push(...validateProviderCoherence(allManifests, defs, aliases));
1656
1655
 
1657
- // Warn about exported kinds lacking a metadata.description (semantic-search input).
1658
- diagnostics.push(...validateKindDescriptions(allManifests));
1659
-
1660
1656
  // Validate throws: declarations and catches: coverage (rules 1, 2, 4, 7)
1661
1657
  diagnostics.push(
1662
1658
  ...validateThrowsCoverage(allManifests, defs, aliases, this.celEnv, aliasesByModule, rootModules),
package/src/index.ts CHANGED
@@ -75,6 +75,16 @@ export {
75
75
  } from "./sources/integrity.js";
76
76
  export { parseModuleRef, isRegistryRef } from "./sources/module-ref.js";
77
77
  export type { ParsedModuleRef } from "./sources/module-ref.js";
78
+ export { OCI_SCHEME, isOciRef, parseOciRef } from "./sources/oci-ref.js";
79
+ export type { ParsedOciRef } from "./sources/oci-ref.js";
80
+ export {
81
+ MANIFEST_CACHE_BASE_URL,
82
+ ManifestCacheSource,
83
+ manifestCacheKey,
84
+ manifestCacheUrl,
85
+ ociManifestCacheCoords,
86
+ } from "./sources/manifest-cache.js";
87
+ export type { ManifestCacheCoords } from "./sources/manifest-cache.js";
78
88
  export { withSyntheticPositions } from "./with-synthetic-positions.js";
79
89
  export { DEFAULT_MANIFEST_FILENAME, DiagnosticSeverity } from "./types.js";
80
90
  export type {
@@ -0,0 +1,112 @@
1
+ import type { ManifestSource } from "../types.js";
2
+ import { DEFAULT_MANIFEST_FILENAME } from "../types.js";
3
+ import { splitIntegrity, verifiedFetch } from "./integrity.js";
4
+ import { OCI_SCHEME, isOciRef, parseOciRef } from "./oci-ref.js";
5
+
6
+ /** The hub's static manifest cache: an R2 bucket bound directly to the domain,
7
+ * serving each tracked module version's `telo.yaml` at a deterministic key.
8
+ * Read plane only — never artifact payloads; install/run stay origin-direct. */
9
+ export const MANIFEST_CACHE_BASE_URL = "https://manifests.telo.sh";
10
+
11
+ /** The location identity of one cached module version. Mirrors what the hub
12
+ * stores per version — `{ transport, host, path, version }` — so the tracker's
13
+ * write key and the editor's read key come from the same function and never
14
+ * drift. `path` is the slash-separated repo/module path (multi-segment OCI
15
+ * repos nest as prefixes). */
16
+ export interface ManifestCacheCoords {
17
+ transport: string;
18
+ host: string;
19
+ path: string;
20
+ version: string;
21
+ }
22
+
23
+ /** True for a segment that would corrupt or escape the cache key space. */
24
+ function invalidSegment(segment: string): boolean {
25
+ return segment === "" || segment === "." || segment === ".." || segment.includes("\\");
26
+ }
27
+
28
+ /** Deterministic cache key for one module version:
29
+ * `<transport>/<host>/<path…>/<version>/telo.yaml`. Returns `null` when any
30
+ * coordinate is empty or would traverse out of the key space. */
31
+ export function manifestCacheKey(coords: ManifestCacheCoords): string | null {
32
+ const { transport, host, path, version } = coords;
33
+ const segments = [transport, host, ...path.split("/"), version];
34
+ if (segments.some(invalidSegment) || transport.includes("/") || host.includes("/") || version.includes("/")) {
35
+ return null;
36
+ }
37
+ return `${segments.join("/")}/${DEFAULT_MANIFEST_FILENAME}`;
38
+ }
39
+
40
+ /** Cache coordinates for an `oci://host/repo@tag` ref. Returns `null` when the
41
+ * ref carries no explicit tag (a defaulted `latest` or a `sha256:` digest is
42
+ * not addressable — the cache is keyed by the human version tag the tracker
43
+ * enumerated). The inline `#sha256-…` fragment is tolerated and ignored. */
44
+ export function ociManifestCacheCoords(ref: string): ManifestCacheCoords | null {
45
+ if (!isOciRef(ref)) return null;
46
+ let parsed: ParsedOciRefShape;
47
+ try {
48
+ parsed = parseOciRef(ref);
49
+ } catch {
50
+ return null;
51
+ }
52
+ const { base } = splitIntegrity(ref);
53
+ const explicitTag = base.endsWith(`@${parsed.reference}`) && !parsed.reference.includes(":");
54
+ if (!explicitTag) return null;
55
+ return { transport: "oci", host: parsed.host, path: parsed.repo, version: parsed.reference };
56
+ }
57
+
58
+ type ParsedOciRefShape = ReturnType<typeof parseOciRef>;
59
+
60
+ /** Full cache URL for a ref or coordinates, or `null` when not addressable. */
61
+ export function manifestCacheUrl(
62
+ refOrCoords: string | ManifestCacheCoords,
63
+ baseUrl: string = MANIFEST_CACHE_BASE_URL,
64
+ ): string | null {
65
+ const coords = typeof refOrCoords === "string" ? ociManifestCacheCoords(refOrCoords) : refOrCoords;
66
+ if (!coords) return null;
67
+ const key = manifestCacheKey(coords);
68
+ return key ? `${baseUrl.replace(/\/+$/, "")}/${key}` : null;
69
+ }
70
+
71
+ /** Resolves `oci://` module refs against the hub's static manifest cache with a
72
+ * plain CORS GET — a browser can't speak the OCI protocol, so this is the
73
+ * browser-safe read path for OCI imports. A pinned ref (`#sha256-…`) is
74
+ * verified against the fetched bytes, so a compromised cache can't mislead
75
+ * analysis; an unpinned ref is analyzed on trust — the security boundary is
76
+ * install/run (origin-direct, re-verified), not edit time. */
77
+ export class ManifestCacheSource implements ManifestSource {
78
+ constructor(private readonly baseUrl: string = MANIFEST_CACHE_BASE_URL) {}
79
+
80
+ supports(url: string): boolean {
81
+ // Claim every oci:// ref, addressable or not — nothing else browser-safe
82
+ // can ever resolve one, so an untagged/digest ref must reach `read()`'s
83
+ // actionable error instead of falling through to a file-not-found from a
84
+ // local/HTTP source.
85
+ return isOciRef(url);
86
+ }
87
+
88
+ async read(ref: string): Promise<{ text: string; source: string }> {
89
+ const { integrity } = splitIntegrity(ref);
90
+ const coords = ociManifestCacheCoords(ref);
91
+ const fetchUrl = coords && manifestCacheUrl(coords, this.baseUrl);
92
+ if (!fetchUrl) {
93
+ throw new Error(
94
+ `Cannot resolve '${ref}' from the manifest cache — an oci:// import needs an explicit version tag (oci://host/repo@version)`,
95
+ );
96
+ }
97
+ const { text } = await verifiedFetch(fetchUrl, integrity, ref);
98
+ return { text, source: `${OCI_SCHEME}${coords.host}/${coords.path}@${coords.version}` };
99
+ }
100
+
101
+ /** Mirror of the kernel OCI transport's resolution: normalize the repo to a
102
+ * directory base so `../lib` under `oci://ghcr.io/aws/my-app` →
103
+ * `oci://ghcr.io/aws/lib`. The tag is dropped — the caller re-pins from the
104
+ * sibling's own version. Non-relative refs pass through. */
105
+ resolveRelative(base: string, relative: string): string {
106
+ if (!relative.startsWith(".") && !relative.startsWith("/")) return relative;
107
+ const { host, repo } = parseOciRef(base);
108
+ const resolved = new URL(relative, `https://${host}/${repo}/`);
109
+ const newRepo = resolved.pathname.replace(/^\/+/, "");
110
+ return `${OCI_SCHEME}${host}/${newRepo}`;
111
+ }
112
+ }
@@ -0,0 +1,53 @@
1
+ import { splitIntegrity } from "./integrity.js";
2
+
3
+ export const OCI_SCHEME = "oci://";
4
+
5
+ /** A parsed `oci://host/repo@reference` module ref.
6
+ *
7
+ * - `host` — the registry host (`ghcr.io`, `123.dkr.ecr.us-east-1.amazonaws.com`).
8
+ * - `repo` — the repository path (`aws/telo-s3`), possibly multi-segment.
9
+ * - `reference` — a tag (`1.2.0`) or a digest (`sha256:...`); the OCI address.
10
+ * - `integrity` — Telo's inline `sha256-<base64url>` hash when the ref is pinned
11
+ * (authoritative across transports; the OCI digest is only corroborating). */
12
+ export interface ParsedOciRef {
13
+ host: string;
14
+ repo: string;
15
+ reference: string;
16
+ integrity?: string;
17
+ }
18
+
19
+ /** True when `ref` uses the `oci://` scheme (integrity fragment tolerated). */
20
+ export function isOciRef(ref: string): boolean {
21
+ return splitIntegrity(ref).base.startsWith(OCI_SCHEME);
22
+ }
23
+
24
+ /** Parse `oci://host/repo@reference[#sha256-...]`. Throws on a malformed ref.
25
+ * A `reference` may be a tag or a `sha256:` digest; when absent (`@` omitted)
26
+ * it defaults to `latest`, matching OCI tooling. */
27
+ export function parseOciRef(ref: string): ParsedOciRef {
28
+ const { base, integrity } = splitIntegrity(ref);
29
+ if (!base.startsWith(OCI_SCHEME)) {
30
+ throw new Error(`Invalid OCI reference '${ref}', expected oci://host/repo@reference`);
31
+ }
32
+ const rest = base.slice(OCI_SCHEME.length);
33
+ const slash = rest.indexOf("/");
34
+ if (slash <= 0) {
35
+ throw new Error(`Invalid OCI reference '${ref}', missing repository path after host`);
36
+ }
37
+ const host = rest.slice(0, slash);
38
+ let repoAndRef = rest.slice(slash + 1);
39
+
40
+ let reference = "latest";
41
+ const at = repoAndRef.lastIndexOf("@");
42
+ // A digest reference is `repo@sha256:...` — the `@` before `sha256:` is the
43
+ // separator, not part of the repo. A tag reference has no `@`.
44
+ if (at > 0) {
45
+ reference = repoAndRef.slice(at + 1);
46
+ repoAndRef = repoAndRef.slice(0, at);
47
+ }
48
+ const repo = repoAndRef;
49
+ if (!host || !repo || !reference) {
50
+ throw new Error(`Invalid OCI reference '${ref}', expected oci://host/repo@reference`);
51
+ }
52
+ return { host, repo, reference, integrity };
53
+ }
@@ -1,5 +1,6 @@
1
1
  import type { ResourceDefinition, ResourceManifest } from "@telorun/sdk";
2
2
  import { isCompiledValue } from "@telorun/sdk";
3
+ import { isTaggedSentinel } from "@telorun/templating";
3
4
  import type { AliasResolver } from "./alias-resolver.js";
4
5
  import type { DefinitionRegistry } from "./definition-registry.js";
5
6
  import { effectiveAuthorSchema, resolveParent, type DefResolver } from "./extends-resolution.js";
@@ -7,10 +8,14 @@ import { DiagnosticSeverity, type AnalysisDiagnostic } from "./types.js";
7
8
 
8
9
  const SOURCE = "telo-analyzer";
9
10
 
10
- /** True when a value subtree contains a compiled CEL leaf — such a value can
11
- * produce anything at runtime, so its type is not statically checkable. */
11
+ /** True when a value subtree contains a CEL leaf — either a compiled CEL value
12
+ * (post-precompile) or the raw `!cel` tagged sentinel the analyzer sees at
13
+ * `telo check` time (this validator runs on the un-precompiled tree). Such a
14
+ * value can produce anything at runtime, so its type is not statically
15
+ * checkable. */
12
16
  function containsCel(value: unknown): boolean {
13
17
  if (isCompiledValue(value)) return true;
18
+ if (isTaggedSentinel(value) && value.engine === "cel") return true;
14
19
  if (Array.isArray(value)) return value.some(containsCel);
15
20
  if (value && typeof value === "object") {
16
21
  return Object.values(value as Record<string, unknown>).some(containsCel);
@@ -1,4 +1,5 @@
1
1
  import type { ASTNode, Environment } from "@marcbachmann/cel-js";
2
+ import { isTaggedSentinel } from "@telorun/templating";
2
3
  import type { ResourceManifest } from "@telorun/sdk";
3
4
  import { scopeResolverForModule, type AliasResolver } from "./alias-resolver.js";
4
5
  import type { DefinitionRegistry } from "./definition-registry.js";
@@ -15,7 +16,8 @@ const SOURCE = "telo-analyzer";
15
16
  const TEMPLATE_REGEX = /\$\{\{\s*([^}]+?)\s*\}\}/g;
16
17
 
17
18
  interface OutcomeEntry {
18
- when?: string;
19
+ /** Inline `"${{ … }}"` string or a `!cel` TaggedSentinel. */
20
+ when?: unknown;
19
21
  body?: unknown;
20
22
  headers?: Record<string, unknown>;
21
23
  status?: number;
@@ -137,14 +139,21 @@ function resolveHandlerRef(sibling: unknown): { kind: string; name?: string } |
137
139
  * Parenthesised nestings of `||` over equality/in are flattened.
138
140
  * Any non-matching sub-expression forfeits coverage for the whole `when:`. */
139
141
  function extractCoveredCodes(
140
- whenExpr: string,
142
+ whenExpr: unknown,
141
143
  env: Environment,
142
144
  ): { proven: boolean; codes: Set<string> } {
143
- const match = whenExpr.match(/\$\{\{\s*([^}]+?)\s*\}\}/);
144
- if (!match) return { proven: false, codes: new Set() };
145
+ // The `when:` value is either a `!cel` TaggedSentinel (carrying the raw CEL
146
+ // source) or an inline `"${{ }}"` string both forms are load-equivalent.
147
+ const source =
148
+ isTaggedSentinel(whenExpr) && whenExpr.engine === "cel"
149
+ ? whenExpr.source
150
+ : typeof whenExpr === "string"
151
+ ? whenExpr.match(/\$\{\{\s*([^}]+?)\s*\}\}/)?.[1]
152
+ : undefined;
153
+ if (!source) return { proven: false, codes: new Set() };
145
154
  let ast: ASTNode;
146
155
  try {
147
- ast = env.parse(match[1].trim()).ast;
156
+ ast = env.parse(source.trim()).ast;
148
157
  } catch {
149
158
  return { proven: false, codes: new Set() };
150
159
  }
@@ -1,20 +0,0 @@
1
- import type { ResourceManifest } from "@telorun/sdk";
2
- import { type AnalysisDiagnostic } from "./types.js";
3
- /**
4
- * Warns when a library exports a kind (via `exports.kinds`) whose local
5
- * `Telo.Definition` carries no `metadata.description`.
6
- *
7
- * The description is the primary human text the federated-discovery hub embeds
8
- * for semantic search (`search_resources`), so an exported kind without one is
9
- * undiscoverable by meaning. A warning (not an error) so the stdlib backfill is
10
- * incremental and CI isn't blocked mid-migration.
11
- *
12
- * Scope: only kinds a library *exports* and *defines locally* are checked.
13
- * Re-exported kinds (`exports.kinds: [Alias.Kind]`) belong to their owning
14
- * module and are skipped; an exported name that isn't a local Telo.Definition
15
- * (e.g. a Telo.Abstract) is skipped too. The check keys off the root library's
16
- * own module doc, which is only present when that library is analyzed directly
17
- * — so importing an under-described library never leaks warnings to its consumer.
18
- */
19
- export declare function validateKindDescriptions(manifests: ResourceManifest[]): AnalysisDiagnostic[];
20
- //# sourceMappingURL=validate-kind-descriptions.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"validate-kind-descriptions.d.ts","sourceRoot":"","sources":["../src/validate-kind-descriptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,gBAAgB,EAAE,GAAG,kBAAkB,EAAE,CA2C5F"}
@@ -1,65 +0,0 @@
1
- import { DiagnosticSeverity } from "./types.js";
2
- const SOURCE = "telo-analyzer";
3
- /**
4
- * Warns when a library exports a kind (via `exports.kinds`) whose local
5
- * `Telo.Definition` carries no `metadata.description`.
6
- *
7
- * The description is the primary human text the federated-discovery hub embeds
8
- * for semantic search (`search_resources`), so an exported kind without one is
9
- * undiscoverable by meaning. A warning (not an error) so the stdlib backfill is
10
- * incremental and CI isn't blocked mid-migration.
11
- *
12
- * Scope: only kinds a library *exports* and *defines locally* are checked.
13
- * Re-exported kinds (`exports.kinds: [Alias.Kind]`) belong to their owning
14
- * module and are skipped; an exported name that isn't a local Telo.Definition
15
- * (e.g. a Telo.Abstract) is skipped too. The check keys off the root library's
16
- * own module doc, which is only present when that library is analyzed directly
17
- * — so importing an under-described library never leaks warnings to its consumer.
18
- */
19
- export function validateKindDescriptions(manifests) {
20
- const diagnostics = [];
21
- // Local Telo.Definition docs keyed by `<module>/<name>`.
22
- const definitions = new Map();
23
- for (const m of manifests) {
24
- if (m.kind !== "Telo.Definition")
25
- continue;
26
- const name = m.metadata?.name;
27
- const mod = m.metadata?.module;
28
- if (name && mod)
29
- definitions.set(`${mod}/${name}`, m);
30
- }
31
- for (const m of manifests) {
32
- if (m.kind !== "Telo.Library")
33
- continue;
34
- const moduleName = m.metadata?.name;
35
- if (!moduleName)
36
- continue;
37
- const exportedKinds = m.exports?.kinds;
38
- if (!Array.isArray(exportedKinds))
39
- continue;
40
- for (const entry of exportedKinds) {
41
- // Re-export (`Alias.Kind`) — the owning module owns its description.
42
- if (typeof entry !== "string" || entry.includes("."))
43
- continue;
44
- const def = definitions.get(`${moduleName}/${entry}`);
45
- if (!def)
46
- continue; // not a local Telo.Definition (e.g. an abstract)
47
- const description = def.metadata?.description;
48
- if (typeof description === "string" && description.trim() !== "")
49
- continue;
50
- diagnostics.push({
51
- severity: DiagnosticSeverity.Warning,
52
- code: "KIND_MISSING_DESCRIPTION",
53
- source: SOURCE,
54
- message: `${moduleName}.${entry}: exported kind has no 'metadata.description'. Add a one-line ` +
55
- `description — it is the primary text indexed for semantic discovery (search_resources).`,
56
- data: {
57
- resource: { kind: "Telo.Definition", name: entry },
58
- filePath: def.metadata?.source,
59
- path: "metadata.description",
60
- },
61
- });
62
- }
63
- }
64
- return diagnostics;
65
- }
@@ -1,65 +0,0 @@
1
- import type { ResourceManifest } from "@telorun/sdk";
2
- import { DiagnosticSeverity, type AnalysisDiagnostic } from "./types.js";
3
-
4
- const SOURCE = "telo-analyzer";
5
-
6
- /**
7
- * Warns when a library exports a kind (via `exports.kinds`) whose local
8
- * `Telo.Definition` carries no `metadata.description`.
9
- *
10
- * The description is the primary human text the federated-discovery hub embeds
11
- * for semantic search (`search_resources`), so an exported kind without one is
12
- * undiscoverable by meaning. A warning (not an error) so the stdlib backfill is
13
- * incremental and CI isn't blocked mid-migration.
14
- *
15
- * Scope: only kinds a library *exports* and *defines locally* are checked.
16
- * Re-exported kinds (`exports.kinds: [Alias.Kind]`) belong to their owning
17
- * module and are skipped; an exported name that isn't a local Telo.Definition
18
- * (e.g. a Telo.Abstract) is skipped too. The check keys off the root library's
19
- * own module doc, which is only present when that library is analyzed directly
20
- * — so importing an under-described library never leaks warnings to its consumer.
21
- */
22
- export function validateKindDescriptions(manifests: ResourceManifest[]): AnalysisDiagnostic[] {
23
- const diagnostics: AnalysisDiagnostic[] = [];
24
-
25
- // Local Telo.Definition docs keyed by `<module>/<name>`.
26
- const definitions = new Map<string, ResourceManifest>();
27
- for (const m of manifests) {
28
- if (m.kind !== "Telo.Definition") continue;
29
- const name = m.metadata?.name as string | undefined;
30
- const mod = (m.metadata as { module?: string } | undefined)?.module;
31
- if (name && mod) definitions.set(`${mod}/${name}`, m);
32
- }
33
-
34
- for (const m of manifests) {
35
- if (m.kind !== "Telo.Library") continue;
36
- const moduleName = m.metadata?.name as string | undefined;
37
- if (!moduleName) continue;
38
- const exportedKinds = (m as { exports?: { kinds?: unknown } }).exports?.kinds;
39
- if (!Array.isArray(exportedKinds)) continue;
40
-
41
- for (const entry of exportedKinds) {
42
- // Re-export (`Alias.Kind`) — the owning module owns its description.
43
- if (typeof entry !== "string" || entry.includes(".")) continue;
44
- const def = definitions.get(`${moduleName}/${entry}`);
45
- if (!def) continue; // not a local Telo.Definition (e.g. an abstract)
46
- const description = (def.metadata as { description?: unknown } | undefined)?.description;
47
- if (typeof description === "string" && description.trim() !== "") continue;
48
- diagnostics.push({
49
- severity: DiagnosticSeverity.Warning,
50
- code: "KIND_MISSING_DESCRIPTION",
51
- source: SOURCE,
52
- message:
53
- `${moduleName}.${entry}: exported kind has no 'metadata.description'. Add a one-line ` +
54
- `description — it is the primary text indexed for semantic discovery (search_resources).`,
55
- data: {
56
- resource: { kind: "Telo.Definition", name: entry },
57
- filePath: (def.metadata as { source?: string } | undefined)?.source,
58
- path: "metadata.description",
59
- },
60
- });
61
- }
62
- }
63
-
64
- return diagnostics;
65
- }