@telorun/analyzer 0.1.4 → 0.2.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/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 +52 -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 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- 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 +1 -0
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +36 -14
- 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 +3 -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 +2 -2
- package/src/analyzer.ts +60 -26
- package/src/builtins.ts +52 -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 -0
- package/src/kernel-globals.ts +19 -10
- package/src/manifest-loader.ts +40 -14
- 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 +3 -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 -17
- package/dist/adapters/node-adapter.d.ts.map +0 -1
- package/dist/adapters/node-adapter.js +0 -71
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) {
|
|
@@ -3,6 +3,7 @@ import { type LoadOptions, type LoaderInitOptions, type ManifestAdapter } from "
|
|
|
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;
|
|
@@ -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,12 +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";
|
|
6
8
|
import { DEFAULT_MANIFEST_FILENAME, } from "./types.js";
|
|
7
|
-
const SYSTEM_KINDS = new Set([
|
|
9
|
+
const SYSTEM_KINDS = new Set([
|
|
10
|
+
"Telo.Application",
|
|
11
|
+
"Telo.Library",
|
|
12
|
+
"Telo.Import",
|
|
13
|
+
"Telo.Definition",
|
|
14
|
+
]);
|
|
8
15
|
export class Loader {
|
|
9
16
|
static moduleCache = new Map();
|
|
10
17
|
adapters;
|
|
18
|
+
celEnv;
|
|
11
19
|
constructor(extraAdaptersOrOptions = []) {
|
|
12
20
|
const options = Array.isArray(extraAdaptersOrOptions)
|
|
13
21
|
? { extraAdapters: extraAdaptersOrOptions }
|
|
@@ -22,6 +30,7 @@ export class Loader {
|
|
|
22
30
|
if (options.extraAdapters?.length) {
|
|
23
31
|
this.adapters.unshift(...options.extraAdapters);
|
|
24
32
|
}
|
|
33
|
+
this.celEnv = buildCelEnvironment(options.celHandlers);
|
|
25
34
|
}
|
|
26
35
|
register(adapter) {
|
|
27
36
|
this.adapters.unshift(adapter);
|
|
@@ -59,7 +68,7 @@ export class Loader {
|
|
|
59
68
|
let compiledDocs;
|
|
60
69
|
if (options?.compile) {
|
|
61
70
|
try {
|
|
62
|
-
const result = precompileDoc(rawDoc);
|
|
71
|
+
const result = precompileDoc(rawDoc, this.celEnv);
|
|
63
72
|
compiledDocs = Array.isArray(result) ? result : [result];
|
|
64
73
|
}
|
|
65
74
|
catch (error) {
|
|
@@ -85,15 +94,17 @@ export class Loader {
|
|
|
85
94
|
resolved.push({ ...manifest, metadata });
|
|
86
95
|
}
|
|
87
96
|
}
|
|
88
|
-
const moduleManifests = resolved.filter((m) => m.kind
|
|
97
|
+
const moduleManifests = resolved.filter((m) => isModuleKind(m.kind));
|
|
89
98
|
if (moduleManifests.length > 1) {
|
|
90
|
-
|
|
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.`);
|
|
91
102
|
}
|
|
92
103
|
const moduleManifest = moduleManifests[0];
|
|
93
104
|
const moduleName = moduleManifest?.metadata?.name;
|
|
94
105
|
if (moduleName) {
|
|
95
106
|
for (const manifest of resolved) {
|
|
96
|
-
if (manifest.kind
|
|
107
|
+
if (!isModuleKind(manifest.kind) && !manifest.metadata?.module) {
|
|
97
108
|
const pi = manifest.metadata?.positionIndex;
|
|
98
109
|
manifest.metadata = { ...manifest.metadata, module: moduleName };
|
|
99
110
|
if (pi) {
|
|
@@ -163,7 +174,7 @@ export class Loader {
|
|
|
163
174
|
let compiledDocs;
|
|
164
175
|
if (options?.compile) {
|
|
165
176
|
try {
|
|
166
|
-
const result = precompileDoc(rawDoc);
|
|
177
|
+
const result = precompileDoc(rawDoc, this.celEnv);
|
|
167
178
|
compiledDocs = Array.isArray(result) ? result : [result];
|
|
168
179
|
}
|
|
169
180
|
catch (error) {
|
|
@@ -197,10 +208,10 @@ export class Loader {
|
|
|
197
208
|
async loadModuleForFile(fileUrl) {
|
|
198
209
|
// Try loading as a regular module first (it might be a telo.yaml itself).
|
|
199
210
|
// Use loadManifests (not loadModule) so imported definitions are included —
|
|
200
|
-
// otherwise the analyzer won't know about kinds from
|
|
211
|
+
// otherwise the analyzer won't know about kinds from Telo.Import sources.
|
|
201
212
|
try {
|
|
202
213
|
const docs = await this.loadModule(fileUrl);
|
|
203
|
-
const hasModule = docs.some((d) => d.kind
|
|
214
|
+
const hasModule = docs.some((d) => isModuleKind(d.kind));
|
|
204
215
|
if (hasModule) {
|
|
205
216
|
const { source } = await this.pick(fileUrl).read(fileUrl);
|
|
206
217
|
const manifests = await this.loadManifests(fileUrl);
|
|
@@ -235,7 +246,7 @@ export class Loader {
|
|
|
235
246
|
const queue = [...entry];
|
|
236
247
|
while (queue.length > 0) {
|
|
237
248
|
const m = queue.shift();
|
|
238
|
-
if (m.kind !== "
|
|
249
|
+
if (m.kind !== "Telo.Import")
|
|
239
250
|
continue;
|
|
240
251
|
const importSource = m.source;
|
|
241
252
|
if (!importSource)
|
|
@@ -258,7 +269,7 @@ export class Loader {
|
|
|
258
269
|
}
|
|
259
270
|
result.set(importUrl, imported);
|
|
260
271
|
for (const im of imported) {
|
|
261
|
-
if (im.kind === "
|
|
272
|
+
if (im.kind === "Telo.Import")
|
|
262
273
|
queue.push(im);
|
|
263
274
|
}
|
|
264
275
|
}
|
|
@@ -271,7 +282,7 @@ export class Loader {
|
|
|
271
282
|
const queue = [...entry];
|
|
272
283
|
while (queue.length > 0) {
|
|
273
284
|
const m = queue.shift();
|
|
274
|
-
if (m.kind !== "
|
|
285
|
+
if (m.kind !== "Telo.Import")
|
|
275
286
|
continue;
|
|
276
287
|
const importSource = m.source;
|
|
277
288
|
if (!importSource)
|
|
@@ -292,7 +303,18 @@ export class Loader {
|
|
|
292
303
|
e.sourceLine = m.metadata?.sourceLine ?? 0;
|
|
293
304
|
throw e;
|
|
294
305
|
}
|
|
295
|
-
|
|
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;
|
|
296
318
|
if (importedModule?.metadata?.name) {
|
|
297
319
|
const pi = m.metadata?.positionIndex;
|
|
298
320
|
m.metadata = {
|
|
@@ -310,9 +332,9 @@ export class Loader {
|
|
|
310
332
|
}
|
|
311
333
|
}
|
|
312
334
|
for (const im of imported) {
|
|
313
|
-
if (im.kind === "
|
|
335
|
+
if (im.kind === "Telo.Definition")
|
|
314
336
|
importedDefs.push(im);
|
|
315
|
-
if (im.kind === "
|
|
337
|
+
if (im.kind === "Telo.Import")
|
|
316
338
|
queue.push(im);
|
|
317
339
|
}
|
|
318
340
|
}
|
|
@@ -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"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
export function createResolveCtx(allManifests, defs, aliases) {
|
|
2
|
+
return {
|
|
3
|
+
allManifests,
|
|
4
|
+
defs,
|
|
5
|
+
aliases,
|
|
6
|
+
memo: new Map(),
|
|
7
|
+
inProgress: new Set(),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function emptyUnion() {
|
|
11
|
+
return { codes: new Map(), unbounded: false };
|
|
12
|
+
}
|
|
13
|
+
function unionInto(target, src) {
|
|
14
|
+
for (const [code, meta] of src.codes) {
|
|
15
|
+
if (!target.codes.has(code))
|
|
16
|
+
target.codes.set(code, meta);
|
|
17
|
+
}
|
|
18
|
+
if (src.unbounded)
|
|
19
|
+
target.unbounded = true;
|
|
20
|
+
}
|
|
21
|
+
function definitionFor(kind, defs, aliases) {
|
|
22
|
+
const resolved = aliases.resolveKind(kind);
|
|
23
|
+
return defs.resolve(kind) ?? (resolved ? defs.resolve(resolved) : undefined);
|
|
24
|
+
}
|
|
25
|
+
function codesFromDefinition(definition) {
|
|
26
|
+
const out = new Map();
|
|
27
|
+
const raw = definition.throws?.codes ?? {};
|
|
28
|
+
for (const [code, meta] of Object.entries(raw)) {
|
|
29
|
+
out.set(code, { data: meta.data });
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
/** Resolve the effective throw union for a named manifest. The result combines
|
|
34
|
+
* explicit `throws.codes`, `throws.inherit: true` dataflow (step-context
|
|
35
|
+
* traversal with try/catch subtraction), and unbounded markers for
|
|
36
|
+
* unresolvable passthrough call sites. Cycles short-circuit to an empty
|
|
37
|
+
* result so resolution always terminates. */
|
|
38
|
+
export function resolveThrowsUnion(manifest, ctx) {
|
|
39
|
+
const name = manifest.metadata?.name;
|
|
40
|
+
if (name) {
|
|
41
|
+
const cached = ctx.memo.get(name);
|
|
42
|
+
if (cached)
|
|
43
|
+
return cached;
|
|
44
|
+
if (ctx.inProgress.has(name))
|
|
45
|
+
return emptyUnion();
|
|
46
|
+
}
|
|
47
|
+
const definition = definitionFor(manifest.kind, ctx.defs, ctx.aliases);
|
|
48
|
+
if (!definition) {
|
|
49
|
+
const u = { codes: new Map(), unbounded: true };
|
|
50
|
+
if (name)
|
|
51
|
+
ctx.memo.set(name, u);
|
|
52
|
+
return u;
|
|
53
|
+
}
|
|
54
|
+
const throws = definition.throws;
|
|
55
|
+
if (!throws) {
|
|
56
|
+
const u = emptyUnion();
|
|
57
|
+
if (name)
|
|
58
|
+
ctx.memo.set(name, u);
|
|
59
|
+
return u;
|
|
60
|
+
}
|
|
61
|
+
if (name)
|
|
62
|
+
ctx.inProgress.add(name);
|
|
63
|
+
try {
|
|
64
|
+
const result = { codes: new Map(), unbounded: false };
|
|
65
|
+
for (const [code, meta] of codesFromDefinition(definition)) {
|
|
66
|
+
result.codes.set(code, meta);
|
|
67
|
+
}
|
|
68
|
+
if (throws.passthrough) {
|
|
69
|
+
// Definition-level passthrough can't be resolved without a call site.
|
|
70
|
+
// resolveStepInvokeThrows handles passthrough call sites directly.
|
|
71
|
+
result.unbounded = true;
|
|
72
|
+
}
|
|
73
|
+
if (throws.inherit) {
|
|
74
|
+
const inherited = resolveInherited(manifest, definition, ctx);
|
|
75
|
+
unionInto(result, inherited);
|
|
76
|
+
}
|
|
77
|
+
if (name)
|
|
78
|
+
ctx.memo.set(name, result);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
if (name)
|
|
83
|
+
ctx.inProgress.delete(name);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function resolveInherited(manifest, definition, ctx) {
|
|
87
|
+
const result = { codes: new Map(), unbounded: false };
|
|
88
|
+
const props = definition.schema?.properties;
|
|
89
|
+
if (!props)
|
|
90
|
+
return result;
|
|
91
|
+
for (const [fieldName, fieldSchema] of Object.entries(props)) {
|
|
92
|
+
const stepCtx = fieldSchema["x-telo-step-context"];
|
|
93
|
+
if (!stepCtx?.invoke)
|
|
94
|
+
continue;
|
|
95
|
+
const steps = manifest[fieldName];
|
|
96
|
+
if (!Array.isArray(steps))
|
|
97
|
+
continue;
|
|
98
|
+
unionInto(result, collectStepArrayThrows(steps, stepCtx.invoke, undefined, ctx));
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
function collectStepArrayThrows(steps, invokeField, enclosingTryCodes, ctx) {
|
|
103
|
+
const result = emptyUnion();
|
|
104
|
+
for (const step of steps) {
|
|
105
|
+
if (!step || typeof step !== "object")
|
|
106
|
+
continue;
|
|
107
|
+
unionInto(result, collectStepThrows(step, invokeField, enclosingTryCodes, ctx));
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
/** Walk one step, dispatching by shape. Generic for any Run.Sequence-style
|
|
112
|
+
* composer: the step keys it recognises (`try` / `catch` / `finally` / `then`
|
|
113
|
+
* / `else` / `elseif` / `do` / `cases` / `default`) are the same set already
|
|
114
|
+
* traversed by the analyzer's `x-telo-step-context` schema builder, so future
|
|
115
|
+
* composers that reuse those shape conventions work without changes here. */
|
|
116
|
+
function collectStepThrows(step, invokeField, enclosingTryCodes, ctx) {
|
|
117
|
+
if (step[invokeField]) {
|
|
118
|
+
return resolveStepInvokeThrows(step, invokeField, enclosingTryCodes, ctx);
|
|
119
|
+
}
|
|
120
|
+
if (step.throw && typeof step.throw === "object") {
|
|
121
|
+
return resolveThrowStepCode(step.throw, enclosingTryCodes);
|
|
122
|
+
}
|
|
123
|
+
if (Array.isArray(step.try)) {
|
|
124
|
+
const tryUnion = collectStepArrayThrows(step.try, invokeField, enclosingTryCodes, ctx);
|
|
125
|
+
let propagated;
|
|
126
|
+
if (Array.isArray(step.catch)) {
|
|
127
|
+
// Catch absorbs the try block's codes; the catch's own throws propagate
|
|
128
|
+
// out instead. Sequence-specific subtraction — the plan explicitly
|
|
129
|
+
// anchors this to Run.Sequence's try/catch schema shape.
|
|
130
|
+
const tryCodes = new Set(tryUnion.codes.keys());
|
|
131
|
+
propagated = collectStepArrayThrows(step.catch, invokeField, tryCodes, ctx);
|
|
132
|
+
// Unbounded in the try block still signals the caller to expect
|
|
133
|
+
// arbitrary codes to flow through the catch (e.g. via passthrough).
|
|
134
|
+
if (tryUnion.unbounded)
|
|
135
|
+
propagated.unbounded = true;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
propagated = cloneUnion(tryUnion);
|
|
139
|
+
}
|
|
140
|
+
if (Array.isArray(step.finally)) {
|
|
141
|
+
unionInto(propagated, collectStepArrayThrows(step.finally, invokeField, enclosingTryCodes, ctx));
|
|
142
|
+
}
|
|
143
|
+
return propagated;
|
|
144
|
+
}
|
|
145
|
+
if (Array.isArray(step.then)) {
|
|
146
|
+
const result = emptyUnion();
|
|
147
|
+
unionInto(result, collectStepArrayThrows(step.then, invokeField, enclosingTryCodes, ctx));
|
|
148
|
+
if (Array.isArray(step.else)) {
|
|
149
|
+
unionInto(result, collectStepArrayThrows(step.else, invokeField, enclosingTryCodes, ctx));
|
|
150
|
+
}
|
|
151
|
+
if (Array.isArray(step.elseif)) {
|
|
152
|
+
for (const branch of step.elseif) {
|
|
153
|
+
if (Array.isArray(branch?.then)) {
|
|
154
|
+
unionInto(result, collectStepArrayThrows(branch.then, invokeField, enclosingTryCodes, ctx));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
if (Array.isArray(step.do)) {
|
|
161
|
+
return collectStepArrayThrows(step.do, invokeField, enclosingTryCodes, ctx);
|
|
162
|
+
}
|
|
163
|
+
if (step.cases && typeof step.cases === "object") {
|
|
164
|
+
const result = emptyUnion();
|
|
165
|
+
for (const arr of Object.values(step.cases)) {
|
|
166
|
+
if (Array.isArray(arr)) {
|
|
167
|
+
unionInto(result, collectStepArrayThrows(arr, invokeField, enclosingTryCodes, ctx));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (Array.isArray(step.default)) {
|
|
171
|
+
unionInto(result, collectStepArrayThrows(step.default, invokeField, enclosingTryCodes, ctx));
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
return emptyUnion();
|
|
176
|
+
}
|
|
177
|
+
function cloneUnion(u) {
|
|
178
|
+
const out = emptyUnion();
|
|
179
|
+
for (const [c, m] of u.codes)
|
|
180
|
+
out.codes.set(c, m);
|
|
181
|
+
out.unbounded = u.unbounded;
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
function resolveStepInvokeThrows(step, invokeField, enclosingTryCodes, ctx) {
|
|
185
|
+
const invokeRef = step[invokeField];
|
|
186
|
+
if (!invokeRef || typeof invokeRef !== "object")
|
|
187
|
+
return emptyUnion();
|
|
188
|
+
const invokedKind = invokeRef.kind;
|
|
189
|
+
if (!invokedKind)
|
|
190
|
+
return emptyUnion();
|
|
191
|
+
const definition = definitionFor(invokedKind, ctx.defs, ctx.aliases);
|
|
192
|
+
if (!definition)
|
|
193
|
+
return { codes: new Map(), unbounded: true };
|
|
194
|
+
if (definition.throws?.passthrough) {
|
|
195
|
+
return resolvePassthroughAtCallSite(step, enclosingTryCodes);
|
|
196
|
+
}
|
|
197
|
+
// Named manifest: resolve the full chain (covers transitive inherit).
|
|
198
|
+
const invokeName = invokeRef.name;
|
|
199
|
+
if (invokeName) {
|
|
200
|
+
const target = ctx.allManifests.find((m) => m.metadata?.name === invokeName &&
|
|
201
|
+
(m.kind === invokedKind ||
|
|
202
|
+
ctx.aliases.resolveKind(m.kind) === invokedKind ||
|
|
203
|
+
m.kind === ctx.aliases.resolveKind(invokedKind)));
|
|
204
|
+
if (target)
|
|
205
|
+
return resolveThrowsUnion(target, ctx);
|
|
206
|
+
}
|
|
207
|
+
// Fall back to the definition's own explicit codes. Mark unbounded when the
|
|
208
|
+
// definition depends on call-site or transitive resolution we couldn't
|
|
209
|
+
// perform (no specific target manifest to recurse into).
|
|
210
|
+
const codes = codesFromDefinition(definition);
|
|
211
|
+
const unbounded = definition.throws?.inherit === true || definition.throws?.passthrough === true;
|
|
212
|
+
return { codes, unbounded };
|
|
213
|
+
}
|
|
214
|
+
/** Resolve a passthrough-style invocable at a specific call site. Recognised forms
|
|
215
|
+
* (see "passthrough: true" in the plan):
|
|
216
|
+
* - constant literal (no template) → `{ <literal> }`
|
|
217
|
+
* - `${{ 'FOO' }}` constant expression → `{ FOO }`
|
|
218
|
+
* - `${{ error.code }}` inside a catch → enclosing try's propagated union
|
|
219
|
+
* Anything else is unbounded; the analyzer flags it downstream. */
|
|
220
|
+
function resolvePassthroughAtCallSite(step, enclosingTryCodes) {
|
|
221
|
+
return resolveCodeExpression(step.inputs?.code, enclosingTryCodes);
|
|
222
|
+
}
|
|
223
|
+
/** Resolve the `code:` of a `throw:` step to a throws union. Uses the same
|
|
224
|
+
* recognised forms as passthrough call sites. */
|
|
225
|
+
function resolveThrowStepCode(throwSpec, enclosingTryCodes) {
|
|
226
|
+
return resolveCodeExpression(throwSpec.code, enclosingTryCodes);
|
|
227
|
+
}
|
|
228
|
+
function resolveCodeExpression(codeInput, enclosingTryCodes) {
|
|
229
|
+
if (typeof codeInput !== "string" || codeInput.length === 0) {
|
|
230
|
+
return { codes: new Map(), unbounded: true };
|
|
231
|
+
}
|
|
232
|
+
const match = codeInput.match(/^\s*\$\{\{\s*([\s\S]+?)\s*\}\}\s*$/);
|
|
233
|
+
if (!match) {
|
|
234
|
+
return { codes: new Map([[codeInput, {}]]), unbounded: false };
|
|
235
|
+
}
|
|
236
|
+
const expr = match[1].trim();
|
|
237
|
+
const litMatch = expr.match(/^'([^']+)'$|^"([^"]+)"$/);
|
|
238
|
+
if (litMatch) {
|
|
239
|
+
const code = litMatch[1] ?? litMatch[2];
|
|
240
|
+
return { codes: new Map([[code, {}]]), unbounded: false };
|
|
241
|
+
}
|
|
242
|
+
if (expr === "error.code") {
|
|
243
|
+
if (enclosingTryCodes) {
|
|
244
|
+
const codes = new Map();
|
|
245
|
+
for (const c of enclosingTryCodes)
|
|
246
|
+
codes.set(c, {});
|
|
247
|
+
return { codes, unbounded: false };
|
|
248
|
+
}
|
|
249
|
+
return { codes: new Map(), unbounded: true };
|
|
250
|
+
}
|
|
251
|
+
return { codes: new Map(), unbounded: true };
|
|
252
|
+
}
|