@telorun/analyzer 0.72.0 → 0.74.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 +76 -20
- package/dist/artifact-axes.d.ts +14 -0
- package/dist/artifact-axes.d.ts.map +1 -0
- package/dist/artifact-axes.js +12 -0
- package/dist/artifact-layer-index.d.ts +2 -2
- package/dist/artifact-layer-index.d.ts.map +1 -1
- package/dist/artifact-layer-index.js +9 -4
- package/dist/artifact-selector.d.ts +47 -21
- package/dist/artifact-selector.d.ts.map +1 -1
- package/dist/artifact-selector.js +85 -16
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +144 -113
- package/dist/call-graph.d.ts +12 -1
- package/dist/call-graph.d.ts.map +1 -1
- package/dist/call-graph.js +22 -0
- package/dist/catch-scope.js +1 -1
- package/dist/cel-scope-query.d.ts.map +1 -1
- package/dist/cel-scope-query.js +32 -11
- package/dist/cel-scope.d.ts.map +1 -1
- package/dist/cel-scope.js +1 -0
- package/dist/eval-paths.d.ts +36 -5
- package/dist/eval-paths.d.ts.map +1 -1
- package/dist/eval-paths.js +47 -6
- package/dist/extends-resolution.d.ts +12 -0
- package/dist/extends-resolution.d.ts.map +1 -1
- package/dist/extends-resolution.js +23 -0
- package/dist/forwarded-declaration.d.ts +26 -0
- package/dist/forwarded-declaration.d.ts.map +1 -0
- package/dist/forwarded-declaration.js +31 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/kernel-globals.d.ts.map +1 -1
- package/dist/kernel-globals.js +21 -5
- package/dist/module-alias-scope.d.ts +11 -0
- package/dist/module-alias-scope.d.ts.map +1 -1
- package/dist/module-alias-scope.js +10 -0
- package/dist/module-file-claims.d.ts +17 -1
- package/dist/module-file-claims.d.ts.map +1 -1
- package/dist/module-file-claims.js +38 -3
- package/dist/module-named-files.d.ts +72 -0
- package/dist/module-named-files.d.ts.map +1 -0
- package/dist/module-named-files.js +115 -0
- package/dist/native-entries.d.ts +83 -0
- package/dist/native-entries.d.ts.map +1 -0
- package/dist/native-entries.js +169 -0
- package/dist/nearest-name.d.ts +22 -0
- package/dist/nearest-name.d.ts.map +1 -0
- package/dist/nearest-name.js +43 -0
- package/dist/normalize-inline-resources.d.ts +41 -13
- package/dist/normalize-inline-resources.d.ts.map +1 -1
- package/dist/normalize-inline-resources.js +236 -54
- package/dist/reference-field-map.d.ts +59 -1
- package/dist/reference-field-map.d.ts.map +1 -1
- package/dist/reference-field-map.js +200 -34
- package/dist/release/destinations.d.ts +59 -0
- package/dist/release/destinations.d.ts.map +1 -0
- package/dist/release/destinations.js +75 -0
- package/dist/release/index.d.ts +6 -2
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +3 -1
- package/dist/release/ledger.d.ts +20 -11
- package/dist/release/ledger.d.ts.map +1 -1
- package/dist/release/ledger.js +43 -14
- package/dist/release/payload-digest.d.ts +2 -1
- package/dist/release/payload-digest.d.ts.map +1 -1
- package/dist/release/payload-digest.js +2 -1
- package/dist/release/release-plan.d.ts +3 -2
- package/dist/release/release-plan.d.ts.map +1 -1
- package/dist/release/release-plan.js +12 -19
- package/dist/release/workspace-config.d.ts +114 -34
- package/dist/release/workspace-config.d.ts.map +1 -1
- package/dist/release/workspace-config.js +322 -38
- package/dist/release/workspace-schema.d.ts +41 -0
- package/dist/release/workspace-schema.d.ts.map +1 -0
- package/dist/release/workspace-schema.js +77 -0
- package/dist/resolve-ref-sentinels.d.ts.map +1 -1
- package/dist/resolve-ref-sentinels.js +8 -5
- package/dist/resolve-throws-union.d.ts +25 -4
- package/dist/resolve-throws-union.d.ts.map +1 -1
- package/dist/resolve-throws-union.js +62 -32
- package/dist/resolve-zone-requirements.d.ts.map +1 -1
- package/dist/resolve-zone-requirements.js +2 -1
- package/dist/rewrite-synthetic-origins.d.ts +6 -1
- package/dist/rewrite-synthetic-origins.d.ts.map +1 -1
- package/dist/rewrite-synthetic-origins.js +107 -13
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +18 -5
- package/dist/schema-error-report.d.ts +7 -0
- package/dist/schema-error-report.d.ts.map +1 -1
- package/dist/schema-error-report.js +9 -4
- package/dist/schema-walk.d.ts +47 -13
- package/dist/schema-walk.d.ts.map +1 -1
- package/dist/schema-walk.js +150 -30
- package/dist/scope-declarations.d.ts +59 -0
- package/dist/scope-declarations.d.ts.map +1 -0
- package/dist/scope-declarations.js +45 -0
- package/dist/source-entries.d.ts +119 -0
- package/dist/source-entries.d.ts.map +1 -0
- package/dist/source-entries.js +349 -0
- package/dist/step-bodies.d.ts +24 -0
- package/dist/step-bodies.d.ts.map +1 -0
- package/dist/step-bodies.js +28 -0
- package/dist/telo-version.d.ts +1 -1
- package/dist/telo-version.js +1 -1
- package/dist/template-body.d.ts +10 -0
- package/dist/template-body.d.ts.map +1 -1
- package/dist/template-body.js +25 -0
- package/dist/validate-cel-context.d.ts +23 -8
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +51 -18
- package/dist/validate-exports.d.ts +28 -0
- package/dist/validate-exports.d.ts.map +1 -0
- package/dist/validate-exports.js +146 -0
- package/dist/validate-extends.d.ts.map +1 -1
- package/dist/validate-extends.js +37 -1
- package/dist/validate-module-artifact.d.ts +4 -2
- package/dist/validate-module-artifact.d.ts.map +1 -1
- package/dist/validate-module-artifact.js +43 -4
- package/dist/validate-native-entries.d.ts +15 -0
- package/dist/validate-native-entries.d.ts.map +1 -0
- package/dist/validate-native-entries.js +103 -0
- package/dist/validate-observed-state.d.ts +7 -1
- package/dist/validate-observed-state.d.ts.map +1 -1
- package/dist/validate-observed-state.js +24 -6
- package/dist/validate-provider-coherence.d.ts +4 -8
- package/dist/validate-provider-coherence.d.ts.map +1 -1
- package/dist/validate-provider-coherence.js +12 -123
- package/dist/validate-reference-forms.d.ts.map +1 -1
- package/dist/validate-reference-forms.js +2 -2
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +152 -65
- package/dist/validate-scope-reach.d.ts +31 -0
- package/dist/validate-scope-reach.d.ts.map +1 -0
- package/dist/validate-scope-reach.js +180 -0
- package/dist/validate-source-entries.d.ts +16 -0
- package/dist/validate-source-entries.d.ts.map +1 -0
- package/dist/validate-source-entries.js +90 -0
- package/dist/validate-template-body.d.ts +36 -0
- package/dist/validate-template-body.d.ts.map +1 -0
- package/dist/validate-template-body.js +246 -0
- package/dist/validate-throws-coverage.d.ts.map +1 -1
- package/dist/validate-throws-coverage.js +21 -39
- package/dist/value-type-keyword.d.ts +22 -5
- package/dist/value-type-keyword.d.ts.map +1 -1
- package/dist/value-type-keyword.js +16 -7
- package/package.json +4 -3
- package/src/analyzer.ts +97 -20
- package/src/artifact-axes.ts +24 -0
- package/src/artifact-layer-index.ts +9 -6
- package/src/artifact-selector.ts +110 -35
- package/src/builtins.ts +160 -113
- package/src/call-graph.ts +30 -0
- package/src/catch-scope.ts +1 -1
- package/src/cel-scope-query.ts +28 -9
- package/src/cel-scope.ts +1 -0
- package/src/eval-paths.ts +53 -6
- package/src/extends-resolution.ts +24 -0
- package/src/forwarded-declaration.ts +32 -0
- package/src/index.ts +37 -1
- package/src/kernel-globals.ts +28 -5
- package/src/migrations/CLAUDE.md +35 -0
- package/src/module-alias-scope.ts +17 -0
- package/src/module-file-claims.ts +63 -4
- package/src/module-named-files.ts +162 -0
- package/src/native-entries.ts +227 -0
- package/src/nearest-name.ts +47 -0
- package/src/normalize-inline-resources.ts +326 -70
- package/src/reference-field-map.ts +257 -52
- package/src/release/CLAUDE.md +32 -0
- package/src/release/destinations.ts +105 -0
- package/src/release/index.ts +33 -2
- package/src/release/ledger.ts +51 -21
- package/src/release/payload-digest.ts +2 -1
- package/src/release/release-plan.ts +16 -26
- package/src/release/workspace-config.ts +483 -47
- package/src/release/workspace-schema.ts +99 -0
- package/src/resolve-ref-sentinels.ts +7 -6
- package/src/resolve-throws-union.ts +87 -32
- package/src/resolve-zone-requirements.ts +2 -1
- package/src/rewrite-synthetic-origins.ts +107 -16
- package/src/schema-compat.ts +18 -4
- package/src/schema-error-report.ts +16 -4
- package/src/schema-walk.ts +162 -34
- package/src/scope-declarations.ts +98 -0
- package/src/source-entries.ts +502 -0
- package/src/step-bodies.ts +55 -0
- package/src/telo-version.ts +1 -1
- package/src/template-body.ts +25 -0
- package/src/validate-cel-context.ts +60 -18
- package/src/validate-exports.ts +185 -0
- package/src/validate-extends.ts +43 -0
- package/src/validate-module-artifact.ts +46 -3
- package/src/validate-native-entries.ts +147 -0
- package/src/validate-observed-state.ts +36 -7
- package/src/validate-provider-coherence.ts +17 -127
- package/src/validate-reference-forms.ts +2 -3
- package/src/validate-references.ts +151 -66
- package/src/validate-scope-reach.ts +213 -0
- package/src/validate-source-entries.ts +120 -0
- package/src/validate-template-body.ts +274 -0
- package/src/validate-throws-coverage.ts +24 -31
- package/src/value-type-keyword.ts +35 -9
- package/dist/validate-template-dispatch.d.ts +0 -27
- package/dist/validate-template-dispatch.d.ts.map +0 -1
- package/dist/validate-template-dispatch.js +0 -95
- package/src/validate-template-dispatch.ts +0 -99
package/src/artifact-selector.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* controller candidate is chosen by, and the key a controller layer of a module
|
|
4
4
|
* artifact is stored under.
|
|
5
5
|
*
|
|
6
|
-
* A selector is `format` plus the optional platform axes
|
|
6
|
+
* A selector is `format` plus the optional platform axes, whose vocabulary is
|
|
7
|
+
* data (`analyzer/artifact-axes/axes.json`, generated into `PLATFORM_AXES`).
|
|
7
8
|
* Matching is one rule, applied per axis: an axis the selector omits accepts
|
|
8
9
|
* anything, an axis it states must be equal. That is what lets a `js` controller
|
|
9
10
|
* be platform-neutral and a `napi` controller be pinned to one triple, with no
|
|
@@ -25,11 +26,20 @@
|
|
|
25
26
|
* published into OCI descriptors.
|
|
26
27
|
*/
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
* carry a selector; `assets` and `common` are singletons and carry none. */
|
|
30
|
-
export type LayerRole = "controller" | "library" | "assets" | "common";
|
|
29
|
+
import { AXIS_VALUE_FORMS, PLATFORM_AXES, type PlatformAxis } from "./artifact-axes.js";
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
/** The role a layer plays in a module artifact. `controller`, `library` and
|
|
32
|
+
* `native` layers carry a selector; `assets` and `common` are singletons and
|
|
33
|
+
* carry none. */
|
|
34
|
+
export type LayerRole = "controller" | "library" | "native" | "assets" | "common";
|
|
35
|
+
|
|
36
|
+
export const LAYER_ROLES: readonly LayerRole[] = [
|
|
37
|
+
"controller",
|
|
38
|
+
"library",
|
|
39
|
+
"native",
|
|
40
|
+
"assets",
|
|
41
|
+
"common",
|
|
42
|
+
];
|
|
33
43
|
|
|
34
44
|
export function isLayerRole(value: unknown): value is LayerRole {
|
|
35
45
|
return typeof value === "string" && (LAYER_ROLES as readonly string[]).includes(value);
|
|
@@ -42,33 +52,28 @@ export function isLayerRole(value: unknown): value is LayerRole {
|
|
|
42
52
|
* A singleton would be wrong the moment a second runtime ships. */
|
|
43
53
|
export const CODE_LAYER_ROLES: readonly LayerRole[] = ["controller", "library"];
|
|
44
54
|
|
|
55
|
+
/** Every role keyed by a selector, one layer per selector: the code roles, plus
|
|
56
|
+
* `native` — a platform-specific file the runtime does not import as code,
|
|
57
|
+
* declared in the module doc's `native:` block. */
|
|
58
|
+
const SELECTOR_LAYER_ROLES: readonly LayerRole[] = [...CODE_LAYER_ROLES, "native"];
|
|
59
|
+
|
|
45
60
|
export function roleCarriesSelector(role: LayerRole): boolean {
|
|
46
|
-
return (
|
|
61
|
+
return (SELECTOR_LAYER_ROLES as readonly string[]).includes(role);
|
|
47
62
|
}
|
|
48
63
|
|
|
49
|
-
|
|
50
|
-
* new architectures appear without a Telo release — only of axis names. */
|
|
51
|
-
export const PLATFORM_AXES = ["os", "arch", "libc"] as const;
|
|
64
|
+
export { PLATFORM_AXES, type PlatformAxis };
|
|
52
65
|
|
|
53
|
-
export
|
|
54
|
-
|
|
55
|
-
export interface ArtifactSelector {
|
|
66
|
+
export interface ArtifactSelector extends Partial<Record<PlatformAxis, string>> {
|
|
56
67
|
/** Bundled controller format: the PURL name segment (`js`, `napi`, `wasm`, …). */
|
|
57
68
|
format: string;
|
|
58
|
-
os?: string;
|
|
59
|
-
arch?: string;
|
|
60
|
-
libc?: string;
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
/** What a selector is matched against: the host the kernel runs on, or the
|
|
64
|
-
* target `telo install
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
export interface PlatformTarget {
|
|
72
|
+
* target `telo install` is warming a cache for. An axis left undetermined (a
|
|
73
|
+
* host whose libc cannot be detected) matches no selector that constrains it —
|
|
74
|
+
* refusing to load is the safe direction for a native binary. */
|
|
75
|
+
export interface PlatformTarget extends Partial<Record<PlatformAxis, string>> {
|
|
68
76
|
format?: string;
|
|
69
|
-
os?: string;
|
|
70
|
-
arch?: string;
|
|
71
|
-
libc?: string;
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
export class ArtifactSelectorError extends Error {
|
|
@@ -84,7 +89,9 @@ export class ArtifactSelectorError extends Error {
|
|
|
84
89
|
* platform written two ways is one layer rather than two. */
|
|
85
90
|
const TOKEN = /^[a-z0-9][a-z0-9_.-]*$/;
|
|
86
91
|
|
|
87
|
-
|
|
92
|
+
/** Validate and normalize one selector value: the shared token grammar, plus the
|
|
93
|
+
* axis's own value form where the vocabulary declares one. */
|
|
94
|
+
export function normalizeAxisValue(axis: string, raw: unknown, describe: string): string {
|
|
88
95
|
if (typeof raw !== "string") {
|
|
89
96
|
throw new ArtifactSelectorError(
|
|
90
97
|
`${describe}: ${axis} must be a string, got ${raw === null ? "null" : typeof raw}.`,
|
|
@@ -97,6 +104,13 @@ function normalizeToken(axis: string, raw: unknown, describe: string): string {
|
|
|
97
104
|
`Use lowercase letters, digits, '.', '-' or '_', starting with a letter or digit.`,
|
|
98
105
|
);
|
|
99
106
|
}
|
|
107
|
+
const valueForm = AXIS_VALUE_FORMS[axis as PlatformAxis];
|
|
108
|
+
if (valueForm && !valueForm.pattern.test(value)) {
|
|
109
|
+
throw new ArtifactSelectorError(
|
|
110
|
+
`${describe}: ${axis} value '${raw}' must have the form ${valueForm.form}, ` +
|
|
111
|
+
`e.g. ${valueForm.examples.map((e) => `'${e}'`).join(" or ")}.`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
100
114
|
return value;
|
|
101
115
|
}
|
|
102
116
|
|
|
@@ -111,35 +125,38 @@ export function selectorFromQualifiers(
|
|
|
111
125
|
describe = "controller selector",
|
|
112
126
|
): ArtifactSelector {
|
|
113
127
|
const selector: ArtifactSelector = {
|
|
114
|
-
format:
|
|
128
|
+
format: normalizeAxisValue("format", format, describe),
|
|
115
129
|
};
|
|
116
130
|
for (const axis of PLATFORM_AXES) {
|
|
117
131
|
const raw = qualifiers?.[axis];
|
|
118
132
|
if (raw === undefined || raw === "") continue;
|
|
119
|
-
selector[axis] =
|
|
133
|
+
selector[axis] = normalizeAxisValue(axis, raw, describe);
|
|
120
134
|
}
|
|
121
135
|
return selector;
|
|
122
136
|
}
|
|
123
137
|
|
|
124
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* Validate and normalize a selector read off a published layer index.
|
|
140
|
+
*
|
|
141
|
+
* Returns undefined when the selector carries an axis this runtime does not
|
|
142
|
+
* know: the layer is for a newer runtime, and the caller skips it whole. The
|
|
143
|
+
* unknown axis is never dropped — two layers differing only in it would then
|
|
144
|
+
* claim one address. The known axes are still validated, since their grammar
|
|
145
|
+
* does not change with the axis set.
|
|
146
|
+
*/
|
|
125
147
|
export function normalizeSelector(
|
|
126
148
|
value: unknown,
|
|
127
149
|
describe = "layer selector",
|
|
128
|
-
): ArtifactSelector {
|
|
150
|
+
): ArtifactSelector | undefined {
|
|
129
151
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
130
152
|
throw new ArtifactSelectorError(`${describe}: expected an object of selector axes.`);
|
|
131
153
|
}
|
|
132
154
|
const record = value as Record<string, unknown>;
|
|
133
|
-
const
|
|
155
|
+
const selector = selectorFromQualifiers(record.format, record, describe);
|
|
156
|
+
const carriesUnknownAxis = Object.keys(record).some(
|
|
134
157
|
(k) => k !== "format" && !(PLATFORM_AXES as readonly string[]).includes(k),
|
|
135
158
|
);
|
|
136
|
-
|
|
137
|
-
throw new ArtifactSelectorError(
|
|
138
|
-
`${describe}: unknown selector ${unknown.length === 1 ? "axis" : "axes"} ` +
|
|
139
|
-
`${unknown.map((k) => `'${k}'`).join(", ")}. Known axes: format, ${PLATFORM_AXES.join(", ")}.`,
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
return selectorFromQualifiers(record.format, record, describe);
|
|
159
|
+
return carriesUnknownAxis ? undefined : selector;
|
|
143
160
|
}
|
|
144
161
|
|
|
145
162
|
/**
|
|
@@ -180,3 +197,61 @@ export function selectorMatches(selector: ArtifactSelector, target: PlatformTarg
|
|
|
180
197
|
}
|
|
181
198
|
return true;
|
|
182
199
|
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The axes `selector` constrains and `target` leaves undetermined, when those
|
|
203
|
+
* alone keep the two from matching — what a warm skips for want of a value rather
|
|
204
|
+
* than because the selector is for another platform. `undefined` when the
|
|
205
|
+
* selector matches, or differs on a determined axis.
|
|
206
|
+
*/
|
|
207
|
+
export function undeterminedAxesBlockingMatch(
|
|
208
|
+
selector: ArtifactSelector,
|
|
209
|
+
target: PlatformTarget,
|
|
210
|
+
): PlatformAxis[] | undefined {
|
|
211
|
+
if (target.format !== undefined && selector.format !== target.format) return undefined;
|
|
212
|
+
const axes: PlatformAxis[] = [];
|
|
213
|
+
for (const axis of PLATFORM_AXES) {
|
|
214
|
+
const constraint = selector[axis];
|
|
215
|
+
if (constraint === undefined) continue;
|
|
216
|
+
if (target[axis] === undefined) axes.push(axis);
|
|
217
|
+
else if (target[axis] !== constraint) return undefined;
|
|
218
|
+
}
|
|
219
|
+
return axes.length > 0 ? axes : undefined;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** A selector that no host can match as its author meant it. */
|
|
223
|
+
export interface SelectorContradiction {
|
|
224
|
+
/** Suffix of the diagnostic code; each surface prefixes its own. */
|
|
225
|
+
readonly rule: "NAPI_ABI_FORBIDDEN" | "LIBC_OFF_LINUX";
|
|
226
|
+
readonly axis: PlatformAxis;
|
|
227
|
+
readonly detail: string;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* The combinations of axes the grammar accepts and no host can mean: an N-API
|
|
232
|
+
* addon stating a runtime ABI, and a libc on an os that has none. Shared by every
|
|
233
|
+
* surface that authors a selector — `native:` entries, controller candidates and
|
|
234
|
+
* `exports.code:` entries — so each reports the same rule under its own prefix.
|
|
235
|
+
*/
|
|
236
|
+
export function selectorContradictions(selector: ArtifactSelector): SelectorContradiction[] {
|
|
237
|
+
const out: SelectorContradiction[] = [];
|
|
238
|
+
if (selector.format === "napi" && selector.abi !== undefined) {
|
|
239
|
+
out.push({
|
|
240
|
+
rule: "NAPI_ABI_FORBIDDEN",
|
|
241
|
+
axis: "abi",
|
|
242
|
+
detail:
|
|
243
|
+
`an N-API addon is ABI-stable across runtime releases and states no abi — remove ` +
|
|
244
|
+
`abi ${selector.abi}, which would keep it from loading anywhere else.`,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
if (selector.libc !== undefined && selector.os !== undefined && selector.os !== "linux") {
|
|
248
|
+
out.push({
|
|
249
|
+
rule: "LIBC_OFF_LINUX",
|
|
250
|
+
axis: "libc",
|
|
251
|
+
detail:
|
|
252
|
+
`libc is only determined on linux, so a selector for os '${selector.os}' that states ` +
|
|
253
|
+
`libc ${selector.libc} could never match a host. Remove libc.`,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return out;
|
|
257
|
+
}
|
package/src/builtins.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PLATFORM_AXES } from "./artifact-axes.js";
|
|
1
2
|
import { manifestFragment, manifestFragmentRef, withSchemaFragments } from "./manifest-schemas.js";
|
|
2
3
|
|
|
3
4
|
/** A slot holding author-written JSON Schema. Localized and hoisted by
|
|
@@ -23,6 +24,62 @@ const observedStateSlot = {
|
|
|
23
24
|
"What a resource of this kind reports while running, published at `resources.<name>.status.<field>`.",
|
|
24
25
|
$ref: manifestFragmentRef("JsonSchema7"),
|
|
25
26
|
};
|
|
27
|
+
|
|
28
|
+
/** The `self`-only CEL scope a dispatch slot's name template is written in. */
|
|
29
|
+
const dispatchSelfContext = {
|
|
30
|
+
type: "object",
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
properties: { self: { "x-telo-context-from-root": "schema" } },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A `Telo.Definition` dispatch slot: which `resources:` entry receives the call.
|
|
37
|
+
*
|
|
38
|
+
* Three shapes, and the union is what keeps the slot CONSTRAINED for manifests
|
|
39
|
+
* no pass walks — `validate-template-body` resolves the target and its
|
|
40
|
+
* capability, but it is entry-scoped, so a dependency's definition would
|
|
41
|
+
* otherwise have nothing checking this slot at all. An array or a number fails
|
|
42
|
+
* every branch here, which is the floor AJV used to provide.
|
|
43
|
+
*
|
|
44
|
+
* `anyOf`, never `oneOf`: a `!ref` sentinel is an object, so it would match both
|
|
45
|
+
* the sentinel branch and any permissive object branch and then fail for
|
|
46
|
+
* matching twice. That is also how the sentinel used to pass — the legacy object
|
|
47
|
+
* branch required nothing, so it accepted one by accident; here it is a branch
|
|
48
|
+
* of its own that says so.
|
|
49
|
+
*/
|
|
50
|
+
function dispatchSlot(title: string, description: string): Record<string, unknown> {
|
|
51
|
+
return {
|
|
52
|
+
title,
|
|
53
|
+
description,
|
|
54
|
+
anyOf: [
|
|
55
|
+
// `!ref <entry>` — the spelling to write.
|
|
56
|
+
{
|
|
57
|
+
type: "object",
|
|
58
|
+
required: ["__tagged", "engine", "source"],
|
|
59
|
+
properties: {
|
|
60
|
+
__tagged: { const: true },
|
|
61
|
+
engine: { const: "ref" },
|
|
62
|
+
source: { type: "string", minLength: 1 },
|
|
63
|
+
},
|
|
64
|
+
additionalProperties: false,
|
|
65
|
+
},
|
|
66
|
+
// Legacy: a name template expanded against `self`. Read forever, because
|
|
67
|
+
// published artifacts carry it; `DEPRECATED_TEMPLATE_DISPATCH_FORM` is
|
|
68
|
+
// what moves an author off it.
|
|
69
|
+
{ type: "string", "x-telo-context": dispatchSelfContext },
|
|
70
|
+
// Legacy: `{ kind?, name }`, where `name` is that same template.
|
|
71
|
+
{
|
|
72
|
+
type: "object",
|
|
73
|
+
required: ["name"],
|
|
74
|
+
properties: {
|
|
75
|
+
kind: { type: "string" },
|
|
76
|
+
name: { type: "string", "x-telo-context": dispatchSelfContext },
|
|
77
|
+
},
|
|
78
|
+
additionalProperties: true,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
26
83
|
import type { ResourceDefinition } from "@telorun/sdk";
|
|
27
84
|
|
|
28
85
|
/** Descriptive provenance a module declares about itself, shared by
|
|
@@ -101,9 +158,70 @@ const LIBRARY_CANDIDATES_SCHEMA = {
|
|
|
101
158
|
format: { type: "string" },
|
|
102
159
|
path: { type: "string" },
|
|
103
160
|
source: { type: "string" },
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
161
|
+
...Object.fromEntries(PLATFORM_AXES.map((axis) => [axis, { type: "string" }])),
|
|
162
|
+
},
|
|
163
|
+
additionalProperties: false,
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/** `native:` — the module's platform-specific files, one entry per logical name
|
|
168
|
+
* per platform tuple. Closed, so a mistyped axis is a schema violation rather
|
|
169
|
+
* than a platform-neutral entry. Grammar and rules live in
|
|
170
|
+
* `analyzer/nodejs/src/native-entries.ts` and `validate-native-entries.ts`. */
|
|
171
|
+
const NATIVE_ENTRIES_SCHEMA = {
|
|
172
|
+
type: "array",
|
|
173
|
+
items: {
|
|
174
|
+
type: "object",
|
|
175
|
+
required: ["name", "format", "os", "arch", "path"],
|
|
176
|
+
properties: {
|
|
177
|
+
name: { type: "string" },
|
|
178
|
+
format: { type: "string" },
|
|
179
|
+
path: { type: "string" },
|
|
180
|
+
...Object.fromEntries(PLATFORM_AXES.map((axis) => [axis, { type: "string" }])),
|
|
181
|
+
},
|
|
182
|
+
additionalProperties: false,
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
/** `sources:` — where every staged file comes from, keyed by source name, each
|
|
187
|
+
* entry keyed by the module-relative path it produces. Closed at every level.
|
|
188
|
+
* An entry is a file (`upstream` + `member`, pinned with `sha256` +
|
|
189
|
+
* `executable`) or a link (`target`); a mix of the two, and every other rule,
|
|
190
|
+
* is reported by `analyzer/nodejs/src/validate-source-entries.ts`. `archive`
|
|
191
|
+
* names the upstream's format. A source built in the repo names its build under
|
|
192
|
+
* `build`, keyed by build system (`cargo: <crate dir>`), with the digest of its
|
|
193
|
+
* build inputs (`inputs`). */
|
|
194
|
+
const SOURCES_SCHEMA = {
|
|
195
|
+
type: "object",
|
|
196
|
+
additionalProperties: {
|
|
197
|
+
type: "object",
|
|
198
|
+
required: ["version", "url", "archive", "notices", "entries"],
|
|
199
|
+
properties: {
|
|
200
|
+
version: { type: "string" },
|
|
201
|
+
url: { type: "string" },
|
|
202
|
+
archive: { enum: ["tar.gz"] },
|
|
203
|
+
notices: { type: "array", minItems: 1, items: { type: "string" } },
|
|
204
|
+
build: {
|
|
205
|
+
type: "object",
|
|
206
|
+
required: ["cargo"],
|
|
207
|
+
properties: { cargo: { type: "string" }, inputs: { type: "string" } },
|
|
208
|
+
additionalProperties: false,
|
|
209
|
+
},
|
|
210
|
+
entries: {
|
|
211
|
+
type: "object",
|
|
212
|
+
additionalProperties: {
|
|
213
|
+
type: "object",
|
|
214
|
+
anyOf: [{ required: ["upstream", "member"] }, { required: ["target"] }],
|
|
215
|
+
properties: {
|
|
216
|
+
upstream: { type: "string" },
|
|
217
|
+
member: { type: "string" },
|
|
218
|
+
sha256: { type: "string" },
|
|
219
|
+
executable: { type: "boolean" },
|
|
220
|
+
target: { type: "string" },
|
|
221
|
+
},
|
|
222
|
+
additionalProperties: false,
|
|
223
|
+
},
|
|
224
|
+
},
|
|
107
225
|
},
|
|
108
226
|
additionalProperties: false,
|
|
109
227
|
},
|
|
@@ -113,25 +231,18 @@ const LIBRARY_CANDIDATES_SCHEMA = {
|
|
|
113
231
|
* One entry per layer except the manifest layer, which cannot list its own hash
|
|
114
232
|
* inside itself and is pinned by the importer's `#sha256-...` instead. Shape and
|
|
115
233
|
* matching rules are normative in kernel/specs/module-artifact.md; the parser
|
|
116
|
-
* that enforces them is `artifact-layer-index.ts`.
|
|
234
|
+
* that enforces them is `artifact-layer-index.ts`. `role` stays open and
|
|
235
|
+
* `selector` unconstrained: an entry for a newer runtime is skipped, not
|
|
236
|
+
* rejected, and its selector is not examined (spec §3.1). A known role's
|
|
237
|
+
* selector is validated by the parser, which `telo check` runs. */
|
|
117
238
|
const LAYER_INDEX_SCHEMA = {
|
|
118
239
|
type: "array",
|
|
119
240
|
items: {
|
|
120
241
|
type: "object",
|
|
121
242
|
required: ["role", "blob", "integrity"],
|
|
122
243
|
properties: {
|
|
123
|
-
role: { type: "string",
|
|
124
|
-
selector: {
|
|
125
|
-
type: "object",
|
|
126
|
-
required: ["format"],
|
|
127
|
-
properties: {
|
|
128
|
-
format: { type: "string" },
|
|
129
|
-
os: { type: "string" },
|
|
130
|
-
arch: { type: "string" },
|
|
131
|
-
libc: { type: "string" },
|
|
132
|
-
},
|
|
133
|
-
additionalProperties: false,
|
|
134
|
-
},
|
|
244
|
+
role: { type: "string", minLength: 1 },
|
|
245
|
+
selector: {},
|
|
135
246
|
blob: { type: "string", pattern: "^sha256:[0-9a-f]{64}$" },
|
|
136
247
|
integrity: { type: "string", pattern: "^sha256-[A-Za-z0-9_-]{43}$" },
|
|
137
248
|
},
|
|
@@ -480,99 +591,31 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
|
|
|
480
591
|
},
|
|
481
592
|
},
|
|
482
593
|
},
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
},
|
|
509
|
-
},
|
|
510
|
-
},
|
|
511
|
-
},
|
|
512
|
-
],
|
|
513
|
-
},
|
|
514
|
-
provide: {
|
|
515
|
-
type: "object",
|
|
516
|
-
additionalProperties: true,
|
|
517
|
-
properties: {
|
|
518
|
-
kind: { type: "string" },
|
|
519
|
-
name: {
|
|
520
|
-
type: "string",
|
|
521
|
-
"x-telo-context": {
|
|
522
|
-
type: "object",
|
|
523
|
-
additionalProperties: false,
|
|
524
|
-
properties: {
|
|
525
|
-
self: { "x-telo-context-from-root": "schema" },
|
|
526
|
-
},
|
|
527
|
-
},
|
|
528
|
-
},
|
|
529
|
-
},
|
|
530
|
-
},
|
|
531
|
-
run: {
|
|
532
|
-
type: "string",
|
|
533
|
-
"x-telo-context": {
|
|
534
|
-
type: "object",
|
|
535
|
-
additionalProperties: false,
|
|
536
|
-
properties: {
|
|
537
|
-
self: { "x-telo-context-from-root": "schema" },
|
|
538
|
-
},
|
|
539
|
-
},
|
|
540
|
-
},
|
|
541
|
-
// Mount dispatch: names the `resources:` entry (a Telo.Mount, e.g. an
|
|
542
|
-
// Http.Api) whose `register()` this definition delegates to. Same
|
|
543
|
-
// string / { kind, name } grammar as `invoke:`. The named child stays
|
|
544
|
-
// persistent so the produced mount's routes can `!ref` its siblings.
|
|
545
|
-
mount: {
|
|
546
|
-
oneOf: [
|
|
547
|
-
{
|
|
548
|
-
type: "string",
|
|
549
|
-
"x-telo-context": {
|
|
550
|
-
type: "object",
|
|
551
|
-
additionalProperties: false,
|
|
552
|
-
properties: {
|
|
553
|
-
self: { "x-telo-context-from-root": "schema" },
|
|
554
|
-
},
|
|
555
|
-
},
|
|
556
|
-
},
|
|
557
|
-
{
|
|
558
|
-
type: "object",
|
|
559
|
-
additionalProperties: true,
|
|
560
|
-
properties: {
|
|
561
|
-
kind: { type: "string" },
|
|
562
|
-
name: {
|
|
563
|
-
type: "string",
|
|
564
|
-
"x-telo-context": {
|
|
565
|
-
type: "object",
|
|
566
|
-
additionalProperties: false,
|
|
567
|
-
properties: {
|
|
568
|
-
self: { "x-telo-context-from-root": "schema" },
|
|
569
|
-
},
|
|
570
|
-
},
|
|
571
|
-
},
|
|
572
|
-
},
|
|
573
|
-
},
|
|
574
|
-
],
|
|
575
|
-
},
|
|
594
|
+
// A dispatch slot names the `resources:` entry that receives the call,
|
|
595
|
+
// as `!ref <entry>` — the one spelling every reference in Telo has. The
|
|
596
|
+
// string and `{ kind, name }` forms it used to admit (a CEL name
|
|
597
|
+
// template matched against CEL-named entries) were the removed
|
|
598
|
+
// reference object surviving in the one place nothing resolved it;
|
|
599
|
+
// `validate-template-body` is what resolves this slot, so the schema
|
|
600
|
+
// stays open here rather than reporting the same defect twice.
|
|
601
|
+
invoke: dispatchSlot(
|
|
602
|
+
"Invoke target",
|
|
603
|
+
"The `resources:` entry whose `invoke()` this kind dispatches to, as `!ref <entry>`.",
|
|
604
|
+
),
|
|
605
|
+
provide: dispatchSlot(
|
|
606
|
+
"Provide target",
|
|
607
|
+
"The `resources:` entry whose `invoke()` produces this provider's value, as `!ref <entry>`.",
|
|
608
|
+
),
|
|
609
|
+
run: dispatchSlot(
|
|
610
|
+
"Run target",
|
|
611
|
+
"The `resources:` entry whose `run()` this kind dispatches to, as `!ref <entry>`.",
|
|
612
|
+
),
|
|
613
|
+
// The named child stays persistent so the produced mount's routes can
|
|
614
|
+
// `!ref` its siblings.
|
|
615
|
+
mount: dispatchSlot(
|
|
616
|
+
"Mount target",
|
|
617
|
+
"The `resources:` entry (a Telo.Mount, e.g. an Http.Api) whose `register()` this kind delegates to, as `!ref <entry>`.",
|
|
618
|
+
),
|
|
576
619
|
inputs: {
|
|
577
620
|
type: "object",
|
|
578
621
|
additionalProperties: true,
|
|
@@ -593,11 +636,11 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
|
|
|
593
636
|
additionalProperties: false,
|
|
594
637
|
properties: {
|
|
595
638
|
self: { "x-telo-context-from-root": "schema" },
|
|
639
|
+
// Typed from the dispatch target's declared output: the slot
|
|
640
|
+
// holds a `!ref` to a `resources:` entry, resolved to that
|
|
641
|
+
// entry's kind (or its own `outputType`, where it narrows one).
|
|
596
642
|
result: {
|
|
597
|
-
"x-telo-context-from-ref-kind": [
|
|
598
|
-
"provide/kind#outputType",
|
|
599
|
-
"invoke/kind#outputType",
|
|
600
|
-
],
|
|
643
|
+
"x-telo-context-from-ref-kind": ["provide#outputType", "invoke#outputType"],
|
|
601
644
|
},
|
|
602
645
|
},
|
|
603
646
|
},
|
|
@@ -763,6 +806,8 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
|
|
|
763
806
|
items: { type: "string" },
|
|
764
807
|
},
|
|
765
808
|
assets: ASSETS_FILES_SCHEMA,
|
|
809
|
+
native: NATIVE_ENTRIES_SCHEMA,
|
|
810
|
+
sources: SOURCES_SCHEMA,
|
|
766
811
|
layers: LAYER_INDEX_SCHEMA,
|
|
767
812
|
filesIntegrity: LEGACY_FILES_INTEGRITY_SCHEMA,
|
|
768
813
|
// Inline imports — name-keyed map sugar for separate `Telo.Import`
|
|
@@ -936,6 +981,8 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
|
|
|
936
981
|
items: { type: "string" },
|
|
937
982
|
},
|
|
938
983
|
assets: ASSETS_FILES_SCHEMA,
|
|
984
|
+
native: NATIVE_ENTRIES_SCHEMA,
|
|
985
|
+
sources: SOURCES_SCHEMA,
|
|
939
986
|
layers: LAYER_INDEX_SCHEMA,
|
|
940
987
|
filesIntegrity: LEGACY_FILES_INTEGRITY_SCHEMA,
|
|
941
988
|
// Inline imports — same name-keyed map sugar as Telo.Application; the
|
package/src/call-graph.ts
CHANGED
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
possibleUses,
|
|
54
54
|
readRefSlot,
|
|
55
55
|
transfersControl,
|
|
56
|
+
type RefSlot,
|
|
56
57
|
type RefUse,
|
|
57
58
|
type RefUseCases,
|
|
58
59
|
} from "./ref-slot.js";
|
|
@@ -399,6 +400,35 @@ function resolveUseAtSite(
|
|
|
399
400
|
return { use: possibleUses(slot), unresolved: entry.useCases, unresolvedReason };
|
|
400
401
|
}
|
|
401
402
|
|
|
403
|
+
/**
|
|
404
|
+
* A slot's use at one concrete site of one resource, for a consumer that walks
|
|
405
|
+
* the kind's schema itself rather than reading the graph's edges.
|
|
406
|
+
*
|
|
407
|
+
* The same rule the graph applies to a resource-level edge — the same enclosing
|
|
408
|
+
* object, the same schema default — so a consumer and an edge cannot disagree
|
|
409
|
+
* about which case of a case map holds. `concretePath` is the site
|
|
410
|
+
* (`routes[0].handler`); `fieldPath` is the declaration it resolved from
|
|
411
|
+
* (`routes[].handler`), which is what the schema default is read against.
|
|
412
|
+
*/
|
|
413
|
+
export function resolveSlotUseAt(
|
|
414
|
+
slot: RefSlot,
|
|
415
|
+
resource: unknown,
|
|
416
|
+
rootSchema: Record<string, any> | undefined,
|
|
417
|
+
concretePath: string,
|
|
418
|
+
fieldPath: string,
|
|
419
|
+
): Pick<CallGraphEdge, "use" | "unresolved" | "unresolvedReason"> {
|
|
420
|
+
const entry: RefFieldEntry = {
|
|
421
|
+
refs: slot.kinds,
|
|
422
|
+
uses: slot.uses,
|
|
423
|
+
isArray: false,
|
|
424
|
+
...(slot.useCases ? { useCases: slot.useCases } : {}),
|
|
425
|
+
};
|
|
426
|
+
const schemaDefault = rootSchema
|
|
427
|
+
? schemaDefaultOf(enclosingSchemaOf(rootSchema, fieldPath))
|
|
428
|
+
: NO_DEFAULT;
|
|
429
|
+
return resolveUseAtSite(entry, resource, concretePath, schemaDefault);
|
|
430
|
+
}
|
|
431
|
+
|
|
402
432
|
/** A resolved plain reference value (`{kind, name}`, optionally `alias`) — the
|
|
403
433
|
* shape `resolveRefSentinels` leaves at a ref site. NOT a step: a bare boot
|
|
404
434
|
* target written `!ref X` must not mint a step node. */
|
package/src/catch-scope.ts
CHANGED
|
@@ -86,7 +86,7 @@ export function buildEnclosers(
|
|
|
86
86
|
const definition = definitionOf(manifest);
|
|
87
87
|
if (!definition?.schema) continue;
|
|
88
88
|
forEachDrivenSlot(definition.schema, manifest, (driven) => {
|
|
89
|
-
if (driven.kind !== "ref" || !driven.slot.throwsThrough) return;
|
|
89
|
+
if (driven.kind !== "ref" || !driven.slots.some(({ slot }) => slot.throwsThrough)) return;
|
|
90
90
|
const target = resolveRefManifest(driven.data, ctx, moduleOf(manifest));
|
|
91
91
|
if (!target || target === manifest) return;
|
|
92
92
|
const list = enclosers.get(target);
|
package/src/cel-scope-query.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { buildKernelGlobalsIndex } from "./kernel-globals.js";
|
|
|
21
21
|
import { moduleAliasScope } from "./module-alias-scope.js";
|
|
22
22
|
import { isModuleKind } from "./module-kinds.js";
|
|
23
23
|
import { navigateConcretePath } from "./manifest-path.js";
|
|
24
|
+
import { kindAtPath } from "./validate-cel-context.js";
|
|
24
25
|
import { findManifest } from "./find-manifest.js";
|
|
25
26
|
import { resolveLocalRef, walkStepArray } from "./schema-walk.js";
|
|
26
27
|
import { readStepSlot } from "./step-slot.js";
|
|
@@ -269,14 +270,27 @@ export class CelScopeQuery {
|
|
|
269
270
|
}
|
|
270
271
|
|
|
271
272
|
// A kind's own declaration: the target is the `Telo.Definition` document,
|
|
272
|
-
// which is an ordinary manifest in the set.
|
|
273
|
+
// which is an ordinary manifest in the set. The slot holds a `!ref` to a
|
|
274
|
+
// `resources:` entry; the entry's own field wins where it narrows one, else
|
|
275
|
+
// the declaration is the entry's KIND. The first slot that resolves to an
|
|
276
|
+
// entry is the one the type resolver reads, so it is the one navigated.
|
|
273
277
|
const fromRefKind = annotated["x-telo-context-from-ref-kind"];
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
278
|
+
const slots = Array.isArray(fromRefKind) ? fromRefKind : [fromRefKind];
|
|
279
|
+
for (const slotSpec of slots) {
|
|
280
|
+
if (typeof slotSpec !== "string") continue;
|
|
281
|
+
const hash = slotSpec.indexOf("#");
|
|
282
|
+
// `continue`, never `return`: a malformed spec is one entry of a list the
|
|
283
|
+
// type resolver walks to the end, and aborting here made the two answer
|
|
284
|
+
// differently for the same annotation.
|
|
285
|
+
if (hash <= 0) continue;
|
|
286
|
+
const field = slotSpec.slice(hash + 1);
|
|
287
|
+
const namedKind = kindAtPath(root, slotSpec.slice(0, hash));
|
|
288
|
+
if (!namedKind) continue;
|
|
289
|
+
if (namedKind.entry?.[field] !== undefined) {
|
|
290
|
+
const index = (root.resources as unknown[]).indexOf(namedKind.entry);
|
|
291
|
+
return { manifest: resource, path: `resources[${index}].${field}`, propertyMap: false };
|
|
292
|
+
}
|
|
293
|
+
const kindValue = namedKind.kind;
|
|
280
294
|
// The kind was read off THIS resource, so it is spelled in the alias scope
|
|
281
295
|
// of the module that declared it — not the entry's.
|
|
282
296
|
const scope = moduleAliasScope(resource.metadata, this.ctx.aliases, this.ctx.aliasesByModule);
|
|
@@ -300,9 +314,14 @@ export class CelScopeQuery {
|
|
|
300
314
|
(owningModule
|
|
301
315
|
? named.find((m) => (m.metadata as { module?: string } | undefined)?.module === owningModule)
|
|
302
316
|
: undefined) ?? (owningModule && named.length > 1 ? undefined : named[0]);
|
|
303
|
-
|
|
304
|
-
|
|
317
|
+
// Fall through to the next spec when this one names no kind, or names one
|
|
318
|
+
// that does not declare the field — the same order the TYPE resolver
|
|
319
|
+
// takes, so go-to-declaration lands where the type came from instead of on
|
|
320
|
+
// a field the target never had.
|
|
321
|
+
if (!target || target[field] === undefined) continue;
|
|
322
|
+
return { manifest: target, path: field, propertyMap: false };
|
|
305
323
|
}
|
|
324
|
+
if (slots.some((s) => typeof s === "string")) return undefined;
|
|
306
325
|
|
|
307
326
|
// `x-telo-context-element-from` / `-collection-from` type a binding from an
|
|
308
327
|
// EXPRESSION, so there is no declaration to navigate to.
|
package/src/cel-scope.ts
CHANGED