@telorun/analyzer 0.63.0 → 0.65.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/analysis-registry.d.ts +24 -0
- package/dist/analysis-registry.d.ts.map +1 -1
- package/dist/analysis-registry.js +35 -0
- package/dist/analyzer.d.ts +3 -37
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +76 -470
- package/dist/cel-scope-query.d.ts +109 -0
- package/dist/cel-scope-query.d.ts.map +1 -0
- package/dist/cel-scope-query.js +270 -0
- package/dist/cel-scope.d.ts +166 -0
- package/dist/cel-scope.d.ts.map +1 -0
- package/dist/cel-scope.js +377 -0
- package/dist/definition-registry.d.ts +38 -6
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +66 -22
- package/dist/find-manifest.d.ts +10 -0
- package/dist/find-manifest.d.ts.map +1 -0
- package/dist/find-manifest.js +12 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/invocation-contract.d.ts +11 -0
- package/dist/invocation-contract.d.ts.map +1 -1
- package/dist/invocation-contract.js +15 -0
- package/dist/manifest-analysis.d.ts +73 -0
- package/dist/manifest-analysis.d.ts.map +1 -0
- package/dist/manifest-analysis.js +78 -0
- package/dist/manifest-path.d.ts +18 -0
- package/dist/manifest-path.d.ts.map +1 -0
- package/dist/manifest-path.js +37 -0
- package/dist/schema-compat.d.ts +59 -22
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +60 -75
- package/dist/schema-error-report.d.ts +68 -0
- package/dist/schema-error-report.d.ts.map +1 -0
- package/dist/schema-error-report.js +356 -0
- package/dist/schema-walk.d.ts +25 -0
- package/dist/schema-walk.d.ts.map +1 -0
- package/dist/schema-walk.js +126 -0
- package/dist/telo-version.d.ts +1 -1
- package/dist/telo-version.js +1 -1
- package/dist/validate-nested-inline.d.ts +22 -1
- package/dist/validate-nested-inline.d.ts.map +1 -1
- package/dist/validate-nested-inline.js +17 -9
- package/dist/validate-step-inputs.d.ts +17 -0
- package/dist/validate-step-inputs.d.ts.map +1 -1
- package/dist/validate-step-inputs.js +108 -9
- package/package.json +2 -2
- package/src/analysis-registry.ts +37 -0
- package/src/analyzer.ts +83 -587
- package/src/cel-scope-query.ts +337 -0
- package/src/cel-scope.ts +570 -0
- package/src/definition-registry.ts +79 -24
- package/src/find-manifest.ts +19 -0
- package/src/index.ts +23 -2
- package/src/invocation-contract.ts +22 -0
- package/src/manifest-analysis.ts +132 -0
- package/src/manifest-path.ts +34 -0
- package/src/schema-compat.ts +92 -79
- package/src/schema-error-report.ts +417 -0
- package/src/schema-walk.ts +144 -0
- package/src/telo-version.ts +1 -1
- package/src/validate-nested-inline.ts +35 -14
- package/src/validate-step-inputs.ts +153 -11
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **Ask what a CEL site sees, from outside the analysis pass.**
|
|
3
|
+
*
|
|
4
|
+
* The pass resolves a scope per expression as it walks. An IDE has no walk: it
|
|
5
|
+
* has a cursor, and needs the same answer for one address — often for an
|
|
6
|
+
* expression the last analysis never saw, because the user is typing it. So the
|
|
7
|
+
* query is driven by (manifest, path) rather than by a visitor event, and the
|
|
8
|
+
* `x-telo-context` match is recomputed here exactly as the visitor computes it
|
|
9
|
+
* (`extractContextsFromSchema` + `pathMatchesScope`, the same two functions).
|
|
10
|
+
*
|
|
11
|
+
* The scope RULE itself is not re-implemented — {@link CelScopeResolver} is the
|
|
12
|
+
* one that answers, here and in the pass. What this module adds is the way in.
|
|
13
|
+
*/
|
|
14
|
+
import type { ResourceManifest } from "@telorun/sdk";
|
|
15
|
+
import type { Environment } from "@marcbachmann/cel-js";
|
|
16
|
+
import { AliasResolver } from "./alias-resolver.js";
|
|
17
|
+
import { type CelScope } from "./cel-scope.js";
|
|
18
|
+
import { DefinitionRegistry } from "./definition-registry.js";
|
|
19
|
+
/** Where a CEL context binding was declared: the manifest that declares it,
|
|
20
|
+
* by identity, plus the concrete path within it. Identity rather than the
|
|
21
|
+
* object because a host locates a manifest in its own loaded files, which is
|
|
22
|
+
* what carries the source ranges. */
|
|
23
|
+
export interface ContextDeclarationSite {
|
|
24
|
+
kind: string;
|
|
25
|
+
name: string;
|
|
26
|
+
path: string;
|
|
27
|
+
}
|
|
28
|
+
/** The analyzer state a query resolves against — what an `AnalysisRegistry`
|
|
29
|
+
* already holds, plus the manifest set the caller analyzed. */
|
|
30
|
+
export interface CelScopeQueryContext {
|
|
31
|
+
defs: DefinitionRegistry;
|
|
32
|
+
aliases: AliasResolver;
|
|
33
|
+
aliasesByModule: Map<string, AliasResolver>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A reusable scope query over one manifest set.
|
|
37
|
+
*
|
|
38
|
+
* Built once per analysis rather than per keystroke: the observed-state index
|
|
39
|
+
* and the kernel globals are a function of the whole set, and rebuilding them
|
|
40
|
+
* for every cursor move would put a full-set walk on the hover path.
|
|
41
|
+
*/
|
|
42
|
+
export declare class CelScopeQuery {
|
|
43
|
+
private readonly manifests;
|
|
44
|
+
private readonly resolver;
|
|
45
|
+
/** The resource the resolver is currently entered on — `enterResource` is the
|
|
46
|
+
* per-resource half of the rule, so re-entering per query is only needed when
|
|
47
|
+
* the cursor moves to a different resource. */
|
|
48
|
+
private entered;
|
|
49
|
+
/** Resolved scopes, keyed by resource and path. A context-matched site builds
|
|
50
|
+
* a fresh typed environment by design (a clone plus a re-registration of
|
|
51
|
+
* every variable), which is affordable once per site in a batch pass and not
|
|
52
|
+
* once per site per KEYSTROKE — which is what a whole-file colourizer asks
|
|
53
|
+
* for. The lifetime is this query's, which is the analysis's. */
|
|
54
|
+
private readonly scopeCache;
|
|
55
|
+
constructor(manifests: ResourceManifest[], ctx: CelScopeQueryContext, celEnv?: Environment);
|
|
56
|
+
private readonly ctx;
|
|
57
|
+
/** The manifest a cursor's document addresses — {@link findManifest}, the one
|
|
58
|
+
* implementation `ManifestAnalysis` also answers from. */
|
|
59
|
+
resourceFor(kind: string | undefined, name: string | undefined): ResourceManifest | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* What CEL at `path` in `resource` is typed against.
|
|
62
|
+
*
|
|
63
|
+
* `path` is the CONCRETE path, indices and all (`routes[0].handler.url`) —
|
|
64
|
+
* an `x-telo-context` region, an error-bearing branch and a step's identity
|
|
65
|
+
* are each addressed per item, so an index-erased path resolves the wrong
|
|
66
|
+
* scope or none.
|
|
67
|
+
*/
|
|
68
|
+
scopeAt(resource: ResourceManifest, path: string): CelScope;
|
|
69
|
+
/**
|
|
70
|
+
* Where a CEL context binding was DECLARED — the manifest node the
|
|
71
|
+
* `x-telo-context-*` annotation derived it from.
|
|
72
|
+
*
|
|
73
|
+
* The mirror of {@link scopeAt}: that one resolves the annotation into a
|
|
74
|
+
* schema and the provenance is gone by the time it returns, while
|
|
75
|
+
* go-to-declaration wants the path and not the schema. Re-walking the
|
|
76
|
+
* annotation is what keeps `CelScope` free of an origin field every type
|
|
77
|
+
* consumer would have to ignore.
|
|
78
|
+
*
|
|
79
|
+
* Generic over the annotation, so `request.query` lands on the route's own
|
|
80
|
+
* `request.schema.query`, `self.<field>` on the definition's `schema`, and
|
|
81
|
+
* `result.<field>` on the INVOKED resource's `outputType` — one walk, no
|
|
82
|
+
* transport and no resource kind named here.
|
|
83
|
+
*
|
|
84
|
+
* Every candidate path is checked against the manifest before it is returned,
|
|
85
|
+
* so a binding the author never declared (a context annotation's static
|
|
86
|
+
* fallback properties) resolves to nothing rather than to a guessed node.
|
|
87
|
+
*/
|
|
88
|
+
contextDeclarationSite(resource: ResourceManifest, sitePath: string, parts: string[]): ContextDeclarationSite | undefined;
|
|
89
|
+
/** The manifest and path an annotated context property is derived from, and
|
|
90
|
+
* whether that node holds a property MAP (names directly) or a JSON Schema. */
|
|
91
|
+
private originOf;
|
|
92
|
+
/**
|
|
93
|
+
* Where the step named `stepName` is declared in `resource`, as a concrete
|
|
94
|
+
* path (`steps[2]`), or undefined when the resource declares no step body or
|
|
95
|
+
* holds no such step.
|
|
96
|
+
*
|
|
97
|
+
* For go-to-declaration on `steps.<name>.result`, which is the one CEL scope
|
|
98
|
+
* whose members ARE written somewhere in the manifest but are reached through
|
|
99
|
+
* no reference slot. Driven by the kind's own step-body annotation and the
|
|
100
|
+
* shared nesting walk, so a step inside a `try:` inside a `catch:` is found
|
|
101
|
+
* and no resource kind is named here.
|
|
102
|
+
*/
|
|
103
|
+
stepDeclarationPath(resource: ResourceManifest, stepName: string): string | undefined;
|
|
104
|
+
private definitionFor;
|
|
105
|
+
/** The `x-telo-context` region this path falls in, matched exactly as the
|
|
106
|
+
* manifest visitor matches it for an expression it walked onto. */
|
|
107
|
+
private matchContext;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=cel-scope-query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cel-scope-query.d.ts","sourceRoot":"","sources":["../src/cel-scope-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAqB,MAAM,qBAAqB,CAAC;AAEvE,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAiB9D;;;sCAGsC;AACtC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAoBD;gEACgE;AAChE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAC7C;AAED;;;;;;GAMG;AACH,qBAAa,aAAa;IActB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAb5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C;;oDAEgD;IAChD,OAAO,CAAC,OAAO,CAA+B;IAC9C;;;;sEAIkE;IAClE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsD;gBAG9D,SAAS,EAAE,gBAAgB,EAAE,EAC9C,GAAG,EAAE,oBAAoB,EACzB,MAAM,CAAC,EAAE,WAAW;IAgCtB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAuB;IAE3C;+DAC2D;IAC3D,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,gBAAgB,GAAG,SAAS;IAI7F;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ;IAe3D;;;;;;;;;;;;;;;;;;OAkBG;IACH,sBAAsB,CACpB,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EAAE,GACd,sBAAsB,GAAG,SAAS;IAqCrC;oFACgF;IAChF,OAAO,CAAC,QAAQ;IAgEhB;;;;;;;;;;OAUG;IACH,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAqBrF,OAAO,CAAC,aAAa;IAMrB;wEACoE;IACpE,OAAO,CAAC,YAAY;CAarB"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { buildCelEnvironment } from "./cel-environment.js";
|
|
2
|
+
import { CelScopeResolver } from "./cel-scope.js";
|
|
3
|
+
import { buildKernelGlobalsIndex } from "./kernel-globals.js";
|
|
4
|
+
import { isModuleKind } from "./module-kinds.js";
|
|
5
|
+
import { navigateConcretePath } from "./manifest-path.js";
|
|
6
|
+
import { findManifest } from "./find-manifest.js";
|
|
7
|
+
import { resolveLocalRef, walkStepArray } from "./schema-walk.js";
|
|
8
|
+
import { readStepSlot } from "./step-slot.js";
|
|
9
|
+
import { buildObservedStateIndex, buildObservedStateResourcesSchema, } from "./validate-observed-state.js";
|
|
10
|
+
import { extractContextsFromSchema, getManifestItem, pathMatchesScope, } from "./validate-cel-context.js";
|
|
11
|
+
/** Join a concrete path segment, tolerating an empty base (the manifest root). */
|
|
12
|
+
function joinPath(base, segment) {
|
|
13
|
+
return base ? `${base}.${segment}` : segment;
|
|
14
|
+
}
|
|
15
|
+
/** The concrete path of the array ITEM an `x-telo-context` scope matched — the
|
|
16
|
+
* path half of `getManifestItem`, which returns only the value. Empty when the
|
|
17
|
+
* scope is not per-item. */
|
|
18
|
+
function manifestItemPath(exprPath, scope) {
|
|
19
|
+
if (!scope)
|
|
20
|
+
return "";
|
|
21
|
+
const stripped = scope.startsWith("$.") ? scope.slice(2) : scope;
|
|
22
|
+
const wildcard = stripped.indexOf("[*]");
|
|
23
|
+
if (wildcard === -1)
|
|
24
|
+
return "";
|
|
25
|
+
const arrayProp = stripped.slice(0, wildcard);
|
|
26
|
+
const match = exprPath.match(new RegExp(`^${arrayProp}\\[(\\d+)\\]`));
|
|
27
|
+
return match ? `${arrayProp}[${match[1]}]` : "";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A reusable scope query over one manifest set.
|
|
31
|
+
*
|
|
32
|
+
* Built once per analysis rather than per keystroke: the observed-state index
|
|
33
|
+
* and the kernel globals are a function of the whole set, and rebuilding them
|
|
34
|
+
* for every cursor move would put a full-set walk on the hover path.
|
|
35
|
+
*/
|
|
36
|
+
export class CelScopeQuery {
|
|
37
|
+
manifests;
|
|
38
|
+
resolver;
|
|
39
|
+
/** The resource the resolver is currently entered on — `enterResource` is the
|
|
40
|
+
* per-resource half of the rule, so re-entering per query is only needed when
|
|
41
|
+
* the cursor moves to a different resource. */
|
|
42
|
+
entered;
|
|
43
|
+
/** Resolved scopes, keyed by resource and path. A context-matched site builds
|
|
44
|
+
* a fresh typed environment by design (a clone plus a re-registration of
|
|
45
|
+
* every variable), which is affordable once per site in a batch pass and not
|
|
46
|
+
* once per site per KEYSTROKE — which is what a whole-file colourizer asks
|
|
47
|
+
* for. The lifetime is this query's, which is the analysis's. */
|
|
48
|
+
scopeCache = new Map();
|
|
49
|
+
constructor(manifests, ctx, celEnv) {
|
|
50
|
+
this.manifests = manifests;
|
|
51
|
+
const { defs, aliases, aliasesByModule } = ctx;
|
|
52
|
+
const rootModules = new Set();
|
|
53
|
+
for (const m of manifests) {
|
|
54
|
+
if (isModuleKind(m.kind) && m.metadata?.name)
|
|
55
|
+
rootModules.add(m.metadata.name);
|
|
56
|
+
}
|
|
57
|
+
const scopes = { aliasesByModule, rootModules };
|
|
58
|
+
const observedState = buildObservedStateIndex(manifests, defs, aliases, scopes);
|
|
59
|
+
const reportsObservedState = [...observedState.values()].some((r) => r.status);
|
|
60
|
+
this.resolver = new CelScopeResolver({
|
|
61
|
+
celEnv: celEnv ?? buildCelEnvironment(),
|
|
62
|
+
defs,
|
|
63
|
+
aliases,
|
|
64
|
+
scopes,
|
|
65
|
+
allManifests: manifests,
|
|
66
|
+
kernelGlobals: buildKernelGlobalsIndex(manifests, observedState),
|
|
67
|
+
moduleManifest: manifests.find((mm) => mm.kind === "Telo.Application") ??
|
|
68
|
+
manifests.find((mm) => mm.kind === "Telo.Library"),
|
|
69
|
+
observedStateContext: reportsObservedState
|
|
70
|
+
? {
|
|
71
|
+
type: "object",
|
|
72
|
+
additionalProperties: true,
|
|
73
|
+
properties: { resources: buildObservedStateResourcesSchema(observedState, true) },
|
|
74
|
+
}
|
|
75
|
+
: null,
|
|
76
|
+
});
|
|
77
|
+
this.ctx = ctx;
|
|
78
|
+
}
|
|
79
|
+
ctx;
|
|
80
|
+
/** The manifest a cursor's document addresses — {@link findManifest}, the one
|
|
81
|
+
* implementation `ManifestAnalysis` also answers from. */
|
|
82
|
+
resourceFor(kind, name) {
|
|
83
|
+
return findManifest(this.manifests, kind, name);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* What CEL at `path` in `resource` is typed against.
|
|
87
|
+
*
|
|
88
|
+
* `path` is the CONCRETE path, indices and all (`routes[0].handler.url`) —
|
|
89
|
+
* an `x-telo-context` region, an error-bearing branch and a step's identity
|
|
90
|
+
* are each addressed per item, so an index-erased path resolves the wrong
|
|
91
|
+
* scope or none.
|
|
92
|
+
*/
|
|
93
|
+
scopeAt(resource, path) {
|
|
94
|
+
let byPath = this.scopeCache.get(resource);
|
|
95
|
+
if (!byPath)
|
|
96
|
+
this.scopeCache.set(resource, (byPath = new Map()));
|
|
97
|
+
const cached = byPath.get(path);
|
|
98
|
+
if (cached)
|
|
99
|
+
return cached;
|
|
100
|
+
if (this.entered !== resource) {
|
|
101
|
+
this.resolver.enterResource(resource, this.definitionFor(resource));
|
|
102
|
+
this.entered = resource;
|
|
103
|
+
}
|
|
104
|
+
const { contextSchema, matchedScope } = this.matchContext(resource, path);
|
|
105
|
+
const scope = this.resolver.scopeFor({ source: resource, path, contextSchema, matchedScope });
|
|
106
|
+
byPath.set(path, scope);
|
|
107
|
+
return scope;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Where a CEL context binding was DECLARED — the manifest node the
|
|
111
|
+
* `x-telo-context-*` annotation derived it from.
|
|
112
|
+
*
|
|
113
|
+
* The mirror of {@link scopeAt}: that one resolves the annotation into a
|
|
114
|
+
* schema and the provenance is gone by the time it returns, while
|
|
115
|
+
* go-to-declaration wants the path and not the schema. Re-walking the
|
|
116
|
+
* annotation is what keeps `CelScope` free of an origin field every type
|
|
117
|
+
* consumer would have to ignore.
|
|
118
|
+
*
|
|
119
|
+
* Generic over the annotation, so `request.query` lands on the route's own
|
|
120
|
+
* `request.schema.query`, `self.<field>` on the definition's `schema`, and
|
|
121
|
+
* `result.<field>` on the INVOKED resource's `outputType` — one walk, no
|
|
122
|
+
* transport and no resource kind named here.
|
|
123
|
+
*
|
|
124
|
+
* Every candidate path is checked against the manifest before it is returned,
|
|
125
|
+
* so a binding the author never declared (a context annotation's static
|
|
126
|
+
* fallback properties) resolves to nothing rather than to a guessed node.
|
|
127
|
+
*/
|
|
128
|
+
contextDeclarationSite(resource, sitePath, parts) {
|
|
129
|
+
if (parts.length < 2)
|
|
130
|
+
return undefined;
|
|
131
|
+
const { contextSchema, matchedScope } = this.matchContext(resource, sitePath);
|
|
132
|
+
const annotated = contextSchema?.properties?.[parts[0]];
|
|
133
|
+
if (!annotated)
|
|
134
|
+
return undefined;
|
|
135
|
+
const origin = this.originOf(annotated, resource, sitePath, matchedScope);
|
|
136
|
+
if (!origin)
|
|
137
|
+
return undefined;
|
|
138
|
+
// The ORIGIN decides the shape rather than positional fallthrough. A
|
|
139
|
+
// property-map origin holds the names themselves, so its first hop is direct
|
|
140
|
+
// and ONLY direct — trying `properties` there would let an author's own
|
|
141
|
+
// `properties:` key win over the name they wrote. Every deeper hop, and
|
|
142
|
+
// every hop of a JSON-Schema origin, goes through `properties`, with the
|
|
143
|
+
// inline `{ kind, schema }` wrapper as the one alternative a type field is
|
|
144
|
+
// routinely written as.
|
|
145
|
+
let base = origin.path;
|
|
146
|
+
for (let i = 1; i < parts.length; i++) {
|
|
147
|
+
const candidates = i === 1 && origin.propertyMap
|
|
148
|
+
? [joinPath(base, parts[i])]
|
|
149
|
+
: [
|
|
150
|
+
joinPath(joinPath(base, "properties"), parts[i]),
|
|
151
|
+
joinPath(joinPath(joinPath(base, "schema"), "properties"), parts[i]),
|
|
152
|
+
];
|
|
153
|
+
const hit = candidates.find((candidate) => navigateConcretePath(origin.manifest, candidate) !== undefined);
|
|
154
|
+
if (!hit)
|
|
155
|
+
return undefined;
|
|
156
|
+
base = hit;
|
|
157
|
+
}
|
|
158
|
+
const metadata = origin.manifest.metadata;
|
|
159
|
+
if (!origin.manifest.kind || !metadata?.name)
|
|
160
|
+
return undefined;
|
|
161
|
+
return { kind: origin.manifest.kind, name: metadata.name, path: base };
|
|
162
|
+
}
|
|
163
|
+
/** The manifest and path an annotated context property is derived from, and
|
|
164
|
+
* whether that node holds a property MAP (names directly) or a JSON Schema. */
|
|
165
|
+
originOf(annotated, resource, sitePath, matchedScope) {
|
|
166
|
+
const root = resource;
|
|
167
|
+
// Per-scope: the annotation navigates the enclosing ARRAY ITEM, so the path
|
|
168
|
+
// it yields is relative to that item rather than to the resource.
|
|
169
|
+
const from = annotated["x-telo-context-from"];
|
|
170
|
+
if (typeof from === "string") {
|
|
171
|
+
const itemPath = manifestItemPath(sitePath, matchedScope);
|
|
172
|
+
return { manifest: root, path: joinPath(itemPath, from.split("/").join(".")), propertyMap: true };
|
|
173
|
+
}
|
|
174
|
+
const fromRoot = annotated["x-telo-context-from-root"];
|
|
175
|
+
if (typeof fromRoot === "string") {
|
|
176
|
+
return { manifest: root, path: fromRoot.split("/").join("."), propertyMap: false };
|
|
177
|
+
}
|
|
178
|
+
// Cross-manifest: the binding is declared by whatever this slot REFERENCES,
|
|
179
|
+
// which is the node a reader wants when a result's members do not resolve.
|
|
180
|
+
const refFrom = annotated["x-telo-context-ref-from"];
|
|
181
|
+
if (typeof refFrom === "string") {
|
|
182
|
+
const slash = refFrom.indexOf("/");
|
|
183
|
+
if (slash === -1)
|
|
184
|
+
return undefined;
|
|
185
|
+
const item = matchedScope
|
|
186
|
+
? getManifestItem(sitePath, matchedScope, root)
|
|
187
|
+
: root;
|
|
188
|
+
const ref = item[refFrom.slice(0, slash)];
|
|
189
|
+
if (!ref?.kind || !ref.name)
|
|
190
|
+
return undefined;
|
|
191
|
+
const target = this.manifests.find((m) => m.kind === ref.kind && m.metadata?.name === ref.name);
|
|
192
|
+
if (!target)
|
|
193
|
+
return undefined;
|
|
194
|
+
return { manifest: target, path: refFrom.slice(slash + 1).split("/").join("."), propertyMap: false };
|
|
195
|
+
}
|
|
196
|
+
// A kind's own declaration: the target is the `Telo.Definition` document,
|
|
197
|
+
// which is an ordinary manifest in the set.
|
|
198
|
+
const fromRefKind = annotated["x-telo-context-from-ref-kind"];
|
|
199
|
+
const first = Array.isArray(fromRefKind) ? fromRefKind[0] : fromRefKind;
|
|
200
|
+
if (typeof first === "string") {
|
|
201
|
+
const hash = first.indexOf("#");
|
|
202
|
+
if (hash <= 0)
|
|
203
|
+
return undefined;
|
|
204
|
+
const kindValue = navigateConcretePath(root, first.slice(0, hash).split("/").join("."));
|
|
205
|
+
if (typeof kindValue !== "string")
|
|
206
|
+
return undefined;
|
|
207
|
+
const canonical = this.ctx.aliases.resolveKind(kindValue) ?? kindValue;
|
|
208
|
+
const suffix = canonical.slice(canonical.indexOf(".") + 1);
|
|
209
|
+
const target = this.manifests.find((m) => (m.kind === "Telo.Definition" || m.kind === "Telo.Abstract") &&
|
|
210
|
+
m.metadata?.name === suffix);
|
|
211
|
+
if (!target)
|
|
212
|
+
return undefined;
|
|
213
|
+
return { manifest: target, path: first.slice(hash + 1), propertyMap: false };
|
|
214
|
+
}
|
|
215
|
+
// `x-telo-context-element-from` / `-collection-from` type a binding from an
|
|
216
|
+
// EXPRESSION, so there is no declaration to navigate to.
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Where the step named `stepName` is declared in `resource`, as a concrete
|
|
221
|
+
* path (`steps[2]`), or undefined when the resource declares no step body or
|
|
222
|
+
* holds no such step.
|
|
223
|
+
*
|
|
224
|
+
* For go-to-declaration on `steps.<name>.result`, which is the one CEL scope
|
|
225
|
+
* whose members ARE written somewhere in the manifest but are reached through
|
|
226
|
+
* no reference slot. Driven by the kind's own step-body annotation and the
|
|
227
|
+
* shared nesting walk, so a step inside a `try:` inside a `catch:` is found
|
|
228
|
+
* and no resource kind is named here.
|
|
229
|
+
*/
|
|
230
|
+
stepDeclarationPath(resource, stepName) {
|
|
231
|
+
const schema = this.definitionFor(resource)?.schema;
|
|
232
|
+
const props = schema?.properties;
|
|
233
|
+
if (!schema || !props)
|
|
234
|
+
return undefined;
|
|
235
|
+
for (const [fieldName, fieldSchema] of Object.entries(props)) {
|
|
236
|
+
if (!readStepSlot(fieldSchema))
|
|
237
|
+
continue;
|
|
238
|
+
const steps = resource[fieldName];
|
|
239
|
+
if (!Array.isArray(steps))
|
|
240
|
+
continue;
|
|
241
|
+
const itemSchema = resolveLocalRef(fieldSchema.items, schema);
|
|
242
|
+
let found;
|
|
243
|
+
walkStepArray(steps, itemSchema, schema, fieldName, (step, stepPath) => {
|
|
244
|
+
if (found === undefined && step.name === stepName)
|
|
245
|
+
found = stepPath;
|
|
246
|
+
});
|
|
247
|
+
if (found)
|
|
248
|
+
return found;
|
|
249
|
+
}
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
definitionFor(resource) {
|
|
253
|
+
const { defs, aliases } = this.ctx;
|
|
254
|
+
const canonical = aliases.resolveKind(resource.kind);
|
|
255
|
+
return defs.resolve(resource.kind) ?? (canonical ? defs.resolve(canonical) : undefined);
|
|
256
|
+
}
|
|
257
|
+
/** The `x-telo-context` region this path falls in, matched exactly as the
|
|
258
|
+
* manifest visitor matches it for an expression it walked onto. */
|
|
259
|
+
matchContext(resource, path) {
|
|
260
|
+
const schema = this.definitionFor(resource)?.schema;
|
|
261
|
+
if (!schema)
|
|
262
|
+
return {};
|
|
263
|
+
for (const ctx of extractContextsFromSchema(schema)) {
|
|
264
|
+
if (pathMatchesScope(path, ctx.scope)) {
|
|
265
|
+
return { contextSchema: ctx.schema, matchedScope: ctx.scope };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return {};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **What is in scope for CEL at one expression site.**
|
|
3
|
+
*
|
|
4
|
+
* A CEL expression is typed against an environment and a resolved context
|
|
5
|
+
* schema that depend on WHERE it sits: the kind's step body contributes
|
|
6
|
+
* `steps.<name>.result`, an error-bearing branch contributes `error`, a
|
|
7
|
+
* `x-telo-bindings-from` field contributes its named bindings, an
|
|
8
|
+
* `x-telo-context` region contributes its own scope resolved against the
|
|
9
|
+
* enclosing array item, and the kernel globals are merged in per declaring
|
|
10
|
+
* module. That assembly used to live inline in the analysis pass, built for one
|
|
11
|
+
* `engine.analyze` call and discarded — so nothing outside the pass could ask
|
|
12
|
+
* what a cursor sees.
|
|
13
|
+
*
|
|
14
|
+
* It is a QUERY here, and the pass is one of its callers. The other is the IDE:
|
|
15
|
+
* completion, hover and signature help must offer exactly the names
|
|
16
|
+
* `telo check` accepts, and two implementations of an open, growing scope rule
|
|
17
|
+
* cannot be held in agreement by tests.
|
|
18
|
+
*
|
|
19
|
+
* Nothing here reports a diagnostic. The pass keeps every check it ever had;
|
|
20
|
+
* what moved is the answer both halves need first.
|
|
21
|
+
*/
|
|
22
|
+
import type { ResourceDefinition, ResourceManifest } from "@telorun/sdk";
|
|
23
|
+
import type { Environment } from "@marcbachmann/cel-js";
|
|
24
|
+
import { AliasResolver, type ModuleScopes } from "./alias-resolver.js";
|
|
25
|
+
import { DefinitionRegistry } from "./definition-registry.js";
|
|
26
|
+
import { type KernelGlobalsIndex } from "./kernel-globals.js";
|
|
27
|
+
/** Returns a "resolver-facing" view of the manifest where the fields used as
|
|
28
|
+
* navigation roots by Telo.Definition's `x-telo-context-from-root` annotations
|
|
29
|
+
* have been pre-augmented:
|
|
30
|
+
* - `schema` → augmented `self` schema (synthetic `name`/`kind`/metadata).
|
|
31
|
+
* - `inputType` → resolved through the shared contract resolver, so
|
|
32
|
+
* `x-telo-context-from-root: inputType` substitutes the
|
|
33
|
+
* real signature. Without it the annotation would replace
|
|
34
|
+
* the node verbatim with the inline `{kind, schema}` wrapper
|
|
35
|
+
* the standard library writes everywhere, typing `inputs` as
|
|
36
|
+
* `{kind, schema}` instead of the declared properties.
|
|
37
|
+
*
|
|
38
|
+
* For non-definition manifests the original object is returned. */
|
|
39
|
+
export declare function manifestRootForResolver(m: Record<string, any>, defs: DefinitionRegistry, aliases: AliasResolver, allManifests: Record<string, any>[], scopes: ModuleScopes): Record<string, any>;
|
|
40
|
+
/**
|
|
41
|
+
* Build a `steps` context schema for a kind's step body.
|
|
42
|
+
* Walks each step in the manifest array, resolves the invoked resource's output
|
|
43
|
+
* contract, and builds `steps.<name>.result` context entries.
|
|
44
|
+
*
|
|
45
|
+
* Resolution is the shared {@link resolveContract} — the invoked resource
|
|
46
|
+
* manifest's own declaration, then the kind's, resolved to the nearest
|
|
47
|
+
* declaration along `extends`, then permissive. Sharing it with the kernel is
|
|
48
|
+
* what stops `telo check` from typing `steps.X.result` against one contract
|
|
49
|
+
* while dispatch validates against another.
|
|
50
|
+
*
|
|
51
|
+
* The kind layer is what makes `x-telo-stream` properties on definitions
|
|
52
|
+
* actually govern step-result chain validation — without it, the validator falls
|
|
53
|
+
* back to permissive and the stream-opacity rule never fires.
|
|
54
|
+
*
|
|
55
|
+
* Recursion into nested step arrays is annotation-driven via
|
|
56
|
+
* `x-telo-topology-role`. The analyzer recognises three role values:
|
|
57
|
+
* - `branch` — value is an array of steps (e.g. then / else / do / catch).
|
|
58
|
+
* - `branch-list`— value is an array of objects each carrying further roled
|
|
59
|
+
* sub-properties (e.g. elseif: [{ if, then }]).
|
|
60
|
+
* - `case-map` — value is an object whose values are step arrays (e.g. cases).
|
|
61
|
+
* No specific Run.Sequence field name is hardcoded; any kind that uses
|
|
62
|
+
* a step body and tags its branch fields with these roles works.
|
|
63
|
+
*/
|
|
64
|
+
export declare function buildStepContextSchema(manifest: Record<string, any>, defSchema: Record<string, any>, allManifests: Record<string, any>[], defs: DefinitionRegistry, aliases: AliasResolver, scopes: ModuleScopes): Record<string, any> | undefined;
|
|
65
|
+
export declare function collectErrorContextScopes(defSchema: Record<string, any> | undefined): Map<string, Record<string, any>>;
|
|
66
|
+
/**
|
|
67
|
+
* Return the error-context schema for a CEL `path` when the path lies within
|
|
68
|
+
* (any depth under) one of the error-bearing fields, else undefined. A path is
|
|
69
|
+
* "within" field `f` when it contains a segment `f[<index>]`. When multiple
|
|
70
|
+
* error-bearing fields match (e.g. a `finally` nested inside a `catch`), the
|
|
71
|
+
* deepest — the one whose segment appears latest in the path — wins, so the
|
|
72
|
+
* innermost branch's schema governs.
|
|
73
|
+
*/
|
|
74
|
+
export declare function errorContextForPath(path: string, scopes: Map<string, Record<string, any>>): Record<string, any> | undefined;
|
|
75
|
+
/** Add a kind's named bindings to a resolved context, when the context declares
|
|
76
|
+
* a bindings region. They go UNDER the context's own properties: a scope
|
|
77
|
+
* variable wins over a same-named binding at runtime, so static typing has to
|
|
78
|
+
* agree (the collision itself is `BINDING_NAME_RESERVED`). */
|
|
79
|
+
export declare function withBindingNames(contextSchema: Record<string, any>, resource: Record<string, any>): Record<string, any>;
|
|
80
|
+
/**
|
|
81
|
+
* What one CEL expression is typed against.
|
|
82
|
+
*
|
|
83
|
+
* Both halves, never a flattened name list: the environment answers "what
|
|
84
|
+
* names exist and what type does this expression have", the context schema
|
|
85
|
+
* answers "what shape does that name carry" — which is what a hover tooltip and
|
|
86
|
+
* a member completion are made of, and what a name list throws away.
|
|
87
|
+
*/
|
|
88
|
+
export interface CelScope {
|
|
89
|
+
/** The environment typed for this expression's path. */
|
|
90
|
+
env: Environment;
|
|
91
|
+
/** The resolved `x-telo-context` schema merged with the kernel globals, or
|
|
92
|
+
* null when no context applied (the environment alone types the site). */
|
|
93
|
+
contextSchema: Record<string, any> | null;
|
|
94
|
+
}
|
|
95
|
+
/** The analyzer state a scope is resolved against — everything a manifest set
|
|
96
|
+
* contributes, gathered once per analysis. */
|
|
97
|
+
export interface CelScopeInputs {
|
|
98
|
+
/** The base (untyped) CEL environment. */
|
|
99
|
+
celEnv: Environment;
|
|
100
|
+
defs: DefinitionRegistry;
|
|
101
|
+
aliases: AliasResolver;
|
|
102
|
+
scopes: ModuleScopes;
|
|
103
|
+
allManifests: ResourceManifest[];
|
|
104
|
+
kernelGlobals: KernelGlobalsIndex;
|
|
105
|
+
/** The module doc carrying the Application-only `ports` namespace. */
|
|
106
|
+
moduleManifest?: ResourceManifest;
|
|
107
|
+
/** The observed-state-only context, used where no `x-telo-context` matched. */
|
|
108
|
+
observedStateContext: Record<string, any> | null;
|
|
109
|
+
}
|
|
110
|
+
/** One CEL site, as the manifest visitor reports it. Structural on purpose —
|
|
111
|
+
* the resolver takes the fields it reads, not the visitor's event type, so a
|
|
112
|
+
* caller that located a site some other way (a cursor in an editor buffer) can
|
|
113
|
+
* ask the same question. */
|
|
114
|
+
export interface CelSiteRef {
|
|
115
|
+
source: ResourceManifest;
|
|
116
|
+
path: string;
|
|
117
|
+
contextSchema?: Record<string, any>;
|
|
118
|
+
matchedScope?: string;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The CEL scope rule, applied per resource and then per expression.
|
|
122
|
+
*
|
|
123
|
+
* Stateful across a resource because the per-resource half — the step context,
|
|
124
|
+
* the error-bearing regions, the invocation context — is derived from the kind's
|
|
125
|
+
* schema once and read by every expression in that resource. `enterResource`
|
|
126
|
+
* establishes it; `scopeFor` answers for one path.
|
|
127
|
+
*/
|
|
128
|
+
export declare class CelScopeResolver {
|
|
129
|
+
private readonly inputs;
|
|
130
|
+
private readonly typedEnvByManifest;
|
|
131
|
+
/** Per-resource state, replaced at each `enterResource`. */
|
|
132
|
+
private stepContext;
|
|
133
|
+
private invocationContext;
|
|
134
|
+
private errorScopes;
|
|
135
|
+
constructor(inputs: CelScopeInputs);
|
|
136
|
+
/** The `steps` context schema for the current resource, or undefined when its
|
|
137
|
+
* kind declares no step body. Exposed because the step-inputs check needs the
|
|
138
|
+
* same schema this resource's expressions are typed against — recomputing it
|
|
139
|
+
* there is how the two would come to disagree. */
|
|
140
|
+
get stepContextSchema(): Record<string, any> | undefined;
|
|
141
|
+
/** The error-bearing regions the current resource's kind declares. */
|
|
142
|
+
get errorContextScopes(): ReadonlyMap<string, Record<string, any>>;
|
|
143
|
+
/** The resource-wide invocation context, when this resource is an inline
|
|
144
|
+
* declaration that carries one. Read by the "CEL in a non-eval field" check:
|
|
145
|
+
* such a resource's CEL is evaluated by the enclosing kind, not by an
|
|
146
|
+
* `x-telo-eval` annotation of its own. */
|
|
147
|
+
get invocationContextSchema(): Record<string, any> | undefined;
|
|
148
|
+
/** Establish the per-resource half of the scope. */
|
|
149
|
+
enterResource(m: ResourceManifest, definition: ResourceDefinition | undefined): void;
|
|
150
|
+
/**
|
|
151
|
+
* What the expression at `site` is typed against.
|
|
152
|
+
*
|
|
153
|
+
* The environment is cached per manifest when no context applied, which is
|
|
154
|
+
* most expressions: it then depends only on the manifest, so rebuilding it per
|
|
155
|
+
* expression is pure waste — a clone plus a re-registration of every variable,
|
|
156
|
+
* on every keystroke in the IDE. A matched context makes it path-specific (its
|
|
157
|
+
* schema is resolved against the enclosing array item), so those build fresh
|
|
158
|
+
* rather than risk one item's types leaking into another's.
|
|
159
|
+
*/
|
|
160
|
+
scopeFor(site: CelSiteRef): CelScope;
|
|
161
|
+
/** The context schema in force at `site`: the matched `x-telo-context` (or the
|
|
162
|
+
* resource-wide invocation context), plus the step and error regions this
|
|
163
|
+
* path falls in, resolved and merged with the kernel globals. */
|
|
164
|
+
private resolveContextFor;
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=cel-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cel-scope.d.ts","sourceRoot":"","sources":["../src/cel-scope.ts"],"names":[],"mappings":"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"}
|