@telorun/analyzer 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +17 -0
- package/dist/adapters/http-adapter.d.ts +10 -0
- package/dist/adapters/http-adapter.d.ts.map +1 -0
- package/dist/adapters/http-adapter.js +17 -0
- package/dist/adapters/node-adapter.d.ts +15 -0
- package/dist/adapters/node-adapter.d.ts.map +1 -0
- package/dist/adapters/node-adapter.js +32 -0
- package/dist/adapters/registry-adapter.d.ts +11 -0
- package/dist/adapters/registry-adapter.d.ts.map +1 -0
- package/dist/adapters/registry-adapter.js +33 -0
- package/dist/alias-resolver.d.ts +12 -0
- package/dist/alias-resolver.d.ts.map +1 -0
- package/dist/alias-resolver.js +36 -0
- package/dist/analysis-registry.d.ts +29 -0
- package/dist/analysis-registry.d.ts.map +1 -0
- package/dist/analysis-registry.js +55 -0
- package/dist/analyzer.d.ts +14 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/analyzer.js +314 -0
- package/dist/builtins.d.ts +3 -0
- package/dist/builtins.d.ts.map +1 -0
- package/dist/builtins.js +109 -0
- package/dist/cel-environment.d.ts +12 -0
- package/dist/cel-environment.d.ts.map +1 -0
- package/dist/cel-environment.js +59 -0
- package/dist/definition-registry.d.ts +58 -0
- package/dist/definition-registry.d.ts.map +1 -0
- package/dist/definition-registry.js +155 -0
- package/dist/dependency-graph.d.ts +38 -0
- package/dist/dependency-graph.d.ts.map +1 -0
- package/dist/dependency-graph.js +155 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/manifest-loader.d.ts +11 -0
- package/dist/manifest-loader.d.ts.map +1 -0
- package/dist/manifest-loader.js +194 -0
- package/dist/normalize-inline-resources.d.ts +22 -0
- package/dist/normalize-inline-resources.d.ts.map +1 -0
- package/dist/normalize-inline-resources.js +136 -0
- package/dist/precompile.d.ts +9 -0
- package/dist/precompile.d.ts.map +1 -0
- package/dist/precompile.js +51 -0
- package/dist/reference-field-map.d.ts +53 -0
- package/dist/reference-field-map.d.ts.map +1 -0
- package/dist/reference-field-map.js +107 -0
- package/dist/schema-compat.d.ts +42 -0
- package/dist/schema-compat.d.ts.map +1 -0
- package/dist/schema-compat.js +234 -0
- package/dist/scope-resolver.d.ts +5 -0
- package/dist/scope-resolver.d.ts.map +1 -0
- package/dist/scope-resolver.js +13 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/validate-cel-context.d.ts +24 -0
- package/dist/validate-cel-context.d.ts.map +1 -0
- package/dist/validate-cel-context.js +136 -0
- package/dist/validate-references.d.ts +19 -0
- package/dist/validate-references.d.ts.map +1 -0
- package/dist/validate-references.js +275 -0
- package/package.json +34 -0
- package/src/adapters/http-adapter.ts +23 -0
- package/src/adapters/node-adapter.ts +38 -0
- package/src/adapters/registry-adapter.ts +43 -0
- package/src/alias-resolver.ts +37 -0
- package/src/analysis-registry.ts +68 -0
- package/src/analyzer.ts +399 -0
- package/src/builtins.ts +111 -0
- package/src/cel-environment.ts +70 -0
- package/src/definition-registry.ts +170 -0
- package/src/dependency-graph.ts +187 -0
- package/src/index.ts +17 -0
- package/src/manifest-loader.ts +203 -0
- package/src/normalize-inline-resources.ts +170 -0
- package/src/precompile.ts +54 -0
- package/src/reference-field-map.ts +147 -0
- package/src/schema-compat.ts +264 -0
- package/src/scope-resolver.ts +13 -0
- package/src/types.ts +68 -0
- package/src/validate-cel-context.ts +142 -0
- package/src/validate-references.ts +311 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# SUSTAINABLE USE LICENSE (Fair-code)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DiglyAI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify, and distribute the Software for any purpose—including commercial purposes—subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
1. ANTI-COMPETITION RESTRICTION: The Software may not be provided to third parties as a managed service, commercial SaaS (Software-as-a-Service), PaaS (Platform-as-a-Service), BaaS (Backend-as-a-Service), or similar offering where the primary value provided to the user is the functionality of the Software itself, without a separate commercial license from the copyright holder.
|
|
8
|
+
|
|
9
|
+
2. PERMITTED COMMERCIAL USE: You are free to use the Software to build, host, and monetize your own commercial applications, products, and services, provided such use does not violate Clause 1.
|
|
10
|
+
|
|
11
|
+
3. ATTRIBUTION: This copyright notice and license must be included in all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
4. CONTRIBUTIONS: Contributions to the Software are welcome and encouraged. By contributing, you agree that your contributions may be incorporated into the Software and distributed under this license.
|
|
14
|
+
|
|
15
|
+
5. DISCLAIMER: The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the Software or the use or other dealings in the Software.
|
|
16
|
+
|
|
17
|
+
For commercial licensing, managed hosting exemptions, or enterprise inquiries, please contact DiglyAI.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ManifestAdapter } from "../types.js";
|
|
2
|
+
export declare class HttpAdapter implements ManifestAdapter {
|
|
3
|
+
supports(url: string): boolean;
|
|
4
|
+
read(url: string): Promise<{
|
|
5
|
+
text: string;
|
|
6
|
+
source: string;
|
|
7
|
+
}>;
|
|
8
|
+
resolveRelative(base: string, relative: string): string;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=http-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/http-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,qBAAa,WAAY,YAAW,eAAe;IACjD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAWlE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;CAIxD"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class HttpAdapter {
|
|
2
|
+
supports(url) {
|
|
3
|
+
return url.startsWith("http://") || url.startsWith("https://");
|
|
4
|
+
}
|
|
5
|
+
async read(url) {
|
|
6
|
+
const fetchUrl = url.includes(".yaml") ? url : `${url}/module.yaml`;
|
|
7
|
+
const response = await fetch(fetchUrl);
|
|
8
|
+
if (!response.ok) {
|
|
9
|
+
throw new Error(`Failed to fetch manifest from ${fetchUrl}: ${response.status} ${response.statusText}`);
|
|
10
|
+
}
|
|
11
|
+
return { text: await response.text(), source: fetchUrl };
|
|
12
|
+
}
|
|
13
|
+
resolveRelative(base, relative) {
|
|
14
|
+
const baseWithSlash = base.endsWith("/") ? base : `${base}/`;
|
|
15
|
+
return new URL(relative, baseWithSlash).href;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ManifestAdapter } from "../types.js";
|
|
2
|
+
/** Node.js fs-based ManifestAdapter for local files. Not browser-compatible. */
|
|
3
|
+
export declare class NodeAdapter implements ManifestAdapter {
|
|
4
|
+
private readonly cwd;
|
|
5
|
+
constructor(cwd?: string);
|
|
6
|
+
supports(url: string): boolean;
|
|
7
|
+
read(url: string): Promise<{
|
|
8
|
+
text: string;
|
|
9
|
+
source: string;
|
|
10
|
+
}>;
|
|
11
|
+
resolveRelative(base: string, relative: string): string;
|
|
12
|
+
}
|
|
13
|
+
/** @deprecated Use `new NodeAdapter(cwd)` instead */
|
|
14
|
+
export declare function createNodeAdapter(cwd?: string): ManifestAdapter;
|
|
15
|
+
//# sourceMappingURL=node-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/node-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,gFAAgF;AAChF,qBAAa,WAAY,YAAW,eAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,GAAE,MAAsB;IAExD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAUxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IASlE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;CAKxD;AAED,qDAAqD;AACrD,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAsB,GAAG,eAAe,CAE9E"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
/** Node.js fs-based ManifestAdapter for local files. Not browser-compatible. */
|
|
4
|
+
export class NodeAdapter {
|
|
5
|
+
cwd;
|
|
6
|
+
constructor(cwd = process.cwd()) {
|
|
7
|
+
this.cwd = cwd;
|
|
8
|
+
}
|
|
9
|
+
supports(url) {
|
|
10
|
+
return (url.startsWith("file://") ||
|
|
11
|
+
url.startsWith("/") ||
|
|
12
|
+
url.startsWith("./") ||
|
|
13
|
+
url.startsWith("../") ||
|
|
14
|
+
(!url.includes("://") && !url.includes("@")));
|
|
15
|
+
}
|
|
16
|
+
async read(url) {
|
|
17
|
+
const filePath = url.startsWith("file://") ? new URL(url).pathname : url;
|
|
18
|
+
const stat = await fs.stat(filePath).catch(() => null);
|
|
19
|
+
const resolvedPath = stat?.isDirectory() ? path.join(filePath, "module.yaml") : filePath;
|
|
20
|
+
const text = await fs.readFile(resolvedPath, "utf8");
|
|
21
|
+
return { text, source: resolvedPath };
|
|
22
|
+
}
|
|
23
|
+
resolveRelative(base, relative) {
|
|
24
|
+
const basePath = base.startsWith("file://") ? new URL(base).pathname : base;
|
|
25
|
+
const baseDir = path.dirname(path.resolve(this.cwd, basePath));
|
|
26
|
+
return path.resolve(baseDir, relative);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** @deprecated Use `new NodeAdapter(cwd)` instead */
|
|
30
|
+
export function createNodeAdapter(cwd = process.cwd()) {
|
|
31
|
+
return new NodeAdapter(cwd);
|
|
32
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ManifestAdapter } from "../types.js";
|
|
2
|
+
export declare class RegistryAdapter implements ManifestAdapter {
|
|
3
|
+
supports(url: string): boolean;
|
|
4
|
+
read(moduleRef: string): Promise<{
|
|
5
|
+
text: string;
|
|
6
|
+
source: string;
|
|
7
|
+
}>;
|
|
8
|
+
resolveRelative(base: string, relative: string): string;
|
|
9
|
+
private toRegistryUrl;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=registry-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/registry-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAInD,qBAAa,eAAgB,YAAW,eAAe;IACrD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAWxB,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAWxE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAQvD,OAAO,CAAC,aAAa;CAOtB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const REGISTRY_BASE = "https://registry.telo.run";
|
|
2
|
+
export class RegistryAdapter {
|
|
3
|
+
supports(url) {
|
|
4
|
+
return (!url.startsWith("http://") &&
|
|
5
|
+
!url.startsWith("https://") &&
|
|
6
|
+
!url.startsWith("/") &&
|
|
7
|
+
!url.startsWith(".") &&
|
|
8
|
+
url.includes("@") &&
|
|
9
|
+
url.includes("/"));
|
|
10
|
+
}
|
|
11
|
+
async read(moduleRef) {
|
|
12
|
+
const fetchUrl = this.toRegistryUrl(moduleRef);
|
|
13
|
+
const response = await fetch(fetchUrl);
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
throw new Error(`Failed to fetch manifest ${moduleRef}: ${response.status} ${response.statusText}`);
|
|
16
|
+
}
|
|
17
|
+
return { text: await response.text(), source: fetchUrl };
|
|
18
|
+
}
|
|
19
|
+
resolveRelative(base, relative) {
|
|
20
|
+
const baseUrl = this.supports(base)
|
|
21
|
+
? this.toRegistryUrl(base).replace("/module.yaml", "")
|
|
22
|
+
: base;
|
|
23
|
+
const baseWithSlash = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
24
|
+
return new URL(relative, baseWithSlash).href;
|
|
25
|
+
}
|
|
26
|
+
toRegistryUrl(moduleRef) {
|
|
27
|
+
const atIdx = moduleRef.lastIndexOf("@");
|
|
28
|
+
const modulePath = moduleRef.slice(0, atIdx);
|
|
29
|
+
const version = moduleRef.slice(atIdx + 1);
|
|
30
|
+
const versionSegment = version.startsWith("v") ? version.substring(1) : version;
|
|
31
|
+
return `${REGISTRY_BASE}/${modulePath}/${versionSegment}/module.yaml`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Pure alias → real module name resolver.
|
|
2
|
+
* Ported from ModuleContext.resolveKind() without any lifecycle dependency. */
|
|
3
|
+
export declare class AliasResolver {
|
|
4
|
+
private readonly importAliases;
|
|
5
|
+
private readonly importedKinds;
|
|
6
|
+
registerImport(alias: string, targetModule: string, exportedKinds: string[]): void;
|
|
7
|
+
/** Resolves "Http.Api" → "http-server.Api". Returns undefined if alias is unknown. */
|
|
8
|
+
resolveKind(kind: string): string | undefined;
|
|
9
|
+
hasAlias(alias: string): boolean;
|
|
10
|
+
knownAliases(): string[];
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=alias-resolver.d.ts.map
|
|
@@ -0,0 +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;IAEhE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,IAAI;IAOlF,sFAAsF;IACtF,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAe7C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIhC,YAAY,IAAI,MAAM,EAAE;CAGzB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Pure alias → real module name resolver.
|
|
2
|
+
* Ported from ModuleContext.resolveKind() without any lifecycle dependency. */
|
|
3
|
+
export class AliasResolver {
|
|
4
|
+
importAliases = new Map();
|
|
5
|
+
importedKinds = new Map();
|
|
6
|
+
registerImport(alias, targetModule, exportedKinds) {
|
|
7
|
+
this.importAliases.set(alias, targetModule);
|
|
8
|
+
if (exportedKinds.length > 0) {
|
|
9
|
+
this.importedKinds.set(alias, new Set(exportedKinds));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** Resolves "Http.Api" → "http-server.Api". Returns undefined if alias is unknown. */
|
|
13
|
+
resolveKind(kind) {
|
|
14
|
+
if (!kind) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const dot = kind.indexOf(".");
|
|
18
|
+
if (dot === -1)
|
|
19
|
+
return undefined;
|
|
20
|
+
const prefix = kind.slice(0, dot);
|
|
21
|
+
const suffix = kind.slice(dot + 1);
|
|
22
|
+
const realModule = this.importAliases.get(prefix);
|
|
23
|
+
if (!realModule)
|
|
24
|
+
return undefined;
|
|
25
|
+
const allowed = this.importedKinds.get(prefix);
|
|
26
|
+
if (allowed !== undefined && !allowed.has(suffix))
|
|
27
|
+
return undefined;
|
|
28
|
+
return `${realModule}.${suffix}`;
|
|
29
|
+
}
|
|
30
|
+
hasAlias(alias) {
|
|
31
|
+
return this.importAliases.has(alias);
|
|
32
|
+
}
|
|
33
|
+
knownAliases() {
|
|
34
|
+
return Array.from(this.importAliases.keys());
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ResourceDefinition, ResourceManifest } from "@telorun/sdk";
|
|
2
|
+
import type { AnalysisContext } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Accumulates type and alias knowledge for a running kernel or analysis session.
|
|
5
|
+
* Wraps AliasResolver and DefinitionRegistry into a single domain-level interface
|
|
6
|
+
* so callers never touch the raw registries directly.
|
|
7
|
+
*/
|
|
8
|
+
export declare class AnalysisRegistry {
|
|
9
|
+
private readonly defs;
|
|
10
|
+
private readonly aliases;
|
|
11
|
+
registerDefinition(def: ResourceDefinition): void;
|
|
12
|
+
registerModuleIdentity(namespace: string | null, name: string): void;
|
|
13
|
+
registerImport(alias: string, target: string, kinds: string[]): void;
|
|
14
|
+
resolveKind(kind: string): string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Iterates a resource's reference and scope fields as declared by its definition.
|
|
17
|
+
* Calls onRef for each plain reference field and onScope for each scope field.
|
|
18
|
+
*/
|
|
19
|
+
iterateFieldEntries(resource: ResourceManifest, onRef: (fieldPath: string) => void, onScope: (fieldPath: string) => void): void;
|
|
20
|
+
/**
|
|
21
|
+
* Returns the built-in kernel definitions. The underlying DefinitionRegistry already
|
|
22
|
+
* seeds these on construction; this method exposes them so callers (e.g. the kernel's
|
|
23
|
+
* controller registry) can iterate them without importing KERNEL_BUILTINS directly.
|
|
24
|
+
*/
|
|
25
|
+
builtinDefinitions(): ResourceDefinition[];
|
|
26
|
+
/** @internal Bridge for StaticAnalyzer — do not use outside the analyzer package. */
|
|
27
|
+
_context(): AnalysisContext;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=analysis-registry.d.ts.map
|
|
@@ -0,0 +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,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;GAIG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAE/C,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;IAIpE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C;;;OAGG;IACH,mBAAmB,CACjB,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,EAClC,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GACnC,IAAI;IAcP;;;;OAIG;IACH,kBAAkB,IAAI,kBAAkB,EAAE;IAI1C,qFAAqF;IACrF,QAAQ,IAAI,eAAe;CAG5B"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { AliasResolver } from "./alias-resolver.js";
|
|
2
|
+
import { KERNEL_BUILTINS } from "./builtins.js";
|
|
3
|
+
import { DefinitionRegistry } from "./definition-registry.js";
|
|
4
|
+
import { isRefEntry, isScopeEntry } from "./reference-field-map.js";
|
|
5
|
+
/**
|
|
6
|
+
* Accumulates type and alias knowledge for a running kernel or analysis session.
|
|
7
|
+
* Wraps AliasResolver and DefinitionRegistry into a single domain-level interface
|
|
8
|
+
* so callers never touch the raw registries directly.
|
|
9
|
+
*/
|
|
10
|
+
export class AnalysisRegistry {
|
|
11
|
+
defs = new DefinitionRegistry();
|
|
12
|
+
aliases = new AliasResolver();
|
|
13
|
+
registerDefinition(def) {
|
|
14
|
+
this.defs.register(def);
|
|
15
|
+
}
|
|
16
|
+
registerModuleIdentity(namespace, name) {
|
|
17
|
+
this.defs.registerModuleIdentity(namespace, name);
|
|
18
|
+
}
|
|
19
|
+
registerImport(alias, target, kinds) {
|
|
20
|
+
this.aliases.registerImport(alias, target, kinds);
|
|
21
|
+
}
|
|
22
|
+
resolveKind(kind) {
|
|
23
|
+
return this.aliases.resolveKind(kind);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Iterates a resource's reference and scope fields as declared by its definition.
|
|
27
|
+
* Calls onRef for each plain reference field and onScope for each scope field.
|
|
28
|
+
*/
|
|
29
|
+
iterateFieldEntries(resource, onRef, onScope) {
|
|
30
|
+
const fieldMap = this.defs.getFieldMapForKind(resource.kind, this.aliases);
|
|
31
|
+
if (!fieldMap)
|
|
32
|
+
return;
|
|
33
|
+
for (const [fieldPath, entry] of fieldMap) {
|
|
34
|
+
if (isScopeEntry(entry)) {
|
|
35
|
+
onScope(fieldPath);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (isRefEntry(entry)) {
|
|
39
|
+
onRef(fieldPath);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Returns the built-in kernel definitions. The underlying DefinitionRegistry already
|
|
45
|
+
* seeds these on construction; this method exposes them so callers (e.g. the kernel's
|
|
46
|
+
* controller registry) can iterate them without importing KERNEL_BUILTINS directly.
|
|
47
|
+
*/
|
|
48
|
+
builtinDefinitions() {
|
|
49
|
+
return KERNEL_BUILTINS;
|
|
50
|
+
}
|
|
51
|
+
/** @internal Bridge for StaticAnalyzer — do not use outside the analyzer package. */
|
|
52
|
+
_context() {
|
|
53
|
+
return { aliases: this.aliases, definitions: this.defs };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ResourceManifest } from "@telorun/sdk";
|
|
2
|
+
import { AnalysisRegistry } from "./analysis-registry.js";
|
|
3
|
+
import { type AnalysisDiagnostic, type AnalysisOptions } from "./types.js";
|
|
4
|
+
export declare class StaticAnalyzer {
|
|
5
|
+
analyze(manifests: ResourceManifest[], options?: AnalysisOptions, registry?: AnalysisRegistry): AnalysisDiagnostic[];
|
|
6
|
+
analyzeErrors(manifests: ResourceManifest[], options?: AnalysisOptions, registry?: AnalysisRegistry): AnalysisDiagnostic[];
|
|
7
|
+
normalize(manifests: ResourceManifest[], registry: AnalysisRegistry): ResourceManifest[];
|
|
8
|
+
prepare(manifests: ResourceManifest[], registry: AnalysisRegistry): {
|
|
9
|
+
diagnostics: AnalysisDiagnostic[];
|
|
10
|
+
order: string[] | null;
|
|
11
|
+
cycleError: string | null;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAW1D,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA+I/F,qBAAa,cAAc;IACzB,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IA0MvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAMvB,SAAS,CAAC,SAAS,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,EAAE;IAKxF,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;CAiB5F"}
|
package/dist/analyzer.js
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { AliasResolver } from "./alias-resolver.js";
|
|
2
|
+
import { buildTypedCelEnvironment, celEnvironment } from "./cel-environment.js";
|
|
3
|
+
import { DefinitionRegistry } from "./definition-registry.js";
|
|
4
|
+
import { buildDependencyGraph, formatCycle } from "./dependency-graph.js";
|
|
5
|
+
import { normalizeInlineResources } from "./normalize-inline-resources.js";
|
|
6
|
+
import { celTypeSatisfiesJsonSchema, substituteCelFields, validateAgainstSchema, } from "./schema-compat.js";
|
|
7
|
+
import { DiagnosticSeverity } from "./types.js";
|
|
8
|
+
import { extractAccessChains, pathMatchesScope, validateChainAgainstSchema, } from "./validate-cel-context.js";
|
|
9
|
+
import { validateReferences } from "./validate-references.js";
|
|
10
|
+
const TEMPLATE_REGEX = /\$\{\{\s*([^}]+?)\s*\}\}/g;
|
|
11
|
+
function walkCelExpressions(value, path, cb) {
|
|
12
|
+
if (typeof value === "string") {
|
|
13
|
+
for (const m of value.matchAll(TEMPLATE_REGEX)) {
|
|
14
|
+
cb(m[1].trim(), path);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
else if (Array.isArray(value)) {
|
|
18
|
+
value.forEach((v, i) => walkCelExpressions(v, `${path}[${i}]`, cb));
|
|
19
|
+
}
|
|
20
|
+
else if (value !== null && typeof value === "object") {
|
|
21
|
+
for (const [k, v] of Object.entries(value)) {
|
|
22
|
+
walkCelExpressions(v, path ? `${path}.${k}` : k, cb);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const SOURCE = "telo-analyzer";
|
|
27
|
+
/**
|
|
28
|
+
* Walk a JSON Schema tree and collect all `x-telo-context` annotations,
|
|
29
|
+
* returning them as `{ scope, schema }` pairs using JSONPath-style scopes —
|
|
30
|
+
* the same format the analyzer uses for CEL context validation.
|
|
31
|
+
*/
|
|
32
|
+
function extractContextsFromSchema(schema, path = "$") {
|
|
33
|
+
if (!schema || typeof schema !== "object")
|
|
34
|
+
return [];
|
|
35
|
+
const results = [];
|
|
36
|
+
if (schema["x-telo-context"]) {
|
|
37
|
+
results.push({ scope: path, schema: schema["x-telo-context"] });
|
|
38
|
+
}
|
|
39
|
+
if (schema.properties) {
|
|
40
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
|
41
|
+
results.push(...extractContextsFromSchema(value, `${path}.${key}`));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (schema.items && typeof schema.items === "object") {
|
|
45
|
+
results.push(...extractContextsFromSchema(schema.items, `${path}[*]`));
|
|
46
|
+
}
|
|
47
|
+
for (const key of ["oneOf", "anyOf", "allOf"]) {
|
|
48
|
+
if (Array.isArray(schema[key])) {
|
|
49
|
+
for (const subschema of schema[key]) {
|
|
50
|
+
results.push(...extractContextsFromSchema(subschema, path));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return results;
|
|
55
|
+
}
|
|
56
|
+
const CEL_PURE_RE = /^\s*\$\{\{[^}]*\}\}\s*$/;
|
|
57
|
+
const CEL_EXPR_RE = /\$\{\{\s*([^}]+?)\s*\}\}/;
|
|
58
|
+
/** Recursively walk `data`+`schema` together, type-checking every pure CEL template
|
|
59
|
+
* string via `env.check()`. Returns `SchemaIssue[]` for any type mismatches found. */
|
|
60
|
+
function collectCelTypeIssues(data, schema, path, definition, manifest, baseEnv) {
|
|
61
|
+
const issues = [];
|
|
62
|
+
if (typeof data === "string" && CEL_PURE_RE.test(data)) {
|
|
63
|
+
const exprMatch = data.match(CEL_EXPR_RE);
|
|
64
|
+
if (exprMatch) {
|
|
65
|
+
const expr = exprMatch[1].trim();
|
|
66
|
+
// Merge x-telo-context variables for this path if applicable
|
|
67
|
+
let typedEnv = baseEnv;
|
|
68
|
+
if (definition.schema) {
|
|
69
|
+
for (const ctx of extractContextsFromSchema(definition.schema)) {
|
|
70
|
+
if (!pathMatchesScope(path, ctx.scope))
|
|
71
|
+
continue;
|
|
72
|
+
typedEnv = buildTypedCelEnvironment(manifest, ctx.schema);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
let checkResult;
|
|
77
|
+
try {
|
|
78
|
+
checkResult = typedEnv.check(expr);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
/* degrade gracefully */
|
|
82
|
+
}
|
|
83
|
+
if (checkResult?.valid && checkResult.type && schema) {
|
|
84
|
+
const celType = checkResult.type.split("<")[0];
|
|
85
|
+
if (!celTypeSatisfiesJsonSchema(celType, schema)) {
|
|
86
|
+
issues.push({
|
|
87
|
+
message: `CEL returns '${checkResult.type}' but field expects '${schema.type ?? "unknown"}'`,
|
|
88
|
+
path,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return issues;
|
|
94
|
+
}
|
|
95
|
+
if (Array.isArray(data)) {
|
|
96
|
+
const itemSchema = (schema.items ?? {});
|
|
97
|
+
for (let i = 0; i < data.length; i++) {
|
|
98
|
+
issues.push(...collectCelTypeIssues(data[i], itemSchema, `${path}[${i}]`, definition, manifest, baseEnv));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if (data !== null && typeof data === "object") {
|
|
102
|
+
const props = (schema.properties ?? {});
|
|
103
|
+
for (const [k, v] of Object.entries(data)) {
|
|
104
|
+
issues.push(...collectCelTypeIssues(v, (props[k] ?? {}), path ? `${path}.${k}` : k, definition, manifest, baseEnv));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return issues;
|
|
108
|
+
}
|
|
109
|
+
export class StaticAnalyzer {
|
|
110
|
+
analyze(manifests, options, registry) {
|
|
111
|
+
const diagnostics = [];
|
|
112
|
+
// Use pre-seeded registries from the provided AnalysisRegistry, or create fresh ones.
|
|
113
|
+
// New aliases/definitions found in the manifests are accumulated into the provided instance
|
|
114
|
+
// so state builds up across successive calls (e.g. incremental editor validation).
|
|
115
|
+
const ctx = registry?._context();
|
|
116
|
+
const aliases = ctx?.aliases ?? new AliasResolver();
|
|
117
|
+
const defs = ctx?.definitions ?? new DefinitionRegistry();
|
|
118
|
+
// Register module identities and aliases.
|
|
119
|
+
// The root Kernel.Module provides its own identity; imported modules surface their
|
|
120
|
+
// identity via resolvedModuleName/resolvedNamespace stamped onto the Kernel.Import
|
|
121
|
+
// by the loader (so we don't need to include imported Kernel.Module manifests in
|
|
122
|
+
// the analysis set, avoiding false reference errors in the parent context).
|
|
123
|
+
for (const m of manifests) {
|
|
124
|
+
if (m.kind === "Kernel.Module") {
|
|
125
|
+
const namespace = m.metadata.namespace ?? null;
|
|
126
|
+
const moduleName = m.metadata.name;
|
|
127
|
+
if (moduleName)
|
|
128
|
+
defs.registerModuleIdentity(namespace, moduleName);
|
|
129
|
+
}
|
|
130
|
+
if (m.kind === "Kernel.Import") {
|
|
131
|
+
const alias = m.metadata.name;
|
|
132
|
+
const source = m.source;
|
|
133
|
+
const exportedKinds = m.exports?.kinds ?? [];
|
|
134
|
+
const resolvedModuleName = m.metadata.resolvedModuleName;
|
|
135
|
+
const resolvedNamespace = m.metadata.resolvedNamespace;
|
|
136
|
+
if (alias && source) {
|
|
137
|
+
const targetModule = resolvedModuleName ?? source.split("/").filter(Boolean).pop() ?? source;
|
|
138
|
+
aliases.registerImport(alias, targetModule, exportedKinds);
|
|
139
|
+
if (resolvedModuleName) {
|
|
140
|
+
defs.registerModuleIdentity(resolvedNamespace ?? null, resolvedModuleName);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Register definitions from Kernel.Definition resources.
|
|
146
|
+
// Normalize alias-prefixed `capability` to canonical form so extendedBy lookup works
|
|
147
|
+
// (e.g. "Workflow.Backend" → "workflow.Backend" when "Workflow" is a known alias).
|
|
148
|
+
for (const m of manifests) {
|
|
149
|
+
if (m.kind === "Kernel.Definition") {
|
|
150
|
+
const def = m;
|
|
151
|
+
const resolvedCapability = def.capability
|
|
152
|
+
? (aliases.resolveKind(def.capability) ?? def.capability)
|
|
153
|
+
: def.capability;
|
|
154
|
+
defs.register(resolvedCapability !== def.capability ? { ...def, capability: resolvedCapability } : def);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Phase 2: extract inline resources from x-telo-ref slots into first-class manifests
|
|
158
|
+
const allManifests = normalizeInlineResources(manifests, defs, aliases);
|
|
159
|
+
// Build a name→manifest map for looking up referenced resources
|
|
160
|
+
const byName = new Map();
|
|
161
|
+
for (const m of allManifests) {
|
|
162
|
+
if (m.metadata?.name) {
|
|
163
|
+
byName.set(m.metadata.name, m);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Validate each non-definition, non-system resource
|
|
167
|
+
for (const m of allManifests) {
|
|
168
|
+
if (!m.kind || !m.metadata?.name) {
|
|
169
|
+
diagnostics.push({
|
|
170
|
+
severity: DiagnosticSeverity.Error,
|
|
171
|
+
code: "MISSING_KIND_OR_NAME",
|
|
172
|
+
source: SOURCE,
|
|
173
|
+
message: "Resource is missing required 'kind' or 'metadata.name' field.",
|
|
174
|
+
data: { path: !m.kind ? "kind" : "metadata.name" },
|
|
175
|
+
});
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (m.kind === "Kernel.Definition" || m.kind === "Kernel.Abstract") {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const resource = { kind: m.kind, name: m.metadata?.name };
|
|
182
|
+
// Resolve kind through alias if needed; direct lookup takes priority so that
|
|
183
|
+
// aliases whose name matches the module name (the common case) work without
|
|
184
|
+
// path-derived name mangling.
|
|
185
|
+
const resolvedKind = aliases.resolveKind(m.kind);
|
|
186
|
+
const definition = defs.resolve(m.kind) ?? (resolvedKind ? defs.resolve(resolvedKind) : undefined);
|
|
187
|
+
if (!definition) {
|
|
188
|
+
const knownAliases = aliases.knownAliases();
|
|
189
|
+
const knownKinds = defs.kinds();
|
|
190
|
+
const parts = [];
|
|
191
|
+
if (knownAliases.length > 0)
|
|
192
|
+
parts.push(`imports: ${knownAliases.join(", ")}`);
|
|
193
|
+
if (knownKinds.length > 0)
|
|
194
|
+
parts.push(`kinds: ${knownKinds.join(", ")}`);
|
|
195
|
+
const hint = parts.length > 0 ? ` Known ${parts.join(" | ")}` : "";
|
|
196
|
+
diagnostics.push({
|
|
197
|
+
severity: DiagnosticSeverity.Error,
|
|
198
|
+
code: "UNDEFINED_KIND",
|
|
199
|
+
source: SOURCE,
|
|
200
|
+
message: `No Kernel.Definition found for kind '${m.kind}'.${hint}`,
|
|
201
|
+
data: { resource, path: "kind" },
|
|
202
|
+
});
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
// Validate resource config against definition schema.
|
|
206
|
+
// `kind` and `metadata` are implicit on every resource — inject them so module
|
|
207
|
+
// authors don't have to repeat them when using additionalProperties: false.
|
|
208
|
+
if (definition.schema) {
|
|
209
|
+
const schema = definition.schema.additionalProperties === false
|
|
210
|
+
? {
|
|
211
|
+
...definition.schema,
|
|
212
|
+
properties: {
|
|
213
|
+
kind: { type: "string" },
|
|
214
|
+
metadata: { type: "object" },
|
|
215
|
+
...definition.schema.properties,
|
|
216
|
+
},
|
|
217
|
+
}
|
|
218
|
+
: definition.schema;
|
|
219
|
+
// Phase 1: CEL type checking — walk data+schema together, check env.check() return types
|
|
220
|
+
const baseEnv = buildTypedCelEnvironment(m);
|
|
221
|
+
const celIssues = collectCelTypeIssues(m, schema, "", definition, m, baseEnv);
|
|
222
|
+
// Phase 2+3: AJV on substituted data — CEL fields replaced with typed placeholders
|
|
223
|
+
const ajvIssues = validateAgainstSchema(substituteCelFields(m, schema), schema);
|
|
224
|
+
const issues = [...celIssues, ...ajvIssues];
|
|
225
|
+
for (const issue of issues) {
|
|
226
|
+
diagnostics.push({
|
|
227
|
+
severity: DiagnosticSeverity.Error,
|
|
228
|
+
code: "SCHEMA_VIOLATION",
|
|
229
|
+
source: SOURCE,
|
|
230
|
+
message: `${m.kind}/${resource.name}: ${issue.message}`,
|
|
231
|
+
data: { resource, path: issue.path },
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// (Invocation context compatibility check is handled via x-telo-context in the CEL pass below)
|
|
236
|
+
}
|
|
237
|
+
// Validate CEL syntax and context variable access in all manifests
|
|
238
|
+
for (const m of allManifests) {
|
|
239
|
+
const resource = { kind: m.kind, name: m.metadata?.name };
|
|
240
|
+
const resolvedKind = aliases.resolveKind(m.kind);
|
|
241
|
+
const mDefinition = defs.resolve(m.kind) ?? (resolvedKind ? defs.resolve(resolvedKind) : undefined);
|
|
242
|
+
walkCelExpressions(m, "", (expr, path) => {
|
|
243
|
+
let parsed;
|
|
244
|
+
try {
|
|
245
|
+
parsed = celEnvironment.parse(expr);
|
|
246
|
+
}
|
|
247
|
+
catch (e) {
|
|
248
|
+
diagnostics.push({
|
|
249
|
+
severity: DiagnosticSeverity.Error,
|
|
250
|
+
code: "CEL_SYNTAX_ERROR",
|
|
251
|
+
source: SOURCE,
|
|
252
|
+
message: `CEL syntax error at ${path}: ${e instanceof Error ? e.message : String(e)}`,
|
|
253
|
+
data: { resource, path },
|
|
254
|
+
});
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const contexts = mDefinition?.schema ? extractContextsFromSchema(mDefinition.schema) : [];
|
|
258
|
+
const invocationContext = m.metadata?.xTeloInvocationContext;
|
|
259
|
+
if (contexts.length === 0 && !invocationContext)
|
|
260
|
+
return;
|
|
261
|
+
let matchedContext;
|
|
262
|
+
for (const ctx of contexts) {
|
|
263
|
+
if (pathMatchesScope(path, ctx.scope)) {
|
|
264
|
+
matchedContext = ctx.schema;
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (!matchedContext)
|
|
269
|
+
matchedContext = invocationContext;
|
|
270
|
+
if (!matchedContext)
|
|
271
|
+
return;
|
|
272
|
+
for (const chain of extractAccessChains(parsed.ast)) {
|
|
273
|
+
const err = validateChainAgainstSchema(chain, matchedContext);
|
|
274
|
+
if (!err)
|
|
275
|
+
continue;
|
|
276
|
+
diagnostics.push({
|
|
277
|
+
severity: DiagnosticSeverity.Error,
|
|
278
|
+
code: "CEL_UNKNOWN_FIELD",
|
|
279
|
+
source: SOURCE,
|
|
280
|
+
message: `${m.kind}/${resource.name}: CEL at '${path}': ${err}`,
|
|
281
|
+
data: { resource, path },
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
// Validate resource references (Phase 3)
|
|
287
|
+
diagnostics.push(...validateReferences(allManifests, { aliases, definitions: defs }));
|
|
288
|
+
return diagnostics;
|
|
289
|
+
}
|
|
290
|
+
analyzeErrors(manifests, options, registry) {
|
|
291
|
+
return this.analyze(manifests, options, registry).filter((d) => d.severity === DiagnosticSeverity.Error);
|
|
292
|
+
}
|
|
293
|
+
normalize(manifests, registry) {
|
|
294
|
+
const ctx = registry._context();
|
|
295
|
+
return normalizeInlineResources(manifests, ctx.definitions, ctx.aliases);
|
|
296
|
+
}
|
|
297
|
+
prepare(manifests, registry) {
|
|
298
|
+
const ctx = registry._context();
|
|
299
|
+
const diagnostics = validateReferences(manifests, ctx);
|
|
300
|
+
const errors = diagnostics.filter((d) => d.severity === DiagnosticSeverity.Error);
|
|
301
|
+
if (errors.length > 0) {
|
|
302
|
+
return { diagnostics: errors, order: null, cycleError: null };
|
|
303
|
+
}
|
|
304
|
+
const graph = buildDependencyGraph(manifests, ctx.definitions, ctx.aliases);
|
|
305
|
+
if (graph.cycle) {
|
|
306
|
+
return { diagnostics: [], order: null, cycleError: formatCycle(graph.cycle) };
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
diagnostics: [],
|
|
310
|
+
order: graph.order ? graph.order.map((n) => n.name) : null,
|
|
311
|
+
cycleError: null,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,EA4G/C,CAAC"}
|