@telorun/analyzer 0.1.3 → 0.2.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/README.md +3 -3
- package/dist/analyzer.d.ts +6 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +45 -25
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +56 -24
- package/dist/cel-environment.d.ts +12 -5
- package/dist/cel-environment.d.ts.map +1 -1
- package/dist/cel-environment.js +31 -17
- package/dist/definition-registry.d.ts +5 -5
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +10 -10
- package/dist/dependency-graph.d.ts.map +1 -1
- package/dist/dependency-graph.js +9 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/kernel-globals.d.ts +6 -2
- package/dist/kernel-globals.d.ts.map +1 -1
- package/dist/kernel-globals.js +14 -8
- package/dist/manifest-loader.d.ts +9 -1
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +165 -11
- package/dist/module-kinds.d.ts +4 -0
- package/dist/module-kinds.d.ts.map +1 -0
- package/dist/module-kinds.js +4 -0
- package/dist/normalize-inline-resources.d.ts.map +1 -1
- package/dist/normalize-inline-resources.js +6 -1
- package/dist/precompile.d.ts +3 -2
- package/dist/precompile.d.ts.map +1 -1
- package/dist/precompile.js +13 -11
- package/dist/reference-field-map.d.ts +1 -1
- package/dist/resolve-throws-union.d.ts +30 -0
- package/dist/resolve-throws-union.d.ts.map +1 -0
- package/dist/resolve-throws-union.js +252 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validate-cel-context.js +1 -1
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +19 -12
- package/dist/validate-throws-coverage.d.ts +8 -0
- package/dist/validate-throws-coverage.d.ts.map +1 -0
- package/dist/validate-throws-coverage.js +461 -0
- package/package.json +3 -3
- package/src/analyzer.ts +60 -26
- package/src/builtins.ts +56 -24
- package/src/cel-environment.ts +40 -17
- package/src/definition-registry.ts +10 -10
- package/src/dependency-graph.ts +9 -2
- package/src/index.ts +2 -1
- package/src/kernel-globals.ts +19 -10
- package/src/manifest-loader.ts +202 -17
- package/src/module-kinds.ts +6 -0
- package/src/normalize-inline-resources.ts +6 -1
- package/src/precompile.ts +14 -11
- package/src/reference-field-map.ts +1 -1
- package/src/resolve-throws-union.ts +345 -0
- package/src/types.ts +13 -0
- package/src/validate-cel-context.ts +1 -1
- package/src/validate-references.ts +19 -12
- package/src/validate-throws-coverage.ts +565 -0
- package/dist/adapters/node-adapter.d.ts +0 -15
- package/dist/adapters/node-adapter.d.ts.map +0 -1
- package/dist/adapters/node-adapter.js +0 -33
- package/src/adapters/node-adapter.ts +0 -38
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { HttpAdapter } from "./adapters/http-adapter.js";
|
|
2
|
-
export { createNodeAdapter, NodeAdapter } from "./adapters/node-adapter.js";
|
|
3
2
|
export { RegistryAdapter } from "./adapters/registry-adapter.js";
|
|
4
3
|
export { AnalysisRegistry } from "./analysis-registry.js";
|
|
5
4
|
export { StaticAnalyzer } from "./analyzer.js";
|
|
6
5
|
export { Loader } from "./manifest-loader.js";
|
|
6
|
+
export { MODULE_KINDS, isModuleKind } from "./module-kinds.js";
|
|
7
7
|
export { DEFAULT_MANIFEST_FILENAME, DiagnosticSeverity } from "./types.js";
|
package/dist/kernel-globals.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { ResourceManifest } from "@telorun/sdk";
|
|
|
6
6
|
* must stay in sync with this list.
|
|
7
7
|
*
|
|
8
8
|
* Note: `env` is only available in the root module context. Child modules
|
|
9
|
-
* loaded via
|
|
9
|
+
* loaded via Telo.Import do not receive host environment variables.
|
|
10
10
|
* There is no `imports` namespace at runtime — import snapshots are stored
|
|
11
11
|
* under `resources.<alias>`.
|
|
12
12
|
*/
|
|
@@ -17,7 +17,11 @@ export declare const KERNEL_GLOBAL_NAMES: readonly ["variables", "secrets", "res
|
|
|
17
17
|
* chain-access validation recognises kernel globals without module authors
|
|
18
18
|
* having to re-declare them.
|
|
19
19
|
*
|
|
20
|
-
* - `variables` / `secrets`: typed from the
|
|
20
|
+
* - `variables` / `secrets`: typed from the root module doc — prefer
|
|
21
|
+
* Telo.Application when present, otherwise fall back to Telo.Library.
|
|
22
|
+
* Applications are the root whose variables/secrets contract governs CEL
|
|
23
|
+
* in the outer module; Libraries are only relevant when the caller scoped
|
|
24
|
+
* the manifest list to a single library's file.
|
|
21
25
|
* - `resources`: enumerates all non-system resource names
|
|
22
26
|
* - `env`: dynamic (runtime env vars, root module only)
|
|
23
27
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kernel-globals.d.ts","sourceRoot":"","sources":["../src/kernel-globals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,uDAAwD,CAAC;
|
|
1
|
+
{"version":3,"file":"kernel-globals.d.ts","sourceRoot":"","sources":["../src/kernel-globals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,uDAAwD,CAAC;AASzF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,gBAAgB,EAAE,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAiCrB;AA2BD;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACjC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CASrB"}
|
package/dist/kernel-globals.js
CHANGED
|
@@ -5,15 +5,16 @@
|
|
|
5
5
|
* must stay in sync with this list.
|
|
6
6
|
*
|
|
7
7
|
* Note: `env` is only available in the root module context. Child modules
|
|
8
|
-
* loaded via
|
|
8
|
+
* loaded via Telo.Import do not receive host environment variables.
|
|
9
9
|
* There is no `imports` namespace at runtime — import snapshots are stored
|
|
10
10
|
* under `resources.<alias>`.
|
|
11
11
|
*/
|
|
12
12
|
export const KERNEL_GLOBAL_NAMES = ["variables", "secrets", "resources", "env"];
|
|
13
13
|
const SYSTEM_KINDS = new Set([
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
14
|
+
"Telo.Definition",
|
|
15
|
+
"Telo.Application",
|
|
16
|
+
"Telo.Library",
|
|
17
|
+
"Telo.Abstract",
|
|
17
18
|
]);
|
|
18
19
|
/**
|
|
19
20
|
* Build a typed JSON Schema describing the kernel globals available in the
|
|
@@ -21,18 +22,23 @@ const SYSTEM_KINDS = new Set([
|
|
|
21
22
|
* chain-access validation recognises kernel globals without module authors
|
|
22
23
|
* having to re-declare them.
|
|
23
24
|
*
|
|
24
|
-
* - `variables` / `secrets`: typed from the
|
|
25
|
+
* - `variables` / `secrets`: typed from the root module doc — prefer
|
|
26
|
+
* Telo.Application when present, otherwise fall back to Telo.Library.
|
|
27
|
+
* Applications are the root whose variables/secrets contract governs CEL
|
|
28
|
+
* in the outer module; Libraries are only relevant when the caller scoped
|
|
29
|
+
* the manifest list to a single library's file.
|
|
25
30
|
* - `resources`: enumerates all non-system resource names
|
|
26
31
|
* - `env`: dynamic (runtime env vars, root module only)
|
|
27
32
|
*/
|
|
28
33
|
export function buildKernelGlobalsSchema(manifests) {
|
|
29
|
-
const moduleManifest = manifests.find((m) => m.kind === "
|
|
34
|
+
const moduleManifest = manifests.find((m) => m.kind === "Telo.Application") ??
|
|
35
|
+
manifests.find((m) => m.kind === "Telo.Library");
|
|
30
36
|
const resourceProps = {};
|
|
31
37
|
for (const m of manifests) {
|
|
32
38
|
const name = m.metadata?.name;
|
|
33
39
|
if (!name || !m.kind)
|
|
34
40
|
continue;
|
|
35
|
-
//
|
|
41
|
+
// Telo.Import snapshots are stored under resources.<alias> at runtime,
|
|
36
42
|
// so they appear here alongside regular resources.
|
|
37
43
|
if (!SYSTEM_KINDS.has(m.kind)) {
|
|
38
44
|
resourceProps[name] = { type: "object", additionalProperties: true };
|
|
@@ -52,7 +58,7 @@ export function buildKernelGlobalsSchema(manifests) {
|
|
|
52
58
|
},
|
|
53
59
|
};
|
|
54
60
|
}
|
|
55
|
-
/** Wrap a JSON Schema property map (like `
|
|
61
|
+
/** Wrap a JSON Schema property map (like `Telo.Application.variables`) into a
|
|
56
62
|
* closed object schema suitable for chain-access validation. Falls back to
|
|
57
63
|
* an open map when the module declares no variables/secrets. */
|
|
58
64
|
function buildSchemaMapSchema(schemaMap) {
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { type ResourceManifest } from "@telorun/sdk";
|
|
2
|
-
import type
|
|
2
|
+
import { type LoadOptions, type LoaderInitOptions, type ManifestAdapter } from "./types.js";
|
|
3
3
|
export declare class Loader {
|
|
4
4
|
private static readonly moduleCache;
|
|
5
5
|
protected adapters: ManifestAdapter[];
|
|
6
|
+
private readonly celEnv;
|
|
6
7
|
constructor(extraAdaptersOrOptions?: ManifestAdapter[] | LoaderInitOptions);
|
|
7
8
|
register(adapter: ManifestAdapter): this;
|
|
8
9
|
private pick;
|
|
9
10
|
resolveEntryPoint(url: string): Promise<string>;
|
|
10
11
|
loadModule(url: string, options?: LoadOptions): Promise<ResourceManifest[]>;
|
|
12
|
+
private resolveIncludes;
|
|
13
|
+
private loadPartialFile;
|
|
14
|
+
loadModuleForFile(fileUrl: string): Promise<{
|
|
15
|
+
ownerUrl: string;
|
|
16
|
+
manifests: ResourceManifest[];
|
|
17
|
+
sourceManifests: Map<string, ResourceManifest[]>;
|
|
18
|
+
} | null>;
|
|
11
19
|
loadModuleGraph(entryUrl: string, onError?: (url: string, error: Error) => void): Promise<Map<string, ResourceManifest[]>>;
|
|
12
20
|
loadManifests(entryUrl: string): Promise<ResourceManifest[]>;
|
|
13
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-loader.d.ts","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"manifest-loader.d.ts","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAOtE,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EAGrB,MAAM,YAAY,CAAC;AASpB,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAG/B;IAEJ,SAAS,CAAC,QAAQ,EAAE,eAAe,EAAE,CAAC;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,sBAAsB,GAAE,eAAe,EAAE,GAAG,iBAAsB;IAmB9E,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAKxC,OAAO,CAAC,IAAI;IAMN,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK/C,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAqGnE,eAAe;YAoBf,eAAe;IAgEvB,iBAAiB,CACrB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,gBAAgB,EAAE,CAAC;QAC9B,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;KAClD,GAAG,IAAI,CAAC;IAiCH,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,GAC5C,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAsCrC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAiEnE"}
|
package/dist/manifest-loader.js
CHANGED
|
@@ -2,10 +2,20 @@ import { isCompiledValue } from "@telorun/sdk";
|
|
|
2
2
|
import { isMap, isPair, isScalar, isSeq, parseAllDocuments } from "yaml";
|
|
3
3
|
import { HttpAdapter } from "./adapters/http-adapter.js";
|
|
4
4
|
import { RegistryAdapter } from "./adapters/registry-adapter.js";
|
|
5
|
+
import { buildCelEnvironment } from "./cel-environment.js";
|
|
6
|
+
import { isModuleKind } from "./module-kinds.js";
|
|
5
7
|
import { precompileDoc } from "./precompile.js";
|
|
8
|
+
import { DEFAULT_MANIFEST_FILENAME, } from "./types.js";
|
|
9
|
+
const SYSTEM_KINDS = new Set([
|
|
10
|
+
"Telo.Application",
|
|
11
|
+
"Telo.Library",
|
|
12
|
+
"Telo.Import",
|
|
13
|
+
"Telo.Definition",
|
|
14
|
+
]);
|
|
6
15
|
export class Loader {
|
|
7
16
|
static moduleCache = new Map();
|
|
8
17
|
adapters;
|
|
18
|
+
celEnv;
|
|
9
19
|
constructor(extraAdaptersOrOptions = []) {
|
|
10
20
|
const options = Array.isArray(extraAdaptersOrOptions)
|
|
11
21
|
? { extraAdapters: extraAdaptersOrOptions }
|
|
@@ -20,6 +30,7 @@ export class Loader {
|
|
|
20
30
|
if (options.extraAdapters?.length) {
|
|
21
31
|
this.adapters.unshift(...options.extraAdapters);
|
|
22
32
|
}
|
|
33
|
+
this.celEnv = buildCelEnvironment(options.celHandlers);
|
|
23
34
|
}
|
|
24
35
|
register(adapter) {
|
|
25
36
|
this.adapters.unshift(adapter);
|
|
@@ -57,7 +68,7 @@ export class Loader {
|
|
|
57
68
|
let compiledDocs;
|
|
58
69
|
if (options?.compile) {
|
|
59
70
|
try {
|
|
60
|
-
const result = precompileDoc(rawDoc);
|
|
71
|
+
const result = precompileDoc(rawDoc, this.celEnv);
|
|
61
72
|
compiledDocs = Array.isArray(result) ? result : [result];
|
|
62
73
|
}
|
|
63
74
|
catch (error) {
|
|
@@ -83,15 +94,17 @@ export class Loader {
|
|
|
83
94
|
resolved.push({ ...manifest, metadata });
|
|
84
95
|
}
|
|
85
96
|
}
|
|
86
|
-
const moduleManifests = resolved.filter((m) => m.kind
|
|
97
|
+
const moduleManifests = resolved.filter((m) => isModuleKind(m.kind));
|
|
87
98
|
if (moduleManifests.length > 1) {
|
|
88
|
-
|
|
99
|
+
const kinds = moduleManifests.map((m) => m.kind).join(", ");
|
|
100
|
+
throw new Error(`File '${source}' contains ${moduleManifests.length} module declarations (${kinds}). ` +
|
|
101
|
+
`A file may declare at most one Telo.Application or Telo.Library.`);
|
|
89
102
|
}
|
|
90
103
|
const moduleManifest = moduleManifests[0];
|
|
91
104
|
const moduleName = moduleManifest?.metadata?.name;
|
|
92
105
|
if (moduleName) {
|
|
93
106
|
for (const manifest of resolved) {
|
|
94
|
-
if (manifest.kind
|
|
107
|
+
if (!isModuleKind(manifest.kind) && !manifest.metadata?.module) {
|
|
95
108
|
const pi = manifest.metadata?.positionIndex;
|
|
96
109
|
manifest.metadata = { ...manifest.metadata, module: moduleName };
|
|
97
110
|
if (pi) {
|
|
@@ -105,9 +118,126 @@ export class Loader {
|
|
|
105
118
|
}
|
|
106
119
|
}
|
|
107
120
|
}
|
|
108
|
-
|
|
121
|
+
// Expand include directives — load partial files into the same module scope.
|
|
122
|
+
// Results with includes are NOT cached because partial file content is not
|
|
123
|
+
// tracked in the cache key — the cache would serve stale data if a partial changes.
|
|
124
|
+
let hasIncludes = false;
|
|
125
|
+
if (moduleManifest) {
|
|
126
|
+
const includePatterns = moduleManifest.include;
|
|
127
|
+
if (includePatterns?.length) {
|
|
128
|
+
hasIncludes = true;
|
|
129
|
+
const adapter = this.pick(source);
|
|
130
|
+
const includedFiles = await this.resolveIncludes(source, includePatterns, adapter);
|
|
131
|
+
for (const includedUrl of includedFiles) {
|
|
132
|
+
const partialManifests = await this.loadPartialFile(includedUrl, moduleName, options);
|
|
133
|
+
resolved.push(...partialManifests);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!hasIncludes) {
|
|
138
|
+
Loader.moduleCache.set(cacheKey, { text, manifests: resolved });
|
|
139
|
+
}
|
|
109
140
|
return cloneManifestArray(resolved);
|
|
110
141
|
}
|
|
142
|
+
async resolveIncludes(ownerSource, patterns, adapter) {
|
|
143
|
+
const hasGlobs = patterns.some((p) => /[*?{}\[\]]/.test(p));
|
|
144
|
+
if (hasGlobs) {
|
|
145
|
+
if (!adapter.expandGlob) {
|
|
146
|
+
throw new Error(`Include patterns in '${ownerSource}' contain globs but the adapter for this source ` +
|
|
147
|
+
`does not support glob expansion. Use explicit file paths instead of patterns like: ` +
|
|
148
|
+
patterns.filter((p) => /[*?{}\[\]]/.test(p)).join(", "));
|
|
149
|
+
}
|
|
150
|
+
return adapter.expandGlob(ownerSource, patterns);
|
|
151
|
+
}
|
|
152
|
+
// Literal relative paths — deduplicate in case the same file appears under multiple patterns.
|
|
153
|
+
return [...new Set(patterns.map((p) => adapter.resolveRelative(ownerSource, p)))];
|
|
154
|
+
}
|
|
155
|
+
async loadPartialFile(url, ownerModuleName, options) {
|
|
156
|
+
const { text, source } = await this.pick(url).read(url);
|
|
157
|
+
const parsedDocuments = parseAllDocuments(text);
|
|
158
|
+
const rawDocs = parsedDocuments.map((d) => d.toJSON());
|
|
159
|
+
const offsets = documentLineOffsets(text);
|
|
160
|
+
const lineOffsets = buildLineOffsets(text);
|
|
161
|
+
const resolved = [];
|
|
162
|
+
let docIdx = 0;
|
|
163
|
+
for (const rawDoc of rawDocs) {
|
|
164
|
+
const currentDocIdx = docIdx++;
|
|
165
|
+
const sourceLine = offsets[currentDocIdx] ?? 0;
|
|
166
|
+
const positionIndex = buildPositionIndex(parsedDocuments[currentDocIdx], lineOffsets);
|
|
167
|
+
if (rawDoc === null || rawDoc === undefined)
|
|
168
|
+
continue;
|
|
169
|
+
const kind = rawDoc.kind;
|
|
170
|
+
if (kind && SYSTEM_KINDS.has(kind)) {
|
|
171
|
+
throw new Error(`Included file '${source}' contains '${kind}' which is not allowed in partial files. ` +
|
|
172
|
+
`Only the owner telo.yaml may declare ${kind} resources.`);
|
|
173
|
+
}
|
|
174
|
+
let compiledDocs;
|
|
175
|
+
if (options?.compile) {
|
|
176
|
+
try {
|
|
177
|
+
const result = precompileDoc(rawDoc, this.celEnv);
|
|
178
|
+
compiledDocs = Array.isArray(result) ? result : [result];
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
throw new Error(`Failed to compile manifest in ${source}: ${error instanceof Error ? error.message : String(error)}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
compiledDocs = [rawDoc];
|
|
186
|
+
}
|
|
187
|
+
for (const doc of compiledDocs) {
|
|
188
|
+
if (doc === null || doc === undefined)
|
|
189
|
+
continue;
|
|
190
|
+
const manifest = doc;
|
|
191
|
+
const metadata = {
|
|
192
|
+
...manifest.metadata,
|
|
193
|
+
source,
|
|
194
|
+
sourceLine,
|
|
195
|
+
...(ownerModuleName && !manifest.metadata?.module ? { module: ownerModuleName } : {}),
|
|
196
|
+
};
|
|
197
|
+
Object.defineProperty(metadata, "positionIndex", {
|
|
198
|
+
value: positionIndex,
|
|
199
|
+
enumerable: false,
|
|
200
|
+
writable: true,
|
|
201
|
+
configurable: true,
|
|
202
|
+
});
|
|
203
|
+
resolved.push({ ...manifest, metadata });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return resolved;
|
|
207
|
+
}
|
|
208
|
+
async loadModuleForFile(fileUrl) {
|
|
209
|
+
// Try loading as a regular module first (it might be a telo.yaml itself).
|
|
210
|
+
// Use loadManifests (not loadModule) so imported definitions are included —
|
|
211
|
+
// otherwise the analyzer won't know about kinds from Telo.Import sources.
|
|
212
|
+
try {
|
|
213
|
+
const docs = await this.loadModule(fileUrl);
|
|
214
|
+
const hasModule = docs.some((d) => isModuleKind(d.kind));
|
|
215
|
+
if (hasModule) {
|
|
216
|
+
const { source } = await this.pick(fileUrl).read(fileUrl);
|
|
217
|
+
const manifests = await this.loadManifests(fileUrl);
|
|
218
|
+
return { ownerUrl: source, manifests, sourceManifests: groupBySource(manifests) };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
// If the file looks like an owner manifest (named telo.yaml), rethrow —
|
|
223
|
+
// a broken owner shouldn't silently fall through to parent lookup.
|
|
224
|
+
const normalized = fileUrl.replace(/\\/g, "/");
|
|
225
|
+
if (normalized.endsWith(`/${DEFAULT_MANIFEST_FILENAME}`) || normalized === DEFAULT_MANIFEST_FILENAME) {
|
|
226
|
+
throw err;
|
|
227
|
+
}
|
|
228
|
+
// Otherwise fall through to owner lookup — this is likely a partial file
|
|
229
|
+
}
|
|
230
|
+
// Find the owning telo.yaml via parent-directory traversal
|
|
231
|
+
const adapter = this.pick(fileUrl);
|
|
232
|
+
if (!adapter.resolveOwnerOf)
|
|
233
|
+
return null;
|
|
234
|
+
const ownerUrl = await adapter.resolveOwnerOf(fileUrl);
|
|
235
|
+
if (!ownerUrl)
|
|
236
|
+
return null;
|
|
237
|
+
// Load the owner module (which will load included files via include expansion)
|
|
238
|
+
const manifests = await this.loadManifests(ownerUrl);
|
|
239
|
+
return { ownerUrl, manifests, sourceManifests: groupBySource(manifests) };
|
|
240
|
+
}
|
|
111
241
|
async loadModuleGraph(entryUrl, onError) {
|
|
112
242
|
const visited = new Set([entryUrl]);
|
|
113
243
|
const result = new Map();
|
|
@@ -116,7 +246,7 @@ export class Loader {
|
|
|
116
246
|
const queue = [...entry];
|
|
117
247
|
while (queue.length > 0) {
|
|
118
248
|
const m = queue.shift();
|
|
119
|
-
if (m.kind !== "
|
|
249
|
+
if (m.kind !== "Telo.Import")
|
|
120
250
|
continue;
|
|
121
251
|
const importSource = m.source;
|
|
122
252
|
if (!importSource)
|
|
@@ -139,7 +269,7 @@ export class Loader {
|
|
|
139
269
|
}
|
|
140
270
|
result.set(importUrl, imported);
|
|
141
271
|
for (const im of imported) {
|
|
142
|
-
if (im.kind === "
|
|
272
|
+
if (im.kind === "Telo.Import")
|
|
143
273
|
queue.push(im);
|
|
144
274
|
}
|
|
145
275
|
}
|
|
@@ -152,7 +282,7 @@ export class Loader {
|
|
|
152
282
|
const queue = [...entry];
|
|
153
283
|
while (queue.length > 0) {
|
|
154
284
|
const m = queue.shift();
|
|
155
|
-
if (m.kind !== "
|
|
285
|
+
if (m.kind !== "Telo.Import")
|
|
156
286
|
continue;
|
|
157
287
|
const importSource = m.source;
|
|
158
288
|
if (!importSource)
|
|
@@ -173,7 +303,18 @@ export class Loader {
|
|
|
173
303
|
e.sourceLine = m.metadata?.sourceLine ?? 0;
|
|
174
304
|
throw e;
|
|
175
305
|
}
|
|
176
|
-
|
|
306
|
+
// Import target must be a Telo.Library. Check the Library branch
|
|
307
|
+
// explicitly rather than "anything that's a module kind" so that a
|
|
308
|
+
// future third kind can't silently slip past as a valid import target.
|
|
309
|
+
const importedLibrary = imported.find((im) => im.kind === "Telo.Library");
|
|
310
|
+
const importedApplication = imported.find((im) => im.kind === "Telo.Application");
|
|
311
|
+
if (importedApplication) {
|
|
312
|
+
const e = new Error(`Telo.Import target '${importSource}' is a Telo.Application. ` +
|
|
313
|
+
`Only Telo.Library modules may be imported. Applications are run directly, not imported.`);
|
|
314
|
+
e.sourceLine = m.metadata?.sourceLine ?? 0;
|
|
315
|
+
throw e;
|
|
316
|
+
}
|
|
317
|
+
const importedModule = importedLibrary;
|
|
177
318
|
if (importedModule?.metadata?.name) {
|
|
178
319
|
const pi = m.metadata?.positionIndex;
|
|
179
320
|
m.metadata = {
|
|
@@ -191,9 +332,9 @@ export class Loader {
|
|
|
191
332
|
}
|
|
192
333
|
}
|
|
193
334
|
for (const im of imported) {
|
|
194
|
-
if (im.kind === "
|
|
335
|
+
if (im.kind === "Telo.Definition")
|
|
195
336
|
importedDefs.push(im);
|
|
196
|
-
if (im.kind === "
|
|
337
|
+
if (im.kind === "Telo.Import")
|
|
197
338
|
queue.push(im);
|
|
198
339
|
}
|
|
199
340
|
}
|
|
@@ -224,6 +365,19 @@ function cloneManifestValue(value) {
|
|
|
224
365
|
}
|
|
225
366
|
return value;
|
|
226
367
|
}
|
|
368
|
+
function groupBySource(manifests) {
|
|
369
|
+
const map = new Map();
|
|
370
|
+
for (const m of manifests) {
|
|
371
|
+
const src = m.metadata?.source ?? "unknown";
|
|
372
|
+
let list = map.get(src);
|
|
373
|
+
if (!list) {
|
|
374
|
+
list = [];
|
|
375
|
+
map.set(src, list);
|
|
376
|
+
}
|
|
377
|
+
list.push(m);
|
|
378
|
+
}
|
|
379
|
+
return map;
|
|
380
|
+
}
|
|
227
381
|
function documentLineOffsets(text) {
|
|
228
382
|
const offsets = [0];
|
|
229
383
|
const lines = text.split("\n");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-kinds.d.ts","sourceRoot":"","sources":["../src/module-kinds.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,+CAAgD,CAAC;AAC1E,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,IAAI,UAAU,CAEzE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize-inline-resources.d.ts","sourceRoot":"","sources":["../src/normalize-inline-resources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"normalize-inline-resources.d.ts","sourceRoot":"","sources":["../src/normalize-inline-resources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAczD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,GACtB,gBAAgB,EAAE,CAkDpB"}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { isRefEntry, isScopeEntry, isInlineResource } from "./reference-field-map.js";
|
|
2
|
-
const SYSTEM_KINDS = new Set([
|
|
2
|
+
const SYSTEM_KINDS = new Set([
|
|
3
|
+
"Telo.Definition",
|
|
4
|
+
"Telo.Application",
|
|
5
|
+
"Telo.Library",
|
|
6
|
+
"Telo.Import",
|
|
7
|
+
]);
|
|
3
8
|
/** Replaces characters outside [a-zA-Z0-9_] with underscores. */
|
|
4
9
|
function sanitizeName(raw) {
|
|
5
10
|
return raw.replace(/[^a-zA-Z0-9_]/g, "_");
|
package/dist/precompile.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { Environment } from "@marcbachmann/cel-js";
|
|
1
2
|
/**
|
|
2
3
|
* Walks a raw YAML document and replaces all "${{ expr }}" strings with
|
|
3
4
|
* CompiledValue wrappers. Throws on CEL syntax errors.
|
|
4
5
|
* Intended to be called once per document at load time.
|
|
5
|
-
*
|
|
6
|
+
* Telo.Definition documents are returned unchanged — their schema fields
|
|
6
7
|
* are static metadata and must not be treated as CEL templates.
|
|
7
8
|
*/
|
|
8
|
-
export declare function precompileDoc(doc: unknown): unknown;
|
|
9
|
+
export declare function precompileDoc(doc: unknown, env: Environment): unknown;
|
|
9
10
|
//# sourceMappingURL=precompile.d.ts.map
|
package/dist/precompile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"precompile.d.ts","sourceRoot":"","sources":["../src/precompile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"precompile.d.ts","sourceRoot":"","sources":["../src/precompile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAKxD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAarE"}
|
package/dist/precompile.js
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import { celEnvironment } from "./cel-environment.js";
|
|
2
1
|
const TEMPLATE_REGEX = /\$\{\{\s*([^}]+?)\s*\}\}/g;
|
|
3
2
|
const EXACT_TEMPLATE_REGEX = /^\s*\$\{\{\s*([^}]+?)\s*\}\}\s*$/;
|
|
4
3
|
/**
|
|
5
4
|
* Walks a raw YAML document and replaces all "${{ expr }}" strings with
|
|
6
5
|
* CompiledValue wrappers. Throws on CEL syntax errors.
|
|
7
6
|
* Intended to be called once per document at load time.
|
|
8
|
-
*
|
|
7
|
+
* Telo.Definition documents are returned unchanged — their schema fields
|
|
9
8
|
* are static metadata and must not be treated as CEL templates.
|
|
10
9
|
*/
|
|
11
|
-
export function precompileDoc(doc) {
|
|
10
|
+
export function precompileDoc(doc, env) {
|
|
12
11
|
if (typeof doc === "string")
|
|
13
|
-
return compileString(doc);
|
|
12
|
+
return compileString(doc, env);
|
|
14
13
|
if (Array.isArray(doc))
|
|
15
|
-
return doc.map(precompileDoc);
|
|
14
|
+
return doc.map((item) => precompileDoc(item, env));
|
|
16
15
|
// Only recurse into plain objects. Class instances (ResourceInstance, ScopeHandle, etc.)
|
|
17
16
|
// are returned as-is — their prototype methods must not be lost by object reconstruction.
|
|
18
17
|
if (doc !== null && typeof doc === "object" && Object.getPrototypeOf(doc) === Object.prototype) {
|
|
19
18
|
const result = {};
|
|
20
19
|
for (const [k, v] of Object.entries(doc)) {
|
|
21
|
-
result[k] = precompileDoc(v);
|
|
20
|
+
result[k] = precompileDoc(v, env);
|
|
22
21
|
}
|
|
23
22
|
return result;
|
|
24
23
|
}
|
|
25
24
|
return doc;
|
|
26
25
|
}
|
|
27
|
-
function compileString(s) {
|
|
26
|
+
function compileString(s, env) {
|
|
28
27
|
if (!s.includes("${{"))
|
|
29
28
|
return s;
|
|
30
29
|
const exact = s.match(EXACT_TEMPLATE_REGEX);
|
|
31
30
|
if (exact) {
|
|
32
|
-
const
|
|
33
|
-
|
|
31
|
+
const expr = exact[1].trim();
|
|
32
|
+
const fn = env.parse(expr);
|
|
33
|
+
return { __compiled: true, source: expr, call: (ctx) => fn(ctx) };
|
|
34
34
|
}
|
|
35
35
|
// Interpolated template — collect literal parts + compiled sub-expressions
|
|
36
36
|
const parts = [];
|
|
@@ -38,14 +38,16 @@ function compileString(s) {
|
|
|
38
38
|
for (const m of s.matchAll(TEMPLATE_REGEX)) {
|
|
39
39
|
if (m.index > last)
|
|
40
40
|
parts.push(s.slice(last, m.index));
|
|
41
|
-
const
|
|
42
|
-
|
|
41
|
+
const expr = m[1].trim();
|
|
42
|
+
const fn = env.parse(expr);
|
|
43
|
+
parts.push({ __compiled: true, source: expr, call: (ctx) => fn(ctx) });
|
|
43
44
|
last = m.index + m[0].length;
|
|
44
45
|
}
|
|
45
46
|
if (last < s.length)
|
|
46
47
|
parts.push(s.slice(last));
|
|
47
48
|
return {
|
|
48
49
|
__compiled: true,
|
|
50
|
+
source: s,
|
|
49
51
|
call: (ctx) => parts.map((p) => (typeof p === "string" ? p : String(p.call(ctx) ?? ""))).join(""),
|
|
50
52
|
};
|
|
51
53
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** An entry for a field that carries one or more x-telo-ref constraints. */
|
|
2
2
|
export interface RefFieldEntry {
|
|
3
|
-
/** One or more canonical ref strings ("namespace/module#TypeName" or "
|
|
3
|
+
/** One or more canonical ref strings ("namespace/module#TypeName" or "telo#TypeName").
|
|
4
4
|
* Multiple entries arise from anyOf branches. */
|
|
5
5
|
refs: string[];
|
|
6
6
|
/** True when the field path traversed through at least one array (path contains "[]"). */
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ResourceManifest } from "@telorun/sdk";
|
|
2
|
+
import type { AliasResolver } from "./alias-resolver.js";
|
|
3
|
+
import type { DefinitionRegistry } from "./definition-registry.js";
|
|
4
|
+
export interface ThrowsCodeMeta {
|
|
5
|
+
data?: Record<string, any>;
|
|
6
|
+
}
|
|
7
|
+
export interface ThrowsUnion {
|
|
8
|
+
/** Code → per-code metadata (data schema, etc). Keys are the declared codes. */
|
|
9
|
+
codes: Map<string, ThrowsCodeMeta>;
|
|
10
|
+
/** True when the union cannot be fully resolved statically — e.g. a
|
|
11
|
+
* `passthrough` call site uses a CEL expression the analyzer can't narrow,
|
|
12
|
+
* an unknown kind was encountered, or a cycle short-circuited resolution.
|
|
13
|
+
* Callers must treat unbounded unions as requiring a catch-all entry. */
|
|
14
|
+
unbounded: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface ResolveCtx {
|
|
17
|
+
allManifests: ResourceManifest[];
|
|
18
|
+
defs: DefinitionRegistry;
|
|
19
|
+
aliases: AliasResolver;
|
|
20
|
+
memo: Map<string, ThrowsUnion>;
|
|
21
|
+
inProgress: Set<string>;
|
|
22
|
+
}
|
|
23
|
+
export declare function createResolveCtx(allManifests: ResourceManifest[], defs: DefinitionRegistry, aliases: AliasResolver): ResolveCtx;
|
|
24
|
+
/** Resolve the effective throw union for a named manifest. The result combines
|
|
25
|
+
* explicit `throws.codes`, `throws.inherit: true` dataflow (step-context
|
|
26
|
+
* traversal with try/catch subtraction), and unbounded markers for
|
|
27
|
+
* unresolvable passthrough call sites. Cycles short-circuit to an empty
|
|
28
|
+
* result so resolution always terminates. */
|
|
29
|
+
export declare function resolveThrowsUnion(manifest: ResourceManifest, ctx: ResolveCtx): ThrowsUnion;
|
|
30
|
+
//# sourceMappingURL=resolve-throws-union.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-throws-union.d.ts","sourceRoot":"","sources":["../src/resolve-throws-union.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,gFAAgF;IAChF,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACnC;;;8EAG0E;IAC1E,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC/B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACzB;AAED,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,gBAAgB,EAAE,EAChC,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE,aAAa,GACrB,UAAU,CAQZ;AA+BD;;;;8CAI8C;AAC9C,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,gBAAgB,EAC1B,GAAG,EAAE,UAAU,GACd,WAAW,CA+Cb"}
|