@telorun/analyzer 0.28.1 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +72 -1
- package/dist/cel-environment.d.ts +2 -2
- package/dist/cel-environment.d.ts.map +1 -1
- package/dist/cel-environment.js +5 -6
- package/dist/eval-paths.d.ts +25 -0
- package/dist/eval-paths.d.ts.map +1 -0
- package/dist/eval-paths.js +55 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/kernel-globals.d.ts +6 -7
- package/dist/kernel-globals.d.ts.map +1 -1
- package/dist/kernel-globals.js +6 -8
- package/dist/reconcile-module-versions.d.ts.map +1 -1
- package/dist/reconcile-module-versions.js +4 -9
- package/dist/validate-cel-context.d.ts +9 -0
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +37 -0
- package/package.json +1 -1
- package/src/analyzer.ts +78 -0
- package/src/cel-environment.ts +5 -6
- package/src/eval-paths.ts +68 -0
- package/src/index.ts +1 -0
- package/src/kernel-globals.ts +6 -8
- package/src/reconcile-module-versions.ts +4 -10
- package/src/validate-cel-context.ts +39 -0
package/dist/analyzer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAmB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAmB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAwjB/F,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,OAAO,GAAE,qBAA0B;IAI/C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAkyBvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAMvB,SAAS,CACP,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,EAI1B,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,GACtC,gBAAgB,EAAE;IAmBrB,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,GACzB;QAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAsB5F"}
|
package/dist/analyzer.js
CHANGED
|
@@ -16,7 +16,8 @@ import { validateSchemaTypeRefs } from "./validate-schema-type-refs.js";
|
|
|
16
16
|
import { rewriteSyntheticOrigins } from "./rewrite-synthetic-origins.js";
|
|
17
17
|
import { celTypeSatisfiesJsonSchema, substituteCelFields, validateAgainstSchema, } from "./schema-compat.js";
|
|
18
18
|
import { DiagnosticSeverity } from "./types.js";
|
|
19
|
-
import { extractContextsFromSchema, getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
|
|
19
|
+
import { extractCelRegionScopes, extractContextsFromSchema, getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
|
|
20
|
+
import { buildEvalPaths, evalPathsCover } from "./eval-paths.js";
|
|
20
21
|
import { validateExtends } from "./validate-extends.js";
|
|
21
22
|
import { validateNestedInlineResources } from "./validate-nested-inline.js";
|
|
22
23
|
import { validateProviderCoherence } from "./validate-provider-coherence.js";
|
|
@@ -320,6 +321,29 @@ function buildStepContextSchema(manifest, defSchema, allManifests, defs, aliases
|
|
|
320
321
|
* specific field name (or `Run.Sequence`) is hardcoded; any composer that tags
|
|
321
322
|
* its error-bearing branch fields opts in the same way.
|
|
322
323
|
*/
|
|
324
|
+
/**
|
|
325
|
+
* True when a `walkCelExpressions` path (`with[0].handler.inputs.x`) crosses an
|
|
326
|
+
* inline nested resource — an `{ kind: … }` object below the host root — before
|
|
327
|
+
* reaching the leaf. Such CEL belongs to the nested resource's kind (validated
|
|
328
|
+
* when that resource is analyzed), not the host's schema, so the
|
|
329
|
+
* non-eval-field check must not attribute it to the host.
|
|
330
|
+
*/
|
|
331
|
+
function pathCrossesNestedResource(root, path) {
|
|
332
|
+
const segments = path.match(/[^.[\]]+/g) ?? [];
|
|
333
|
+
let node = root;
|
|
334
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
335
|
+
node = Array.isArray(node)
|
|
336
|
+
? node[Number(segments[i])]
|
|
337
|
+
: node?.[segments[i]];
|
|
338
|
+
if (node !== null &&
|
|
339
|
+
typeof node === "object" &&
|
|
340
|
+
!Array.isArray(node) &&
|
|
341
|
+
typeof node.kind === "string") {
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
323
347
|
function collectErrorContextScopes(defSchema) {
|
|
324
348
|
const out = new Map();
|
|
325
349
|
if (!defSchema || typeof defSchema !== "object")
|
|
@@ -995,6 +1019,13 @@ export class StaticAnalyzer {
|
|
|
995
1019
|
let celStepContextSchema;
|
|
996
1020
|
let celInvocationContext;
|
|
997
1021
|
let celErrorScopes = new Map();
|
|
1022
|
+
// Region coverage for the "CEL in a non-eval field" check: the union of
|
|
1023
|
+
// `x-telo-eval` paths (own + capability) and `x-telo-context` /
|
|
1024
|
+
// `x-telo-step-context` / `x-telo-error-context` scopes. A `!cel` outside
|
|
1025
|
+
// every region is read as a literal — the runtime never evaluates it.
|
|
1026
|
+
let celEvalPaths = [];
|
|
1027
|
+
let celRegionScopes = [];
|
|
1028
|
+
let celRuleApplies = false;
|
|
998
1029
|
visitManifest(allManifests, defs, {
|
|
999
1030
|
onResourceEnter: (e) => {
|
|
1000
1031
|
const m = e.source;
|
|
@@ -1003,12 +1034,52 @@ export class StaticAnalyzer {
|
|
|
1003
1034
|
? buildStepContextSchema(m, e.definition.schema, allManifests, defs, aliases)
|
|
1004
1035
|
: undefined;
|
|
1005
1036
|
celErrorScopes = collectErrorContextScopes(e.definition?.schema);
|
|
1037
|
+
// The non-eval-field check only applies to runtime resource instances:
|
|
1038
|
+
// structural / templating kinds (capability `Telo.Template`, or no
|
|
1039
|
+
// definition) carry CEL the kernel evaluates by other rules.
|
|
1040
|
+
const capability = e.definition?.capability;
|
|
1041
|
+
celRuleApplies =
|
|
1042
|
+
!!e.definition?.schema && capability !== undefined && capability !== "Telo.Template";
|
|
1043
|
+
if (celRuleApplies) {
|
|
1044
|
+
const ownSchema = e.definition.schema;
|
|
1045
|
+
const own = buildEvalPaths(ownSchema);
|
|
1046
|
+
const capabilityDef = capability ? defs.resolve(capability) : undefined;
|
|
1047
|
+
const parent = capabilityDef?.schema
|
|
1048
|
+
? buildEvalPaths(capabilityDef.schema)
|
|
1049
|
+
: { compile: [], runtime: [] };
|
|
1050
|
+
celEvalPaths = [...own.compile, ...own.runtime, ...parent.compile, ...parent.runtime];
|
|
1051
|
+
celRegionScopes = extractCelRegionScopes(ownSchema);
|
|
1052
|
+
}
|
|
1053
|
+
else {
|
|
1054
|
+
celEvalPaths = [];
|
|
1055
|
+
celRegionScopes = [];
|
|
1056
|
+
}
|
|
1006
1057
|
},
|
|
1007
1058
|
onCel: (e) => {
|
|
1008
1059
|
const m = e.source;
|
|
1009
1060
|
const resource = { kind: m.kind, name: m.metadata?.name };
|
|
1010
1061
|
const filePath = m.metadata?.source;
|
|
1011
1062
|
const { expr, path, engineName, matchedScope } = e;
|
|
1063
|
+
// A `!cel` (or `${{ }}`) in a field with no `x-telo-eval` / `x-telo-context`
|
|
1064
|
+
// is never evaluated — the runtime reads it as a literal (e.g. a
|
|
1065
|
+
// `concurrency` `!cel` that silently degraded to a sparse `[null, …]`).
|
|
1066
|
+
// Flag it rather than letting it pass as valid CEL. Inline resources
|
|
1067
|
+
// (resource-wide invocation context) carry CEL the kernel evaluates.
|
|
1068
|
+
if (celRuleApplies &&
|
|
1069
|
+
engineName === "cel" &&
|
|
1070
|
+
celInvocationContext === undefined &&
|
|
1071
|
+
!evalPathsCover(celEvalPaths, path) &&
|
|
1072
|
+
!celRegionScopes.some((scope) => pathMatchesScope(path, scope)) &&
|
|
1073
|
+
!pathCrossesNestedResource(m, path)) {
|
|
1074
|
+
diagnostics.push({
|
|
1075
|
+
severity: DiagnosticSeverity.Error,
|
|
1076
|
+
code: "CEL_IN_NON_EVAL_FIELD",
|
|
1077
|
+
source: SOURCE,
|
|
1078
|
+
message: `${m.kind}/${resource.name}: CEL at '${path}' is never evaluated — the field has no x-telo-eval / x-telo-context annotation, so its value is read as a literal. Annotate the field as a CEL slot or remove the !cel tag.`,
|
|
1079
|
+
data: { resource, filePath, path },
|
|
1080
|
+
});
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1012
1083
|
let matchedContext = e.contextSchema ?? celInvocationContext;
|
|
1013
1084
|
if (celStepContextSchema) {
|
|
1014
1085
|
const base = matchedContext ?? { type: "object", properties: {}, additionalProperties: true };
|
|
@@ -7,7 +7,7 @@ export type { CelHandlers } from "@telorun/templating";
|
|
|
7
7
|
*
|
|
8
8
|
* - `variables`: typed from the manifest's `variables` field if it is a schema map
|
|
9
9
|
* (only module-identity docs — `Telo.Application` / `Telo.Library` — carry this); otherwise registered as `map` (dyn).
|
|
10
|
-
* - `secrets`, `resources
|
|
10
|
+
* - `secrets`, `resources`: always `map` (dyn — output schemas unknown).
|
|
11
11
|
* - `extraContextSchema`: additional variables from an `x-telo-context` annotation.
|
|
12
12
|
*
|
|
13
13
|
* NOTE: The set of kernel globals registered here must match `KERNEL_GLOBAL_NAMES`
|
|
@@ -17,7 +17,7 @@ export declare function buildTypedCelEnvironment(baseEnv: Environment, manifest:
|
|
|
17
17
|
*
|
|
18
18
|
* Import inputs are a config-only contract: their expressions are evaluated
|
|
19
19
|
* against the IMPORTING module's `variables`/`secrets`, never the import's own
|
|
20
|
-
* values map (the bug) nor the imported child's. `resources
|
|
20
|
+
* values map (the bug) nor the imported child's. `resources` and `ports`
|
|
21
21
|
* are registered as empty typed objects, so referencing them is a "No such key"
|
|
22
22
|
* error that steers authors to a typed `variables` entry. */
|
|
23
23
|
export declare function buildImportInputCelEnvironment(baseEnv: Environment, moduleManifest: ResourceManifest | undefined): Environment;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cel-environment.d.ts","sourceRoot":"","sources":["../src/cel-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAUrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;uEASuE;AACvE,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAI/C,kBAAkB,CAAC,EAAE,gBAAgB,GACpC,WAAW,
|
|
1
|
+
{"version":3,"file":"cel-environment.d.ts","sourceRoot":"","sources":["../src/cel-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAUrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;uEASuE;AACvE,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAI/C,kBAAkB,CAAC,EAAE,gBAAgB,GACpC,WAAW,CAsEb;AAuBD;;;;;;8DAM8D;AAC9D,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,WAAW,EACpB,cAAc,EAAE,gBAAgB,GAAG,SAAS,GAC3C,WAAW,CAwBb"}
|
package/dist/cel-environment.js
CHANGED
|
@@ -11,7 +11,7 @@ export { buildCelEnvironment } from "@telorun/templating";
|
|
|
11
11
|
*
|
|
12
12
|
* - `variables`: typed from the manifest's `variables` field if it is a schema map
|
|
13
13
|
* (only module-identity docs — `Telo.Application` / `Telo.Library` — carry this); otherwise registered as `map` (dyn).
|
|
14
|
-
* - `secrets`, `resources
|
|
14
|
+
* - `secrets`, `resources`: always `map` (dyn — output schemas unknown).
|
|
15
15
|
* - `extraContextSchema`: additional variables from an `x-telo-context` annotation.
|
|
16
16
|
*
|
|
17
17
|
* NOTE: The set of kernel globals registered here must match `KERNEL_GLOBAL_NAMES`
|
|
@@ -71,7 +71,6 @@ rootModuleManifest) {
|
|
|
71
71
|
}
|
|
72
72
|
env.registerVariable("secrets", "map");
|
|
73
73
|
env.registerVariable("resources", "map");
|
|
74
|
-
env.registerVariable("env", "map");
|
|
75
74
|
if (extraContextSchema?.properties) {
|
|
76
75
|
for (const [name, propSchema] of Object.entries(extraContextSchema.properties)) {
|
|
77
76
|
if (!env.hasVariable(name)) {
|
|
@@ -104,7 +103,7 @@ function registerConfigNamespace(env, block, name) {
|
|
|
104
103
|
*
|
|
105
104
|
* Import inputs are a config-only contract: their expressions are evaluated
|
|
106
105
|
* against the IMPORTING module's `variables`/`secrets`, never the import's own
|
|
107
|
-
* values map (the bug) nor the imported child's. `resources
|
|
106
|
+
* values map (the bug) nor the imported child's. `resources` and `ports`
|
|
108
107
|
* are registered as empty typed objects, so referencing them is a "No such key"
|
|
109
108
|
* error that steers authors to a typed `variables` entry. */
|
|
110
109
|
export function buildImportInputCelEnvironment(baseEnv, moduleManifest) {
|
|
@@ -125,10 +124,10 @@ export function buildImportInputCelEnvironment(baseEnv, moduleManifest) {
|
|
|
125
124
|
env.registerVariable("variables", "map");
|
|
126
125
|
env.registerVariable("secrets", "map");
|
|
127
126
|
}
|
|
128
|
-
// Override the base env's dyn `resources`/`
|
|
129
|
-
// so any access (`resources.X`, `
|
|
127
|
+
// Override the base env's dyn `resources`/`ports` with empty typed objects
|
|
128
|
+
// so any access (`resources.X`, `ports.X`) is a "No such key" error — these
|
|
130
129
|
// surfaces are not part of the config-only import contract.
|
|
131
|
-
for (const name of ["resources", "
|
|
130
|
+
for (const name of ["resources", "ports"]) {
|
|
132
131
|
env.registerVariable({ name, schema: {} });
|
|
133
132
|
}
|
|
134
133
|
return env;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single containment rule for `x-telo-eval` paths, shared by every matcher so
|
|
3
|
+
* the analyzer's coverage decision and the kernel's expansion/exclusion can't
|
|
4
|
+
* drift. True when `target` lies in the subtree rooted at `evalPath`: `"**"`
|
|
5
|
+
* covers everything; a dotted path covers itself and any descendant — `"handler"`
|
|
6
|
+
* covers `handler`, `handler.body`, `handler[0]`. Targets use `walkCelExpressions`
|
|
7
|
+
* form (`a.b[0].c`); eval paths are property-only (no array segments —
|
|
8
|
+
* `buildEvalPaths` does not descend into `items`), so `.`/`[` boundary prefixing
|
|
9
|
+
* is exact. Consumers: the analyzer's `evalPathsCover`, the kernel's `isExcluded`
|
|
10
|
+
* (applied in both directions), and — structurally — `expandPaths`' navigation.
|
|
11
|
+
*/
|
|
12
|
+
export declare function evalPathCovers(evalPath: string, target: string): boolean;
|
|
13
|
+
/** True when any `x-telo-eval` path in the set covers `exprPath` (see
|
|
14
|
+
* {@link evalPathCovers}). */
|
|
15
|
+
export declare function evalPathsCover(evalPaths: readonly string[], exprPath: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Traverses a definition schema and collects all paths annotated with `x-telo-eval`.
|
|
18
|
+
* Root-level `x-telo-eval` produces the `"**"` wildcard (expand all fields).
|
|
19
|
+
* Property-level annotations produce the dot-notation path to that property.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildEvalPaths(schema: Record<string, any>): {
|
|
22
|
+
compile: string[];
|
|
23
|
+
runtime: string[];
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=eval-paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eval-paths.d.ts","sourceRoot":"","sources":["../src/eval-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAKxE;AAED;+BAC+B;AAC/B,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEtF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG;IAC3D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAcA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single containment rule for `x-telo-eval` paths, shared by every matcher so
|
|
3
|
+
* the analyzer's coverage decision and the kernel's expansion/exclusion can't
|
|
4
|
+
* drift. True when `target` lies in the subtree rooted at `evalPath`: `"**"`
|
|
5
|
+
* covers everything; a dotted path covers itself and any descendant — `"handler"`
|
|
6
|
+
* covers `handler`, `handler.body`, `handler[0]`. Targets use `walkCelExpressions`
|
|
7
|
+
* form (`a.b[0].c`); eval paths are property-only (no array segments —
|
|
8
|
+
* `buildEvalPaths` does not descend into `items`), so `.`/`[` boundary prefixing
|
|
9
|
+
* is exact. Consumers: the analyzer's `evalPathsCover`, the kernel's `isExcluded`
|
|
10
|
+
* (applied in both directions), and — structurally — `expandPaths`' navigation.
|
|
11
|
+
*/
|
|
12
|
+
export function evalPathCovers(evalPath, target) {
|
|
13
|
+
if (evalPath === "**")
|
|
14
|
+
return true;
|
|
15
|
+
return (target === evalPath || target.startsWith(`${evalPath}.`) || target.startsWith(`${evalPath}[`));
|
|
16
|
+
}
|
|
17
|
+
/** True when any `x-telo-eval` path in the set covers `exprPath` (see
|
|
18
|
+
* {@link evalPathCovers}). */
|
|
19
|
+
export function evalPathsCover(evalPaths, exprPath) {
|
|
20
|
+
return evalPaths.some((p) => evalPathCovers(p, exprPath));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Traverses a definition schema and collects all paths annotated with `x-telo-eval`.
|
|
24
|
+
* Root-level `x-telo-eval` produces the `"**"` wildcard (expand all fields).
|
|
25
|
+
* Property-level annotations produce the dot-notation path to that property.
|
|
26
|
+
*/
|
|
27
|
+
export function buildEvalPaths(schema) {
|
|
28
|
+
const compile = [];
|
|
29
|
+
const runtime = [];
|
|
30
|
+
if (schema["x-telo-eval"] === "compile")
|
|
31
|
+
compile.push("**");
|
|
32
|
+
else if (schema["x-telo-eval"] === "runtime")
|
|
33
|
+
runtime.push("**");
|
|
34
|
+
if (schema.properties) {
|
|
35
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
36
|
+
collectEvalPathsNode(propSchema, key, compile, runtime);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { compile, runtime };
|
|
40
|
+
}
|
|
41
|
+
function collectEvalPathsNode(node, path, compile, runtime) {
|
|
42
|
+
if (node["x-telo-eval"] === "compile") {
|
|
43
|
+
compile.push(path);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (node["x-telo-eval"] === "runtime") {
|
|
47
|
+
runtime.push(path);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (node.properties) {
|
|
51
|
+
for (const [key, propSchema] of Object.entries(node.properties)) {
|
|
52
|
+
collectEvalPathsNode(propSchema, `${path}.${key}`, compile, runtime);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type { RefFieldInfo } from "./analysis-registry.js";
|
|
|
3
3
|
export { StaticAnalyzer } from "./analyzer.js";
|
|
4
4
|
export type { GraphLoadError, ImportEdge, LoadedFile, LoadedGraph, LoadedModule, ParseError, } from "./loaded-types.js";
|
|
5
5
|
export { flattenForAnalyzer, flattenLoadedModule, forwardReExportManifests, parseExportEntry, reExportSpecsFromExports, resolveExportedKinds, selectModuleManifestsForAnalysis, stampReExportedKinds, type ParsedExportEntry, type ReExportSpec, } from "./flatten-for-analyzer.js";
|
|
6
|
+
export { buildEvalPaths, evalPathCovers } from "./eval-paths.js";
|
|
6
7
|
export { visitManifest } from "./manifest-visitor.js";
|
|
7
8
|
export type { CelSiteEvent, ManifestVisitor, RefSiteEvent, ResourceEnterEvent, ResourceExitEvent, ScopeBoundaryEvent, SchemaFromSiteEvent, VisitOptions, } from "./manifest-visitor.js";
|
|
8
9
|
export { Loader } from "./manifest-loader.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EACR,cAAc,EACd,UAAU,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,UAAU,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACH,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,EACxB,oBAAoB,EACpB,gCAAgC,EAChC,oBAAoB,EACpB,KAAK,iBAAiB,EACtB,KAAK,YAAY,GACpB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,YAAY,EACR,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,YAAY,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EACH,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC3E,YAAY,EACR,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,QAAQ,EACR,aAAa,EACb,KAAK,EACR,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EACR,cAAc,EACd,UAAU,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,UAAU,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACH,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,EACxB,oBAAoB,EACpB,gCAAgC,EAChC,oBAAoB,EACpB,KAAK,iBAAiB,EACtB,KAAK,YAAY,GACpB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,YAAY,EACR,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,YAAY,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EACH,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC3E,YAAY,EACR,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,QAAQ,EACR,aAAa,EACb,KAAK,EACR,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { AnalysisRegistry } from "./analysis-registry.js";
|
|
2
2
|
export { StaticAnalyzer } from "./analyzer.js";
|
|
3
3
|
export { flattenForAnalyzer, flattenLoadedModule, forwardReExportManifests, parseExportEntry, reExportSpecsFromExports, resolveExportedKinds, selectModuleManifestsForAnalysis, stampReExportedKinds, } from "./flatten-for-analyzer.js";
|
|
4
|
+
export { buildEvalPaths, evalPathCovers } from "./eval-paths.js";
|
|
4
5
|
export { visitManifest } from "./manifest-visitor.js";
|
|
5
6
|
export { Loader } from "./manifest-loader.js";
|
|
6
7
|
export { isModuleKind, MODULE_KINDS } from "./module-kinds.js";
|
package/dist/kernel-globals.d.ts
CHANGED
|
@@ -5,12 +5,12 @@ import type { ResourceManifest } from "@telorun/sdk";
|
|
|
5
5
|
* `buildTypedCelEnvironment` in cel-environment.ts (CEL type-checking)
|
|
6
6
|
* must stay in sync with this list.
|
|
7
7
|
*
|
|
8
|
-
* Note:
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* Note: there is no `imports` namespace at runtime — import snapshots are
|
|
9
|
+
* stored under `resources.<alias>`. Host environment variables are reached
|
|
10
|
+
* by declaring a typed `variables:`/`secrets:` entry with an `env:` binding
|
|
11
|
+
* and referencing `variables.X` / `secrets.X`.
|
|
12
12
|
*/
|
|
13
|
-
export declare const KERNEL_GLOBAL_NAMES: readonly ["variables", "secrets", "resources", "ports"
|
|
13
|
+
export declare const KERNEL_GLOBAL_NAMES: readonly ["variables", "secrets", "resources", "ports"];
|
|
14
14
|
/**
|
|
15
15
|
* Build a typed JSON Schema describing the kernel globals available in the
|
|
16
16
|
* given manifest set. Used to merge into `x-telo-context` schemas so that
|
|
@@ -23,12 +23,11 @@ export declare const KERNEL_GLOBAL_NAMES: readonly ["variables", "secrets", "res
|
|
|
23
23
|
* in the outer module; Libraries are only relevant when the caller scoped
|
|
24
24
|
* the manifest list to a single library's file.
|
|
25
25
|
* - `resources`: enumerates all non-system resource names
|
|
26
|
-
* - `env`: dynamic (runtime env vars, root module only)
|
|
27
26
|
*/
|
|
28
27
|
export declare function buildKernelGlobalsSchema(manifests: ResourceManifest[]): Record<string, any>;
|
|
29
28
|
/**
|
|
30
29
|
* Merge kernel globals into an `x-telo-context` schema so chain-access
|
|
31
|
-
* validation recognises `variables`, `secrets`, `resources`, `
|
|
30
|
+
* validation recognises `variables`, `secrets`, `resources`, `ports`
|
|
32
31
|
* without module authors having to re-declare them.
|
|
33
32
|
*
|
|
34
33
|
* Context-specific properties take precedence over globals (spread order).
|
|
@@ -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;AAGrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,
|
|
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;AAGrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,yDAA0D,CAAC;AAS3F;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,gBAAgB,EAAE,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAiCrB;AA4CD;;;;;;;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,12 +5,12 @@ import { residualEntrySchemaMap } from "./residual-schema.js";
|
|
|
5
5
|
* `buildTypedCelEnvironment` in cel-environment.ts (CEL type-checking)
|
|
6
6
|
* must stay in sync with this list.
|
|
7
7
|
*
|
|
8
|
-
* Note:
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* Note: there is no `imports` namespace at runtime — import snapshots are
|
|
9
|
+
* stored under `resources.<alias>`. Host environment variables are reached
|
|
10
|
+
* by declaring a typed `variables:`/`secrets:` entry with an `env:` binding
|
|
11
|
+
* and referencing `variables.X` / `secrets.X`.
|
|
12
12
|
*/
|
|
13
|
-
export const KERNEL_GLOBAL_NAMES = ["variables", "secrets", "resources", "ports"
|
|
13
|
+
export const KERNEL_GLOBAL_NAMES = ["variables", "secrets", "resources", "ports"];
|
|
14
14
|
const SYSTEM_KINDS = new Set([
|
|
15
15
|
"Telo.Definition",
|
|
16
16
|
"Telo.Application",
|
|
@@ -29,7 +29,6 @@ const SYSTEM_KINDS = new Set([
|
|
|
29
29
|
* in the outer module; Libraries are only relevant when the caller scoped
|
|
30
30
|
* the manifest list to a single library's file.
|
|
31
31
|
* - `resources`: enumerates all non-system resource names
|
|
32
|
-
* - `env`: dynamic (runtime env vars, root module only)
|
|
33
32
|
*/
|
|
34
33
|
export function buildKernelGlobalsSchema(manifests) {
|
|
35
34
|
const moduleManifest = manifests.find((m) => m.kind === "Telo.Application") ??
|
|
@@ -56,7 +55,6 @@ export function buildKernelGlobalsSchema(manifests) {
|
|
|
56
55
|
additionalProperties: false,
|
|
57
56
|
},
|
|
58
57
|
ports: buildPortsSchema(moduleManifest?.ports),
|
|
59
|
-
env: { type: "object", additionalProperties: true },
|
|
60
58
|
},
|
|
61
59
|
};
|
|
62
60
|
}
|
|
@@ -98,7 +96,7 @@ function buildSchemaMapSchema(schemaMap) {
|
|
|
98
96
|
}
|
|
99
97
|
/**
|
|
100
98
|
* Merge kernel globals into an `x-telo-context` schema so chain-access
|
|
101
|
-
* validation recognises `variables`, `secrets`, `resources`, `
|
|
99
|
+
* validation recognises `variables`, `secrets`, `resources`, `ports`
|
|
102
100
|
* without module authors having to re-declare them.
|
|
103
101
|
*
|
|
104
102
|
* Context-specific properties take precedence over globals (spread order).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile-module-versions.d.ts","sourceRoot":"","sources":["../src/reconcile-module-versions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;0CAI0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;2EACuE;IACvE,WAAW,EAAE,kBAAkB,EAAE,CAAC;CACnC;
|
|
1
|
+
{"version":3,"file":"reconcile-module-versions.d.ts","sourceRoot":"","sources":["../src/reconcile-module-versions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;0CAI0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;2EACuE;IACvE,WAAW,EAAE,kBAAkB,EAAE,CAAC;CACnC;AAwKD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAClC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,GAChD,qBAAqB,CAiDvB"}
|
|
@@ -148,15 +148,10 @@ function hoistDiagnostic(identity, importerSource, alias, loser, winner, conflic
|
|
|
148
148
|
data,
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
message: `Module '${identity}@${loser.version || "<unknown>"}' was hoisted to '${winner.version}' ` +
|
|
156
|
-
`because the same module is imported at the higher version elsewhere in the graph. ` +
|
|
157
|
-
`Pre-1.0 versions are additive, so the higher version is used for every importer.`,
|
|
158
|
-
data,
|
|
159
|
-
};
|
|
151
|
+
// Same-major hoist to a higher version: additive pre-1.0, so the redirect is
|
|
152
|
+
// non-lossy and by design — resolve to the winner silently, like a package
|
|
153
|
+
// manager picking the highest compatible version of a transitive dep.
|
|
154
|
+
return null;
|
|
160
155
|
}
|
|
161
156
|
/**
|
|
162
157
|
* Reconcile a loaded import graph so each module identity (`namespace/name`)
|
|
@@ -97,4 +97,13 @@ export declare function extractContextsFromSchema(schema: Record<string, any>, p
|
|
|
97
97
|
scope: string;
|
|
98
98
|
schema: Record<string, any>;
|
|
99
99
|
}>;
|
|
100
|
+
/**
|
|
101
|
+
* Walk a JSON Schema tree and collect the JSONPath scopes of every field that
|
|
102
|
+
* declares a CEL-bearing region (`x-telo-context` / `x-telo-step-context` /
|
|
103
|
+
* `x-telo-error-context`). Used — alongside `x-telo-eval` paths — to decide
|
|
104
|
+
* whether a `!cel` expression sits in a slot the runtime actually evaluates.
|
|
105
|
+
* Scopes use the same `$.a.b[*]` form as `extractContextsFromSchema`, matched
|
|
106
|
+
* against expression paths with `pathMatchesScope`.
|
|
107
|
+
*/
|
|
108
|
+
export declare function extractCelRegionScopes(schema: Record<string, any>, path?: string): string[];
|
|
100
109
|
//# sourceMappingURL=validate-cel-context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-cel-context.d.ts","sourceRoot":"","sources":["../src/validate-cel-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAGtF,MAAM,WAAW,kBAAkB;IACjC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC;;kDAE8C;IAC9C,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;KACxD,CAAC;IACF,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KAC/C,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACnC,QAAQ,GAAE,WAAW,CAAC,MAAM,CAAa,GACxC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA0CjC;AAuFD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,IAAI,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAChD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAgHrB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAQrB;AAWD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,SAAM,GACT,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAAC,CAGvD"}
|
|
1
|
+
{"version":3,"file":"validate-cel-context.d.ts","sourceRoot":"","sources":["../src/validate-cel-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAGtF,MAAM,WAAW,kBAAkB;IACjC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC;;kDAE8C;IAC9C,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;KACxD,CAAC;IACF,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KAC/C,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACnC,QAAQ,GAAE,WAAW,CAAC,MAAM,CAAa,GACxC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA0CjC;AAuFD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,IAAI,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAChD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAgHrB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAQrB;AAWD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,SAAM,GACT,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAAC,CAGvD;AAUD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,SAAM,GAAG,MAAM,EAAE,CAqBxF"}
|
|
@@ -334,6 +334,43 @@ export function extractContextsFromSchema(schema, path = "$") {
|
|
|
334
334
|
const all = collectContexts(schema, path);
|
|
335
335
|
return all.sort((a, b) => b.scope.length - a.scope.length);
|
|
336
336
|
}
|
|
337
|
+
/** Schema keys that declare a CEL-bearing region: a field carrying any of these
|
|
338
|
+
* is evaluated at runtime, so a `!cel` inside it (or a descendant) is live. */
|
|
339
|
+
const CEL_REGION_KEYS = [
|
|
340
|
+
"x-telo-context",
|
|
341
|
+
"x-telo-step-context",
|
|
342
|
+
"x-telo-error-context",
|
|
343
|
+
];
|
|
344
|
+
/**
|
|
345
|
+
* Walk a JSON Schema tree and collect the JSONPath scopes of every field that
|
|
346
|
+
* declares a CEL-bearing region (`x-telo-context` / `x-telo-step-context` /
|
|
347
|
+
* `x-telo-error-context`). Used — alongside `x-telo-eval` paths — to decide
|
|
348
|
+
* whether a `!cel` expression sits in a slot the runtime actually evaluates.
|
|
349
|
+
* Scopes use the same `$.a.b[*]` form as `extractContextsFromSchema`, matched
|
|
350
|
+
* against expression paths with `pathMatchesScope`.
|
|
351
|
+
*/
|
|
352
|
+
export function extractCelRegionScopes(schema, path = "$") {
|
|
353
|
+
if (!schema || typeof schema !== "object")
|
|
354
|
+
return [];
|
|
355
|
+
const out = [];
|
|
356
|
+
if (CEL_REGION_KEYS.some((k) => schema[k]))
|
|
357
|
+
out.push(path);
|
|
358
|
+
if (schema.properties) {
|
|
359
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
|
360
|
+
out.push(...extractCelRegionScopes(value, `${path}.${key}`));
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (schema.items && typeof schema.items === "object") {
|
|
364
|
+
out.push(...extractCelRegionScopes(schema.items, `${path}[*]`));
|
|
365
|
+
}
|
|
366
|
+
for (const key of ["oneOf", "anyOf", "allOf"]) {
|
|
367
|
+
if (Array.isArray(schema[key])) {
|
|
368
|
+
for (const subschema of schema[key])
|
|
369
|
+
out.push(...extractCelRegionScopes(subschema, path));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return out;
|
|
373
|
+
}
|
|
337
374
|
function collectContexts(schema, path) {
|
|
338
375
|
if (!schema || typeof schema !== "object")
|
|
339
376
|
return [];
|
package/package.json
CHANGED
package/src/analyzer.ts
CHANGED
|
@@ -30,12 +30,14 @@ import {
|
|
|
30
30
|
} from "./schema-compat.js";
|
|
31
31
|
import { DiagnosticSeverity, type AnalysisDiagnostic, type AnalysisOptions } from "./types.js";
|
|
32
32
|
import {
|
|
33
|
+
extractCelRegionScopes,
|
|
33
34
|
extractContextsFromSchema,
|
|
34
35
|
getManifestItem,
|
|
35
36
|
pathMatchesScope,
|
|
36
37
|
resolveContextAnnotations,
|
|
37
38
|
resolveTypeFieldToSchema,
|
|
38
39
|
} from "./validate-cel-context.js";
|
|
40
|
+
import { buildEvalPaths, evalPathsCover } from "./eval-paths.js";
|
|
39
41
|
import { validateExtends } from "./validate-extends.js";
|
|
40
42
|
import { validateNestedInlineResources } from "./validate-nested-inline.js";
|
|
41
43
|
import { validateProviderCoherence } from "./validate-provider-coherence.js";
|
|
@@ -393,6 +395,32 @@ function buildStepContextSchema(
|
|
|
393
395
|
* specific field name (or `Run.Sequence`) is hardcoded; any composer that tags
|
|
394
396
|
* its error-bearing branch fields opts in the same way.
|
|
395
397
|
*/
|
|
398
|
+
/**
|
|
399
|
+
* True when a `walkCelExpressions` path (`with[0].handler.inputs.x`) crosses an
|
|
400
|
+
* inline nested resource — an `{ kind: … }` object below the host root — before
|
|
401
|
+
* reaching the leaf. Such CEL belongs to the nested resource's kind (validated
|
|
402
|
+
* when that resource is analyzed), not the host's schema, so the
|
|
403
|
+
* non-eval-field check must not attribute it to the host.
|
|
404
|
+
*/
|
|
405
|
+
function pathCrossesNestedResource(root: unknown, path: string): boolean {
|
|
406
|
+
const segments = path.match(/[^.[\]]+/g) ?? [];
|
|
407
|
+
let node: unknown = root;
|
|
408
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
409
|
+
node = Array.isArray(node)
|
|
410
|
+
? node[Number(segments[i])]
|
|
411
|
+
: (node as Record<string, unknown> | undefined)?.[segments[i]!];
|
|
412
|
+
if (
|
|
413
|
+
node !== null &&
|
|
414
|
+
typeof node === "object" &&
|
|
415
|
+
!Array.isArray(node) &&
|
|
416
|
+
typeof (node as { kind?: unknown }).kind === "string"
|
|
417
|
+
) {
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
|
|
396
424
|
function collectErrorContextScopes(
|
|
397
425
|
defSchema: Record<string, any> | undefined,
|
|
398
426
|
): Map<string, Record<string, any>> {
|
|
@@ -1189,6 +1217,13 @@ export class StaticAnalyzer {
|
|
|
1189
1217
|
let celStepContextSchema: Record<string, any> | undefined;
|
|
1190
1218
|
let celInvocationContext: Record<string, any> | undefined;
|
|
1191
1219
|
let celErrorScopes: Map<string, Record<string, any>> = new Map();
|
|
1220
|
+
// Region coverage for the "CEL in a non-eval field" check: the union of
|
|
1221
|
+
// `x-telo-eval` paths (own + capability) and `x-telo-context` /
|
|
1222
|
+
// `x-telo-step-context` / `x-telo-error-context` scopes. A `!cel` outside
|
|
1223
|
+
// every region is read as a literal — the runtime never evaluates it.
|
|
1224
|
+
let celEvalPaths: string[] = [];
|
|
1225
|
+
let celRegionScopes: string[] = [];
|
|
1226
|
+
let celRuleApplies = false;
|
|
1192
1227
|
|
|
1193
1228
|
visitManifest(
|
|
1194
1229
|
allManifests,
|
|
@@ -1211,6 +1246,26 @@ export class StaticAnalyzer {
|
|
|
1211
1246
|
celErrorScopes = collectErrorContextScopes(
|
|
1212
1247
|
e.definition?.schema as Record<string, any> | undefined,
|
|
1213
1248
|
);
|
|
1249
|
+
|
|
1250
|
+
// The non-eval-field check only applies to runtime resource instances:
|
|
1251
|
+
// structural / templating kinds (capability `Telo.Template`, or no
|
|
1252
|
+
// definition) carry CEL the kernel evaluates by other rules.
|
|
1253
|
+
const capability = e.definition?.capability;
|
|
1254
|
+
celRuleApplies =
|
|
1255
|
+
!!e.definition?.schema && capability !== undefined && capability !== "Telo.Template";
|
|
1256
|
+
if (celRuleApplies) {
|
|
1257
|
+
const ownSchema = e.definition!.schema as Record<string, any>;
|
|
1258
|
+
const own = buildEvalPaths(ownSchema);
|
|
1259
|
+
const capabilityDef = capability ? defs.resolve(capability) : undefined;
|
|
1260
|
+
const parent = capabilityDef?.schema
|
|
1261
|
+
? buildEvalPaths(capabilityDef.schema as Record<string, any>)
|
|
1262
|
+
: { compile: [], runtime: [] };
|
|
1263
|
+
celEvalPaths = [...own.compile, ...own.runtime, ...parent.compile, ...parent.runtime];
|
|
1264
|
+
celRegionScopes = extractCelRegionScopes(ownSchema);
|
|
1265
|
+
} else {
|
|
1266
|
+
celEvalPaths = [];
|
|
1267
|
+
celRegionScopes = [];
|
|
1268
|
+
}
|
|
1214
1269
|
},
|
|
1215
1270
|
onCel: (e) => {
|
|
1216
1271
|
const m = e.source;
|
|
@@ -1218,6 +1273,29 @@ export class StaticAnalyzer {
|
|
|
1218
1273
|
const filePath = (m.metadata as { source?: string } | undefined)?.source;
|
|
1219
1274
|
const { expr, path, engineName, matchedScope } = e;
|
|
1220
1275
|
|
|
1276
|
+
// A `!cel` (or `${{ }}`) in a field with no `x-telo-eval` / `x-telo-context`
|
|
1277
|
+
// is never evaluated — the runtime reads it as a literal (e.g. a
|
|
1278
|
+
// `concurrency` `!cel` that silently degraded to a sparse `[null, …]`).
|
|
1279
|
+
// Flag it rather than letting it pass as valid CEL. Inline resources
|
|
1280
|
+
// (resource-wide invocation context) carry CEL the kernel evaluates.
|
|
1281
|
+
if (
|
|
1282
|
+
celRuleApplies &&
|
|
1283
|
+
engineName === "cel" &&
|
|
1284
|
+
celInvocationContext === undefined &&
|
|
1285
|
+
!evalPathsCover(celEvalPaths, path) &&
|
|
1286
|
+
!celRegionScopes.some((scope) => pathMatchesScope(path, scope)) &&
|
|
1287
|
+
!pathCrossesNestedResource(m, path)
|
|
1288
|
+
) {
|
|
1289
|
+
diagnostics.push({
|
|
1290
|
+
severity: DiagnosticSeverity.Error,
|
|
1291
|
+
code: "CEL_IN_NON_EVAL_FIELD",
|
|
1292
|
+
source: SOURCE,
|
|
1293
|
+
message: `${m.kind}/${resource.name}: CEL at '${path}' is never evaluated — the field has no x-telo-eval / x-telo-context annotation, so its value is read as a literal. Annotate the field as a CEL slot or remove the !cel tag.`,
|
|
1294
|
+
data: { resource, filePath, path },
|
|
1295
|
+
});
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1221
1299
|
let matchedContext: Record<string, any> | undefined =
|
|
1222
1300
|
e.contextSchema ?? celInvocationContext;
|
|
1223
1301
|
|
package/src/cel-environment.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type { CelHandlers } from "@telorun/templating";
|
|
|
17
17
|
*
|
|
18
18
|
* - `variables`: typed from the manifest's `variables` field if it is a schema map
|
|
19
19
|
* (only module-identity docs — `Telo.Application` / `Telo.Library` — carry this); otherwise registered as `map` (dyn).
|
|
20
|
-
* - `secrets`, `resources
|
|
20
|
+
* - `secrets`, `resources`: always `map` (dyn — output schemas unknown).
|
|
21
21
|
* - `extraContextSchema`: additional variables from an `x-telo-context` annotation.
|
|
22
22
|
*
|
|
23
23
|
* NOTE: The set of kernel globals registered here must match `KERNEL_GLOBAL_NAMES`
|
|
@@ -85,7 +85,6 @@ export function buildTypedCelEnvironment(
|
|
|
85
85
|
|
|
86
86
|
env.registerVariable("secrets", "map");
|
|
87
87
|
env.registerVariable("resources", "map");
|
|
88
|
-
env.registerVariable("env", "map");
|
|
89
88
|
|
|
90
89
|
if (extraContextSchema?.properties) {
|
|
91
90
|
for (const [name, propSchema] of Object.entries(
|
|
@@ -128,7 +127,7 @@ function registerConfigNamespace(
|
|
|
128
127
|
*
|
|
129
128
|
* Import inputs are a config-only contract: their expressions are evaluated
|
|
130
129
|
* against the IMPORTING module's `variables`/`secrets`, never the import's own
|
|
131
|
-
* values map (the bug) nor the imported child's. `resources
|
|
130
|
+
* values map (the bug) nor the imported child's. `resources` and `ports`
|
|
132
131
|
* are registered as empty typed objects, so referencing them is a "No such key"
|
|
133
132
|
* error that steers authors to a typed `variables` entry. */
|
|
134
133
|
export function buildImportInputCelEnvironment(
|
|
@@ -151,10 +150,10 @@ export function buildImportInputCelEnvironment(
|
|
|
151
150
|
env.registerVariable("variables", "map");
|
|
152
151
|
env.registerVariable("secrets", "map");
|
|
153
152
|
}
|
|
154
|
-
// Override the base env's dyn `resources`/`
|
|
155
|
-
// so any access (`resources.X`, `
|
|
153
|
+
// Override the base env's dyn `resources`/`ports` with empty typed objects
|
|
154
|
+
// so any access (`resources.X`, `ports.X`) is a "No such key" error — these
|
|
156
155
|
// surfaces are not part of the config-only import contract.
|
|
157
|
-
for (const name of ["resources", "
|
|
156
|
+
for (const name of ["resources", "ports"]) {
|
|
158
157
|
(env as any).registerVariable({ name, schema: {} });
|
|
159
158
|
}
|
|
160
159
|
return env;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single containment rule for `x-telo-eval` paths, shared by every matcher so
|
|
3
|
+
* the analyzer's coverage decision and the kernel's expansion/exclusion can't
|
|
4
|
+
* drift. True when `target` lies in the subtree rooted at `evalPath`: `"**"`
|
|
5
|
+
* covers everything; a dotted path covers itself and any descendant — `"handler"`
|
|
6
|
+
* covers `handler`, `handler.body`, `handler[0]`. Targets use `walkCelExpressions`
|
|
7
|
+
* form (`a.b[0].c`); eval paths are property-only (no array segments —
|
|
8
|
+
* `buildEvalPaths` does not descend into `items`), so `.`/`[` boundary prefixing
|
|
9
|
+
* is exact. Consumers: the analyzer's `evalPathsCover`, the kernel's `isExcluded`
|
|
10
|
+
* (applied in both directions), and — structurally — `expandPaths`' navigation.
|
|
11
|
+
*/
|
|
12
|
+
export function evalPathCovers(evalPath: string, target: string): boolean {
|
|
13
|
+
if (evalPath === "**") return true;
|
|
14
|
+
return (
|
|
15
|
+
target === evalPath || target.startsWith(`${evalPath}.`) || target.startsWith(`${evalPath}[`)
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** True when any `x-telo-eval` path in the set covers `exprPath` (see
|
|
20
|
+
* {@link evalPathCovers}). */
|
|
21
|
+
export function evalPathsCover(evalPaths: readonly string[], exprPath: string): boolean {
|
|
22
|
+
return evalPaths.some((p) => evalPathCovers(p, exprPath));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Traverses a definition schema and collects all paths annotated with `x-telo-eval`.
|
|
27
|
+
* Root-level `x-telo-eval` produces the `"**"` wildcard (expand all fields).
|
|
28
|
+
* Property-level annotations produce the dot-notation path to that property.
|
|
29
|
+
*/
|
|
30
|
+
export function buildEvalPaths(schema: Record<string, any>): {
|
|
31
|
+
compile: string[];
|
|
32
|
+
runtime: string[];
|
|
33
|
+
} {
|
|
34
|
+
const compile: string[] = [];
|
|
35
|
+
const runtime: string[] = [];
|
|
36
|
+
|
|
37
|
+
if (schema["x-telo-eval"] === "compile") compile.push("**");
|
|
38
|
+
else if (schema["x-telo-eval"] === "runtime") runtime.push("**");
|
|
39
|
+
|
|
40
|
+
if (schema.properties) {
|
|
41
|
+
for (const [key, propSchema] of Object.entries(schema.properties as Record<string, any>)) {
|
|
42
|
+
collectEvalPathsNode(propSchema, key, compile, runtime);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { compile, runtime };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function collectEvalPathsNode(
|
|
50
|
+
node: Record<string, any>,
|
|
51
|
+
path: string,
|
|
52
|
+
compile: string[],
|
|
53
|
+
runtime: string[],
|
|
54
|
+
): void {
|
|
55
|
+
if (node["x-telo-eval"] === "compile") {
|
|
56
|
+
compile.push(path);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (node["x-telo-eval"] === "runtime") {
|
|
60
|
+
runtime.push(path);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (node.properties) {
|
|
64
|
+
for (const [key, propSchema] of Object.entries(node.properties as Record<string, any>)) {
|
|
65
|
+
collectEvalPathsNode(propSchema, `${path}.${key}`, compile, runtime);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/index.ts
CHANGED
package/src/kernel-globals.ts
CHANGED
|
@@ -7,12 +7,12 @@ import { residualEntrySchemaMap } from "./residual-schema.js";
|
|
|
7
7
|
* `buildTypedCelEnvironment` in cel-environment.ts (CEL type-checking)
|
|
8
8
|
* must stay in sync with this list.
|
|
9
9
|
*
|
|
10
|
-
* Note:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Note: there is no `imports` namespace at runtime — import snapshots are
|
|
11
|
+
* stored under `resources.<alias>`. Host environment variables are reached
|
|
12
|
+
* by declaring a typed `variables:`/`secrets:` entry with an `env:` binding
|
|
13
|
+
* and referencing `variables.X` / `secrets.X`.
|
|
14
14
|
*/
|
|
15
|
-
export const KERNEL_GLOBAL_NAMES = ["variables", "secrets", "resources", "ports"
|
|
15
|
+
export const KERNEL_GLOBAL_NAMES = ["variables", "secrets", "resources", "ports"] as const;
|
|
16
16
|
|
|
17
17
|
const SYSTEM_KINDS = new Set([
|
|
18
18
|
"Telo.Definition",
|
|
@@ -33,7 +33,6 @@ const SYSTEM_KINDS = new Set([
|
|
|
33
33
|
* in the outer module; Libraries are only relevant when the caller scoped
|
|
34
34
|
* the manifest list to a single library's file.
|
|
35
35
|
* - `resources`: enumerates all non-system resource names
|
|
36
|
-
* - `env`: dynamic (runtime env vars, root module only)
|
|
37
36
|
*/
|
|
38
37
|
export function buildKernelGlobalsSchema(
|
|
39
38
|
manifests: ResourceManifest[],
|
|
@@ -68,7 +67,6 @@ export function buildKernelGlobalsSchema(
|
|
|
68
67
|
additionalProperties: false,
|
|
69
68
|
},
|
|
70
69
|
ports: buildPortsSchema(moduleManifest?.ports),
|
|
71
|
-
env: { type: "object", additionalProperties: true },
|
|
72
70
|
},
|
|
73
71
|
};
|
|
74
72
|
}
|
|
@@ -117,7 +115,7 @@ function buildSchemaMapSchema(
|
|
|
117
115
|
|
|
118
116
|
/**
|
|
119
117
|
* Merge kernel globals into an `x-telo-context` schema so chain-access
|
|
120
|
-
* validation recognises `variables`, `secrets`, `resources`, `
|
|
118
|
+
* validation recognises `variables`, `secrets`, `resources`, `ports`
|
|
121
119
|
* without module authors having to re-declare them.
|
|
122
120
|
*
|
|
123
121
|
* Context-specific properties take precedence over globals (spread order).
|
|
@@ -177,16 +177,10 @@ function hoistDiagnostic(
|
|
|
177
177
|
data,
|
|
178
178
|
};
|
|
179
179
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
message:
|
|
185
|
-
`Module '${identity}@${loser.version || "<unknown>"}' was hoisted to '${winner.version}' ` +
|
|
186
|
-
`because the same module is imported at the higher version elsewhere in the graph. ` +
|
|
187
|
-
`Pre-1.0 versions are additive, so the higher version is used for every importer.`,
|
|
188
|
-
data,
|
|
189
|
-
};
|
|
180
|
+
// Same-major hoist to a higher version: additive pre-1.0, so the redirect is
|
|
181
|
+
// non-lossy and by design — resolve to the winner silently, like a package
|
|
182
|
+
// manager picking the highest compatible version of a transitive dep.
|
|
183
|
+
return null;
|
|
190
184
|
}
|
|
191
185
|
|
|
192
186
|
/**
|
|
@@ -396,6 +396,45 @@ export function extractContextsFromSchema(
|
|
|
396
396
|
return all.sort((a, b) => b.scope.length - a.scope.length);
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
+
/** Schema keys that declare a CEL-bearing region: a field carrying any of these
|
|
400
|
+
* is evaluated at runtime, so a `!cel` inside it (or a descendant) is live. */
|
|
401
|
+
const CEL_REGION_KEYS = [
|
|
402
|
+
"x-telo-context",
|
|
403
|
+
"x-telo-step-context",
|
|
404
|
+
"x-telo-error-context",
|
|
405
|
+
] as const;
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Walk a JSON Schema tree and collect the JSONPath scopes of every field that
|
|
409
|
+
* declares a CEL-bearing region (`x-telo-context` / `x-telo-step-context` /
|
|
410
|
+
* `x-telo-error-context`). Used — alongside `x-telo-eval` paths — to decide
|
|
411
|
+
* whether a `!cel` expression sits in a slot the runtime actually evaluates.
|
|
412
|
+
* Scopes use the same `$.a.b[*]` form as `extractContextsFromSchema`, matched
|
|
413
|
+
* against expression paths with `pathMatchesScope`.
|
|
414
|
+
*/
|
|
415
|
+
export function extractCelRegionScopes(schema: Record<string, any>, path = "$"): string[] {
|
|
416
|
+
if (!schema || typeof schema !== "object") return [];
|
|
417
|
+
const out: string[] = [];
|
|
418
|
+
|
|
419
|
+
if (CEL_REGION_KEYS.some((k) => schema[k])) out.push(path);
|
|
420
|
+
|
|
421
|
+
if (schema.properties) {
|
|
422
|
+
for (const [key, value] of Object.entries(schema.properties as Record<string, any>)) {
|
|
423
|
+
out.push(...extractCelRegionScopes(value, `${path}.${key}`));
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (schema.items && typeof schema.items === "object") {
|
|
427
|
+
out.push(...extractCelRegionScopes(schema.items, `${path}[*]`));
|
|
428
|
+
}
|
|
429
|
+
for (const key of ["oneOf", "anyOf", "allOf"] as const) {
|
|
430
|
+
if (Array.isArray(schema[key])) {
|
|
431
|
+
for (const subschema of schema[key]) out.push(...extractCelRegionScopes(subschema, path));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return out;
|
|
436
|
+
}
|
|
437
|
+
|
|
399
438
|
function collectContexts(
|
|
400
439
|
schema: Record<string, any>,
|
|
401
440
|
path: string,
|