@telorun/analyzer 0.32.0 → 0.34.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.map +1 -1
- package/dist/analysis-registry.js +10 -9
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +284 -80
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +15 -0
- package/dist/definition-registry.d.ts +4 -1
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +20 -3
- package/dist/extends-resolution.d.ts +33 -0
- package/dist/extends-resolution.d.ts.map +1 -0
- package/dist/extends-resolution.js +82 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/sources/integrity.d.ts +7 -0
- package/dist/sources/integrity.d.ts.map +1 -1
- package/dist/sources/integrity.js +12 -2
- package/dist/sources/module-ref.d.ts +6 -2
- package/dist/sources/module-ref.d.ts.map +1 -1
- package/dist/sources/module-ref.js +7 -6
- package/dist/validate-base-mapping.d.ts +21 -0
- package/dist/validate-base-mapping.d.ts.map +1 -0
- package/dist/validate-base-mapping.js +130 -0
- package/dist/validate-extends.d.ts.map +1 -1
- package/dist/validate-extends.js +20 -9
- package/dist/validate-kind-descriptions.d.ts +20 -0
- package/dist/validate-kind-descriptions.d.ts.map +1 -0
- package/dist/validate-kind-descriptions.js +65 -0
- package/dist/validate-provider-coherence.d.ts.map +1 -1
- package/dist/validate-provider-coherence.js +8 -1
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +15 -9
- package/package.json +3 -3
- package/src/analysis-registry.ts +9 -8
- package/src/analyzer.ts +333 -91
- package/src/builtins.ts +15 -0
- package/src/definition-registry.ts +18 -3
- package/src/extends-resolution.ts +124 -0
- package/src/index.ts +13 -0
- package/src/sources/integrity.ts +13 -2
- package/src/sources/module-ref.ts +7 -6
- package/src/validate-base-mapping.ts +154 -0
- package/src/validate-extends.ts +24 -11
- package/src/validate-kind-descriptions.ts +65 -0
- package/src/validate-provider-coherence.ts +12 -2
- package/src/validate-references.ts +13 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analysis-registry.d.ts","sourceRoot":"","sources":["../src/analysis-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKzE,OAAO,EAAqC,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEhG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD;;wDAEwD;AACxD,MAAM,WAAW,YAAY;IAC3B;0CACsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;8EAG0E;IAC1E,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;GAIG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;IAEpE,kBAAkB,CAAC,GAAG,EAAE,kBAAkB,GAAG,IAAI;IAIjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIpE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAIpE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C;;;;;;;OAOG;IACH,mBAAmB,CACjB,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,EAClC,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GACnC,IAAI;IAkBP;;;;;;OAMG;IACH,oBAAoB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,YAAY,EAAE;IAoBhE;;;;;;wBAMoB;IACpB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQtD;;;;;;sFAMkF;IAClF,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS;IAenE;;;;;qDAKiD;IACjD,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS;IAepE,OAAO,CAAC,mBAAmB;IAS3B;;;;;;OAMG;IACH,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,EACxB,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;KAAE,GACzF,IAAI;IAQP;;;;OAIG;IACH,kBAAkB,IAAI,kBAAkB,EAAE;IAI1C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAM/D;;qFAEiF;IACjF,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAUzC,QAAQ,IAAI,MAAM,EAAE;IAIpB;mEAC+D;IAC/D,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAIxC;;;gCAG4B;IAC5B,oBAAoB,IAAI,MAAM,EAAE;IAIhC;wEACoE;IACpE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIhD;;;;;;;;iDAQ6C;IAC7C,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS;
|
|
1
|
+
{"version":3,"file":"analysis-registry.d.ts","sourceRoot":"","sources":["../src/analysis-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -220,17 +220,18 @@ export class AnalysisRegistry {
|
|
|
220
220
|
const targetDef = this.defs.resolve(targetKind);
|
|
221
221
|
if (!targetDef)
|
|
222
222
|
return undefined;
|
|
223
|
+
// General single inheritance: the accepted set is the target kind plus every
|
|
224
|
+
// kind that transitively extends it (subtypes are substitutable). A concrete
|
|
225
|
+
// target contributes itself and any specializations; an abstract contributes
|
|
226
|
+
// its implementations. Same transitive index for both.
|
|
223
227
|
const out = new Set();
|
|
224
|
-
if (targetDef.kind
|
|
225
|
-
for (const def of this.defs.getByExtends(targetKind)) {
|
|
226
|
-
const module = def.metadata?.module;
|
|
227
|
-
if (module && def.metadata?.name) {
|
|
228
|
-
out.add(`${module}.${def.metadata.name}`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
228
|
+
if (targetDef.kind !== "Telo.Abstract")
|
|
233
229
|
out.add(targetKind);
|
|
230
|
+
for (const def of this.defs.getByExtends(targetKind)) {
|
|
231
|
+
const module = def.metadata?.module;
|
|
232
|
+
if (module && def.metadata?.name) {
|
|
233
|
+
out.add(`${module}.${def.metadata.name}`);
|
|
234
|
+
}
|
|
234
235
|
}
|
|
235
236
|
return out;
|
|
236
237
|
}
|
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;
|
|
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;AAoB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAwxB/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;IAmzBvB,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
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { canonicalTypeSchemaId } from "@telorun/sdk";
|
|
2
|
-
import { defaultRegistry, isTaggedSentinel } from "@telorun/templating";
|
|
2
|
+
import { defaultRegistry, isRefSentinel, isTaggedSentinel } from "@telorun/templating";
|
|
3
3
|
import { AliasResolver, scopeResolverForModule } from "./alias-resolver.js";
|
|
4
4
|
import { buildCelEnvironment, buildImportInputCelEnvironment, buildTypedCelEnvironment, } from "./cel-environment.js";
|
|
5
5
|
import { DefinitionRegistry } from "./definition-registry.js";
|
|
6
|
+
import { effectiveAuthorSchema } from "./extends-resolution.js";
|
|
6
7
|
import { buildDependencyGraph, formatCycle } from "./dependency-graph.js";
|
|
7
8
|
import { buildKernelGlobalsSchema, mergeKernelGlobalsIntoContext } from "./kernel-globals.js";
|
|
8
9
|
import { computeSuggestKind } from "./kind-suggest.js";
|
|
@@ -19,7 +20,9 @@ import { DiagnosticSeverity } from "./types.js";
|
|
|
19
20
|
import { extractCelRegionScopes, extractContextsFromSchema, getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
|
|
20
21
|
import { buildEvalPaths, evalPathsCover } from "./eval-paths.js";
|
|
21
22
|
import { validateExtends } from "./validate-extends.js";
|
|
23
|
+
import { validateBaseMapping } from "./validate-base-mapping.js";
|
|
22
24
|
import { validateNestedInlineResources } from "./validate-nested-inline.js";
|
|
25
|
+
import { validateKindDescriptions } from "./validate-kind-descriptions.js";
|
|
23
26
|
import { validateProviderCoherence } from "./validate-provider-coherence.js";
|
|
24
27
|
import { validateReferences } from "./validate-references.js";
|
|
25
28
|
import { validateReferenceForms } from "./validate-reference-forms.js";
|
|
@@ -87,8 +90,12 @@ const SOURCE = "telo-analyzer";
|
|
|
87
90
|
* property the user declared in `schema:` plus synthetic `name` / `kind` and
|
|
88
91
|
* the metadata sub-object (kept open since metadata legitimately carries
|
|
89
92
|
* arbitrary user-added fields). */
|
|
90
|
-
function buildSelfSchema(definition) {
|
|
91
|
-
|
|
93
|
+
function buildSelfSchema(definition, defs, aliases) {
|
|
94
|
+
// The author-facing schema resolves inheritance: with `base:` the child's own
|
|
95
|
+
// schema (the parent's config is internal); without it, `merge(parent, own)`.
|
|
96
|
+
const userSchema = (defs
|
|
97
|
+
? effectiveAuthorSchema(definition, (k) => defs.resolve(aliases?.resolveKind(k) ?? k) ?? defs.resolve(k))
|
|
98
|
+
: (definition.schema ?? {}));
|
|
92
99
|
const userProps = (userSchema.properties ?? {});
|
|
93
100
|
const userRequired = Array.isArray(userSchema.required) ? userSchema.required : [];
|
|
94
101
|
return {
|
|
@@ -145,7 +152,7 @@ function manifestRootForResolver(m, defs, aliases, allManifests) {
|
|
|
145
152
|
const inputs = lookupTemplateInputsSchema(m, defs, aliases, allManifests);
|
|
146
153
|
return {
|
|
147
154
|
...m,
|
|
148
|
-
schema: buildSelfSchema(m),
|
|
155
|
+
schema: buildSelfSchema(m, defs, aliases),
|
|
149
156
|
...(inputs ? { inputType: inputs } : {}),
|
|
150
157
|
};
|
|
151
158
|
}
|
|
@@ -186,6 +193,59 @@ function gatherPropertySchemas(schema) {
|
|
|
186
193
|
}
|
|
187
194
|
return out;
|
|
188
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Generic, role-driven walk over an `x-telo-step-context` step array. Calls
|
|
198
|
+
* `visit(step, stepPath)` for every step — top-level and nested through the
|
|
199
|
+
* `x-telo-topology-role` forms (`branch`, `branch-list`, `case-map`). This is
|
|
200
|
+
* the single definition of how steps nest, shared by `buildStepContextSchema`
|
|
201
|
+
* (which types `steps.<name>.result`) and `validateStepInvokeReferences` (which
|
|
202
|
+
* checks invoke refs), so the topology contract lives in one place — adding a
|
|
203
|
+
* role or nesting form updates both consumers at once. No resource kind is
|
|
204
|
+
* hardcoded; recursion is driven entirely by the schema annotations.
|
|
205
|
+
*/
|
|
206
|
+
function walkStepArray(steps, stepItemSchema, rootSchema, basePath, visit) {
|
|
207
|
+
const dispatchRole = (data, role, itemsSchema, path) => {
|
|
208
|
+
if (role === "branch" && Array.isArray(data)) {
|
|
209
|
+
walkStepArray(data, stepItemSchema, rootSchema, path, visit);
|
|
210
|
+
}
|
|
211
|
+
else if (role === "case-map" && data && typeof data === "object" && !Array.isArray(data)) {
|
|
212
|
+
for (const [caseKey, arr] of Object.entries(data)) {
|
|
213
|
+
if (Array.isArray(arr))
|
|
214
|
+
walkStepArray(arr, stepItemSchema, rootSchema, `${path}.${caseKey}`, visit);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else if (role === "branch-list" && Array.isArray(data)) {
|
|
218
|
+
const entrySchema = resolveLocalRef(itemsSchema, rootSchema);
|
|
219
|
+
if (!entrySchema)
|
|
220
|
+
return;
|
|
221
|
+
data.forEach((entry, i) => {
|
|
222
|
+
if (!entry || typeof entry !== "object")
|
|
223
|
+
return;
|
|
224
|
+
for (const [subKey, subSchema] of gatherPropertySchemas(entrySchema)) {
|
|
225
|
+
const subRole = subSchema["x-telo-topology-role"];
|
|
226
|
+
if (typeof subRole !== "string")
|
|
227
|
+
continue;
|
|
228
|
+
dispatchRole(entry[subKey], subRole, subSchema.items, `${path}[${i}].${subKey}`);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
steps.forEach((step, i) => {
|
|
234
|
+
if (!step || typeof step !== "object")
|
|
235
|
+
return;
|
|
236
|
+
const s = step;
|
|
237
|
+
const stepPath = `${basePath}[${i}]`;
|
|
238
|
+
visit(s, stepPath);
|
|
239
|
+
if (!stepItemSchema)
|
|
240
|
+
return;
|
|
241
|
+
for (const [propKey, propSchema] of gatherPropertySchemas(stepItemSchema)) {
|
|
242
|
+
const role = propSchema["x-telo-topology-role"];
|
|
243
|
+
if (typeof role !== "string")
|
|
244
|
+
continue;
|
|
245
|
+
dispatchRole(s[propKey], role, propSchema.items, `${stepPath}.${propKey}`);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
189
249
|
/**
|
|
190
250
|
* Build a `steps` context schema from `x-telo-step-context` annotation.
|
|
191
251
|
* Walks each step in the manifest array, resolves the invoked resource's outputType,
|
|
@@ -229,80 +289,40 @@ function buildStepContextSchema(manifest, defSchema, allManifests, defs, aliases
|
|
|
229
289
|
continue;
|
|
230
290
|
const stepItemSchema = resolveLocalRef(fieldSchema.items, defSchema);
|
|
231
291
|
const stepProperties = {};
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
292
|
+
walkStepArray(steps, stepItemSchema, defSchema, fieldName, (s) => {
|
|
293
|
+
const name = s.name;
|
|
294
|
+
const invoke = s[invokeField];
|
|
295
|
+
// Only invoke steps register a `steps.<name>.result` entry — control-flow
|
|
296
|
+
// wrappers (try/if/while/switch/throw) don't produce a result and must
|
|
297
|
+
// not shadow real entries with a permissive `additionalProperties: true`,
|
|
298
|
+
// or unknown step references slip through chain validation.
|
|
299
|
+
if (typeof name !== "string" || !invoke || typeof invoke !== "object")
|
|
300
|
+
return;
|
|
301
|
+
let outputSchema;
|
|
302
|
+
const invokedKind = invoke.kind;
|
|
303
|
+
const invokedName = invoke.name;
|
|
304
|
+
if (invokedName) {
|
|
305
|
+
const invokedManifest = allManifests.find((m) => m.metadata?.name === invokedName &&
|
|
306
|
+
(!invokedKind || m.kind === invokedKind));
|
|
307
|
+
if (invokedManifest) {
|
|
308
|
+
outputSchema = resolveTypeFieldToSchema(invokedManifest[outputTypeField], allManifests);
|
|
240
309
|
}
|
|
241
310
|
}
|
|
242
|
-
else
|
|
243
|
-
|
|
244
|
-
if (!entrySchema)
|
|
245
|
-
return;
|
|
246
|
-
for (const entry of data) {
|
|
247
|
-
if (!entry || typeof entry !== "object")
|
|
248
|
-
continue;
|
|
249
|
-
for (const [subKey, subSchema] of gatherPropertySchemas(entrySchema)) {
|
|
250
|
-
const subRole = subSchema["x-telo-topology-role"];
|
|
251
|
-
if (typeof subRole !== "string")
|
|
252
|
-
continue;
|
|
253
|
-
dispatchRole(entry[subKey], subRole, subSchema.items);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
311
|
+
else {
|
|
312
|
+
outputSchema = resolveTypeFieldToSchema(invoke[outputTypeField], allManifests);
|
|
256
313
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
continue;
|
|
262
|
-
const s = step;
|
|
263
|
-
const name = s.name;
|
|
264
|
-
const invoke = s[invokeField];
|
|
265
|
-
// Only invoke steps register a `steps.<name>.result` entry — control-flow
|
|
266
|
-
// wrappers (try/if/while/switch/throw) don't produce a result and must
|
|
267
|
-
// not shadow real entries with a permissive `additionalProperties: true`,
|
|
268
|
-
// or unknown step references slip through chain validation.
|
|
269
|
-
if (typeof name === "string" && invoke && typeof invoke === "object") {
|
|
270
|
-
let outputSchema;
|
|
271
|
-
const invokedKind = invoke.kind;
|
|
272
|
-
const invokedName = invoke.name;
|
|
273
|
-
if (invokedName) {
|
|
274
|
-
const invokedManifest = allManifests.find((m) => m.metadata?.name === invokedName &&
|
|
275
|
-
(!invokedKind || m.kind === invokedKind));
|
|
276
|
-
if (invokedManifest) {
|
|
277
|
-
outputSchema = resolveTypeFieldToSchema(invokedManifest[outputTypeField], allManifests);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
outputSchema = resolveTypeFieldToSchema(invoke[outputTypeField], allManifests);
|
|
282
|
-
}
|
|
283
|
-
// Fallback: pull outputType from the kind's Telo.Definition. The
|
|
284
|
-
// resource manifest typically doesn't carry outputType; the def does.
|
|
285
|
-
if (!outputSchema && invokedKind) {
|
|
286
|
-
outputSchema = lookupDefinitionTypeField(invokedKind, outputTypeField, defs, aliases, allManifests);
|
|
287
|
-
}
|
|
288
|
-
stepProperties[name] = {
|
|
289
|
-
type: "object",
|
|
290
|
-
properties: {
|
|
291
|
-
result: outputSchema ?? { type: "object", additionalProperties: true },
|
|
292
|
-
},
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
if (stepItemSchema) {
|
|
296
|
-
for (const [propKey, propSchema] of gatherPropertySchemas(stepItemSchema)) {
|
|
297
|
-
const role = propSchema["x-telo-topology-role"];
|
|
298
|
-
if (typeof role !== "string")
|
|
299
|
-
continue;
|
|
300
|
-
dispatchRole(s[propKey], role, propSchema.items);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
314
|
+
// Fallback: pull outputType from the kind's Telo.Definition. The
|
|
315
|
+
// resource manifest typically doesn't carry outputType; the def does.
|
|
316
|
+
if (!outputSchema && invokedKind) {
|
|
317
|
+
outputSchema = lookupDefinitionTypeField(invokedKind, outputTypeField, defs, aliases, allManifests);
|
|
303
318
|
}
|
|
304
|
-
|
|
305
|
-
|
|
319
|
+
stepProperties[name] = {
|
|
320
|
+
type: "object",
|
|
321
|
+
properties: {
|
|
322
|
+
result: outputSchema ?? { type: "object", additionalProperties: true },
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
});
|
|
306
326
|
if (Object.keys(stepProperties).length > 0) {
|
|
307
327
|
return {
|
|
308
328
|
type: "object",
|
|
@@ -312,6 +332,178 @@ function buildStepContextSchema(manifest, defSchema, allManifests, defs, aliases
|
|
|
312
332
|
}
|
|
313
333
|
return undefined;
|
|
314
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Capabilities whose instances structurally expose no `invoke`/`run` method, so
|
|
337
|
+
* a step `invoke` of one always fails at runtime with ERR_RESOURCE_NOT_INVOKABLE
|
|
338
|
+
* (kernel dispatch checks method presence, not capability — evaluation-context.ts).
|
|
339
|
+
* `Telo.Service` is intentionally absent: some services are invocable (a function
|
|
340
|
+
* handler dispatched directly, e.g. `Lambda.Function`), so it can't be rejected
|
|
341
|
+
* statically without false positives. This is the sound subset of the runtime rule.
|
|
342
|
+
*/
|
|
343
|
+
const NON_INVOKABLE_CAPABILITIES = new Set([
|
|
344
|
+
"Telo.Provider",
|
|
345
|
+
"Telo.Mount",
|
|
346
|
+
"Telo.Type",
|
|
347
|
+
"Telo.Template",
|
|
348
|
+
]);
|
|
349
|
+
/**
|
|
350
|
+
* Validate `x-telo-step-context` step `invoke` references (e.g. `Run.Sequence`
|
|
351
|
+
* steps).
|
|
352
|
+
*
|
|
353
|
+
* The reference field map deliberately does NOT descend into step `invoke`
|
|
354
|
+
* slots — they sit behind a local `$ref` to the shared step definition, and
|
|
355
|
+
* turning the descent on would make Phase 5 inject live instances there,
|
|
356
|
+
* breaking the invoke dispatch path (see `reference-field-map.ts`). A
|
|
357
|
+
* consequence is that `validateReferences` never sees these slots, so a bad
|
|
358
|
+
* step invoke passes `telo check` and only fails at runtime. This pass covers
|
|
359
|
+
* exactly those slots, in two dimensions:
|
|
360
|
+
* - Existence: an `invoke: !ref <name>` that names a missing instance — or a
|
|
361
|
+
* *kind* instead of an exported instance (`!ref Stream.Of`) — is a still-a-
|
|
362
|
+
* sentinel after Phase 2.5 resolution → `UNRESOLVED_REFERENCE` (runtime
|
|
363
|
+
* `ERR_RESOURCE_NOT_FOUND`).
|
|
364
|
+
* - Invokability: a resolved instance whose capability structurally has no
|
|
365
|
+
* invoke/run method (`NON_INVOKABLE_CAPABILITIES`) → `REFERENCE_KIND_MISMATCH`
|
|
366
|
+
* (runtime `ERR_RESOURCE_NOT_INVOKABLE`).
|
|
367
|
+
*
|
|
368
|
+
* Generic and topology-driven — it walks steps via the same `x-telo-step-context`
|
|
369
|
+
* / `x-telo-topology-role` annotations `buildStepContextSchema` uses (through the
|
|
370
|
+
* shared `walkStepArray`), so nested branches (then/else/do/catch/cases) are
|
|
371
|
+
* covered and no `Run.Sequence` field name is hardcoded. The cross-module
|
|
372
|
+
* partial-analysis guard mirrors `validateReferences`, so a reference into an
|
|
373
|
+
* unloaded import is skipped rather than false-flagged.
|
|
374
|
+
*/
|
|
375
|
+
function validateStepInvokeReferences(allManifests, defs, aliases) {
|
|
376
|
+
const diagnostics = [];
|
|
377
|
+
// Local instance names + loaded-module set — same construction as
|
|
378
|
+
// validateReferences, so the cross-module guard behaves identically.
|
|
379
|
+
const localNames = new Set();
|
|
380
|
+
const loadedModules = new Set();
|
|
381
|
+
// Also collect names of resources nested inside a manifest tree — notably
|
|
382
|
+
// `with:`-scoped resources (an `x-telo-scope` region the field map does not
|
|
383
|
+
// extract to a top-level manifest). A step can invoke one by bare name, so
|
|
384
|
+
// omitting them would false-flag a valid `!ref`. Conservative: any nested
|
|
385
|
+
// object carrying both a `kind` and a `metadata.name` is a resource
|
|
386
|
+
// definition; scope visibility is left to the runtime.
|
|
387
|
+
const collectNestedNames = (value) => {
|
|
388
|
+
if (!value || typeof value !== "object" || isTaggedSentinel(value))
|
|
389
|
+
return;
|
|
390
|
+
if (Array.isArray(value)) {
|
|
391
|
+
for (const item of value)
|
|
392
|
+
collectNestedNames(item);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const obj = value;
|
|
396
|
+
const name = obj.metadata?.name;
|
|
397
|
+
if (typeof obj.kind === "string" && typeof name === "string")
|
|
398
|
+
localNames.add(name);
|
|
399
|
+
for (const v of Object.values(obj))
|
|
400
|
+
collectNestedNames(v);
|
|
401
|
+
};
|
|
402
|
+
for (const r of allManifests) {
|
|
403
|
+
if (r.kind === "Telo.Import") {
|
|
404
|
+
const m = r.metadata?.resolvedModuleName;
|
|
405
|
+
if (typeof m === "string")
|
|
406
|
+
loadedModules.add(m);
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
const meta = r.metadata;
|
|
410
|
+
if (typeof meta?.name !== "string" || REF_VALIDATION_SKIP_KINDS.has(r.kind))
|
|
411
|
+
continue;
|
|
412
|
+
if (meta.forwardedExport === true) {
|
|
413
|
+
if (typeof meta.module === "string")
|
|
414
|
+
loadedModules.add(meta.module);
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
localNames.add(meta.name);
|
|
418
|
+
collectNestedNames(r);
|
|
419
|
+
}
|
|
420
|
+
const validateInvoke = (value, resource, filePath, path) => {
|
|
421
|
+
if (isRefSentinel(value)) {
|
|
422
|
+
// An unresolved `!ref` is a miss: a real instance would have resolved to
|
|
423
|
+
// `{kind, name}` in Phase 2.5.
|
|
424
|
+
const refName = value.source;
|
|
425
|
+
const dot = refName.indexOf(".");
|
|
426
|
+
const aliasPrefix = dot > 0 ? refName.slice(0, dot) : undefined;
|
|
427
|
+
if (aliasPrefix && aliasPrefix !== "Self" && aliases.hasAlias(aliasPrefix)) {
|
|
428
|
+
const module = aliases.moduleForAlias(aliasPrefix);
|
|
429
|
+
// Partial single-file analysis (import not loaded) — skip to avoid a false miss.
|
|
430
|
+
if (module && !loadedModules.has(module))
|
|
431
|
+
return;
|
|
432
|
+
diagnostics.push({
|
|
433
|
+
severity: DiagnosticSeverity.Error,
|
|
434
|
+
code: "UNRESOLVED_REFERENCE",
|
|
435
|
+
source: SOURCE,
|
|
436
|
+
message: `${resource.kind}/${resource.name}: step invoke at '${path}' → '${refName}' is not an exported instance of module '${module ?? aliasPrefix}' (reference a declared instance, not a kind)`,
|
|
437
|
+
data: { resource, filePath, path },
|
|
438
|
+
});
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
const localName = aliasPrefix === "Self" ? refName.slice(dot + 1) : refName;
|
|
442
|
+
if (localNames.has(localName))
|
|
443
|
+
return;
|
|
444
|
+
diagnostics.push({
|
|
445
|
+
severity: DiagnosticSeverity.Error,
|
|
446
|
+
code: "UNRESOLVED_REFERENCE",
|
|
447
|
+
source: SOURCE,
|
|
448
|
+
message: `${resource.kind}/${resource.name}: step invoke at '${path}' → resource '${localName}' not found`,
|
|
449
|
+
data: { resource, filePath, path },
|
|
450
|
+
});
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
// Resolved `{kind, name}` (or an inline `{kind, …}` definition) — the
|
|
454
|
+
// instance exists. Mirror the kernel's ERR_RESOURCE_NOT_INVOKABLE, which
|
|
455
|
+
// fires when the instance has neither an `invoke` nor a `run` method
|
|
456
|
+
// (evaluation-context.ts). That is a per-instance property, not a pure
|
|
457
|
+
// capability, so only capabilities that STRUCTURALLY expose no such method
|
|
458
|
+
// are rejected statically — Service is intentionally excluded, since some
|
|
459
|
+
// services are invocable (e.g. a function handler dispatched directly).
|
|
460
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
461
|
+
return;
|
|
462
|
+
const kind = value.kind;
|
|
463
|
+
if (typeof kind !== "string")
|
|
464
|
+
return;
|
|
465
|
+
const capability = defs.resolve(aliases.resolveKind(kind) ?? kind)?.capability;
|
|
466
|
+
if (typeof capability === "string" && NON_INVOKABLE_CAPABILITIES.has(capability)) {
|
|
467
|
+
diagnostics.push({
|
|
468
|
+
severity: DiagnosticSeverity.Error,
|
|
469
|
+
code: "REFERENCE_KIND_MISMATCH",
|
|
470
|
+
source: SOURCE,
|
|
471
|
+
message: `${resource.kind}/${resource.name}: step invoke at '${path}' → '${kind}' is a ${capability} and cannot be invoked in a step — it has no invoke or run method (runtime ERR_RESOURCE_NOT_INVOKABLE)`,
|
|
472
|
+
data: { resource, filePath, path },
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
for (const m of allManifests) {
|
|
477
|
+
const meta = m.metadata;
|
|
478
|
+
if (typeof meta?.name !== "string" ||
|
|
479
|
+
REF_VALIDATION_SKIP_KINDS.has(m.kind) ||
|
|
480
|
+
meta.forwardedExport === true)
|
|
481
|
+
continue;
|
|
482
|
+
const def = defs.resolve(aliases.resolveKind(m.kind) ?? m.kind);
|
|
483
|
+
const defSchema = def?.schema;
|
|
484
|
+
if (!defSchema?.properties)
|
|
485
|
+
continue;
|
|
486
|
+
const resource = { kind: m.kind, name: meta.name };
|
|
487
|
+
const filePath = typeof meta.source === "string" ? meta.source : undefined;
|
|
488
|
+
for (const [fieldName, fieldSchema] of Object.entries(defSchema.properties)) {
|
|
489
|
+
const stepCtx = fieldSchema["x-telo-step-context"];
|
|
490
|
+
const invokeField = stepCtx?.invoke;
|
|
491
|
+
if (!invokeField)
|
|
492
|
+
continue;
|
|
493
|
+
const steps = m[fieldName];
|
|
494
|
+
if (!Array.isArray(steps))
|
|
495
|
+
continue;
|
|
496
|
+
const stepItemSchema = resolveLocalRef(fieldSchema.items, defSchema);
|
|
497
|
+
walkStepArray(steps, stepItemSchema, defSchema, fieldName, (s, stepPath) => {
|
|
498
|
+
const invoke = s[invokeField];
|
|
499
|
+
if (invoke === undefined || invoke === null)
|
|
500
|
+
return;
|
|
501
|
+
validateInvoke(invoke, resource, filePath, `${stepPath}.${invokeField}`);
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return diagnostics;
|
|
506
|
+
}
|
|
315
507
|
/**
|
|
316
508
|
* Collect every field annotated with `x-telo-error-context` anywhere in a
|
|
317
509
|
* definition schema (resolving local `$ref`s into `$defs`, cycle-safe), mapping
|
|
@@ -865,20 +1057,25 @@ export class StaticAnalyzer {
|
|
|
865
1057
|
});
|
|
866
1058
|
continue;
|
|
867
1059
|
}
|
|
868
|
-
// Validate resource config against definition schema
|
|
1060
|
+
// Validate resource config against the definition's AUTHOR-FACING schema —
|
|
1061
|
+
// inheritance-resolved: with `base:` the child's own schema (parent config
|
|
1062
|
+
// is internal), else `merge(parent, own)` so a `base:`-less `extends` child
|
|
1063
|
+
// is validated against the inherited fields it may set. For a definition
|
|
1064
|
+
// that neither extends nor uses `base:` this is exactly its own schema.
|
|
869
1065
|
// `kind` and `metadata` are implicit on every resource — inject them so module
|
|
870
1066
|
// authors don't have to repeat them when using additionalProperties: false.
|
|
871
|
-
|
|
872
|
-
|
|
1067
|
+
const authorSchema = effectiveAuthorSchema(definition, (k) => defs.resolve(aliases.resolveKind(k) ?? k) ?? defs.resolve(k));
|
|
1068
|
+
if (authorSchema && Object.keys(authorSchema).length > 0) {
|
|
1069
|
+
const schema = authorSchema.additionalProperties === false
|
|
873
1070
|
? {
|
|
874
|
-
...
|
|
1071
|
+
...authorSchema,
|
|
875
1072
|
properties: {
|
|
876
1073
|
kind: { type: "string" },
|
|
877
1074
|
metadata: { type: "object" },
|
|
878
|
-
...
|
|
1075
|
+
...authorSchema.properties,
|
|
879
1076
|
},
|
|
880
1077
|
}
|
|
881
|
-
:
|
|
1078
|
+
: authorSchema;
|
|
882
1079
|
// Phase 1: CEL type checking — walk data+schema together, check env.check() return types.
|
|
883
1080
|
// A Telo.Import's variables/secrets are a config-only contract evaluated against the
|
|
884
1081
|
// IMPORTING module's scope, so type them from the owning module doc (matched by
|
|
@@ -1177,10 +1374,17 @@ export class StaticAnalyzer {
|
|
|
1177
1374
|
}, { aliases });
|
|
1178
1375
|
// Validate resource references (Phase 3)
|
|
1179
1376
|
diagnostics.push(...validateReferences(allManifests, { aliases, definitions: defs, aliasesByModule }));
|
|
1377
|
+
// Validate step `invoke` references — the slots the reference field map
|
|
1378
|
+
// deliberately skips (behind the step `$ref`), so a missing instance or a
|
|
1379
|
+
// kind-instead-of-instance ref there is caught statically, not at runtime.
|
|
1380
|
+
diagnostics.push(...validateStepInvokeReferences(allManifests, defs, aliases));
|
|
1180
1381
|
// Validate `extends` fields and flag legacy `capability: <UserAbstract>` overload.
|
|
1181
1382
|
diagnostics.push(...validateExtends(allManifests, defs, aliases));
|
|
1383
|
+
diagnostics.push(...validateBaseMapping(allManifests, defs, aliases));
|
|
1182
1384
|
// Validate provider coherence rules for `provide:` template-target definitions.
|
|
1183
1385
|
diagnostics.push(...validateProviderCoherence(allManifests, defs, aliases));
|
|
1386
|
+
// Warn about exported kinds lacking a metadata.description (semantic-search input).
|
|
1387
|
+
diagnostics.push(...validateKindDescriptions(allManifests));
|
|
1184
1388
|
// Validate throws: declarations and catches: coverage (rules 1, 2, 4, 7)
|
|
1185
1389
|
diagnostics.push(...validateThrowsCoverage(allManifests, defs, aliases, this.celEnv, aliasesByModule, rootModules));
|
|
1186
1390
|
// Warn about declared variables / secrets / ports that no CEL references.
|
package/dist/builtins.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,EAujB/C,CAAC"}
|
package/dist/builtins.js
CHANGED
|
@@ -207,6 +207,21 @@ export const KERNEL_BUILTINS = [
|
|
|
207
207
|
},
|
|
208
208
|
},
|
|
209
209
|
},
|
|
210
|
+
// `base:` ("super(...)") — construction mapping for an inherited
|
|
211
|
+
// (concrete-`extends`) definition. Its CEL is evaluated once against
|
|
212
|
+
// `self` (typed from this definition's `schema:`) to build the parent
|
|
213
|
+
// kind's config. Same `self`-only scope as a resource body.
|
|
214
|
+
base: {
|
|
215
|
+
type: "object",
|
|
216
|
+
additionalProperties: true,
|
|
217
|
+
"x-telo-context": {
|
|
218
|
+
type: "object",
|
|
219
|
+
additionalProperties: false,
|
|
220
|
+
properties: {
|
|
221
|
+
self: { "x-telo-context-from-root": "schema" },
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
210
225
|
},
|
|
211
226
|
},
|
|
212
227
|
},
|
|
@@ -65,7 +65,10 @@ export declare class DefinitionRegistry {
|
|
|
65
65
|
* Returns undefined when the string is malformed or the identity is not registered. */
|
|
66
66
|
resolveRef(xTeloRef: string): string | undefined;
|
|
67
67
|
resolve(kind: string): ResourceDefinition | undefined;
|
|
68
|
-
/** Returns the
|
|
68
|
+
/** Returns the reference field map for the given kind, computed lazily from the
|
|
69
|
+
* kind's AUTHOR-FACING (inheritance-resolved) schema and memoized. Lazy so a
|
|
70
|
+
* child registered before its parent still sees the parent's inherited ref
|
|
71
|
+
* slots once both are present. */
|
|
69
72
|
getFieldMap(kind: string): ReferenceFieldMap | undefined;
|
|
70
73
|
/** Returns the field map for `kind`, falling back to the alias-resolved kind when not found. */
|
|
71
74
|
getFieldMapForKind(kind: string, aliases?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition-registry.d.ts","sourceRoot":"","sources":["../src/definition-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"definition-registry.d.ts","sourceRoot":"","sources":["../src/definition-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAC;AAIlC,+EAA+E;AAC/E,qBAAa,kBAAkB;;IAK7B;;sFAEkF;IAClF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IAEzD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;IAClE,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D;6DACyD;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD;;0EAEsE;IACtE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA6B;IAEhE,QAAQ,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI;IAqC9C,OAAO,CAAC,aAAa;IASrB;;;yFAGqF;IACrF,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IA+B1E;;;;iFAI6E;IAC7E,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAMtE;uFACmF;IACnF,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIhC;4EACwE;IACxE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMnE;;;;oFAIgF;IAChF,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE;IAWtE;;;;;;iCAM6B;IAC7B,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,GAAG,SAAS;IASnE,OAAO,CAAC,iBAAiB;IAczB;;;;;;;;4FAQwF;IACxF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAUhD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAIrD;;;uCAGmC;IACnC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAWxD,gGAAgG;IAChG,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,GACvD,iBAAiB,GAAG,SAAS;IAOhC;;;;;;;;qFAQiF;IACjF,2BAA2B,CACzB,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,aAAa,EACtB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC1C,iBAAiB,GAAG,SAAS;IAkChC,OAAO,CAAC,uBAAuB;IA+B/B;;qEAEiE;IACjE,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAgBxD,KAAK,IAAI,MAAM,EAAE;CAGlB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { KERNEL_BUILTINS } from "./builtins.js";
|
|
2
2
|
import { buildFieldMapAtPath, buildReferenceFieldMap, isSchemaFromEntry, } from "./reference-field-map.js";
|
|
3
3
|
import { createAjv, formatSingleError, navigateJsonPointer } from "./schema-compat.js";
|
|
4
|
+
import { effectiveAuthorSchema } from "./extends-resolution.js";
|
|
4
5
|
/** Pure kind → ResourceDefinition map. No controller loading, no lifecycle. */
|
|
5
6
|
export class DefinitionRegistry {
|
|
6
7
|
constructor() {
|
|
@@ -27,7 +28,11 @@ export class DefinitionRegistry {
|
|
|
27
28
|
const { name, module: mod } = definition.metadata;
|
|
28
29
|
const key = mod ? `${mod}.${name}` : name;
|
|
29
30
|
this.defs.set(key, definition);
|
|
30
|
-
|
|
31
|
+
// Field maps derive from the AUTHOR-FACING (inheritance-resolved) schema, which
|
|
32
|
+
// depends on the parent — possibly registered after this child. Clear the cache
|
|
33
|
+
// so any already-computed map recomputes against the now-larger def set; the
|
|
34
|
+
// maps rebuild lazily on first `getFieldMap` (after all defs are registered).
|
|
35
|
+
this.fieldMaps.clear();
|
|
31
36
|
// `capability` populates extendedBy for backward-compat with the legacy pattern where
|
|
32
37
|
// a concrete definition overloaded `capability: <AbstractKind>` to mean "implements
|
|
33
38
|
// this abstract." The canonical pattern is `extends` (below). Both populate the index,
|
|
@@ -186,9 +191,21 @@ export class DefinitionRegistry {
|
|
|
186
191
|
resolve(kind) {
|
|
187
192
|
return this.defs.get(kind);
|
|
188
193
|
}
|
|
189
|
-
/** Returns the
|
|
194
|
+
/** Returns the reference field map for the given kind, computed lazily from the
|
|
195
|
+
* kind's AUTHOR-FACING (inheritance-resolved) schema and memoized. Lazy so a
|
|
196
|
+
* child registered before its parent still sees the parent's inherited ref
|
|
197
|
+
* slots once both are present. */
|
|
190
198
|
getFieldMap(kind) {
|
|
191
|
-
|
|
199
|
+
const cached = this.fieldMaps.get(kind);
|
|
200
|
+
if (cached)
|
|
201
|
+
return cached;
|
|
202
|
+
const def = this.defs.get(kind);
|
|
203
|
+
if (!def)
|
|
204
|
+
return undefined;
|
|
205
|
+
const schema = effectiveAuthorSchema(def, (k) => this.resolve(k));
|
|
206
|
+
const map = buildReferenceFieldMap(schema ?? {});
|
|
207
|
+
this.fieldMaps.set(kind, map);
|
|
208
|
+
return map;
|
|
192
209
|
}
|
|
193
210
|
/** Returns the field map for `kind`, falling back to the alias-resolved kind when not found. */
|
|
194
211
|
getFieldMapForKind(kind, aliases) {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ResourceDefinition } from "@telorun/sdk";
|
|
2
|
+
/** Resolves a kind string (canonical or alias form, depending on the caller's
|
|
3
|
+
* registry) to its `Telo.Definition` / `Telo.Abstract`, or undefined. */
|
|
4
|
+
export type DefResolver = (kind: string) => ResourceDefinition | undefined;
|
|
5
|
+
/** The definition a given definition directly `extends`, or undefined when it
|
|
6
|
+
* extends nothing / the target can't be resolved. */
|
|
7
|
+
export declare function resolveParent(def: ResourceDefinition | undefined, resolve: DefResolver): ResourceDefinition | undefined;
|
|
8
|
+
/** The `extends` ancestor chain, nearest-first, excluding `def` itself.
|
|
9
|
+
* Cycle-guarded so a malformed self/mutual `extends` can't loop forever. */
|
|
10
|
+
export declare function ancestorChain(def: ResourceDefinition | undefined, resolve: DefResolver): ResourceDefinition[];
|
|
11
|
+
/** True when a definition carries its own controller (`controllers:`) or a
|
|
12
|
+
* template body (`invoke:` / `run:` / `provide:` / `mount:` / `resources:`). */
|
|
13
|
+
export declare function hasOwnControllerOrTemplate(def: ResourceDefinition | undefined): boolean;
|
|
14
|
+
/** The nearest concrete ancestor that provides a controller (own `controllers:`
|
|
15
|
+
* or a template body) — the definition whose controller an inherited child
|
|
16
|
+
* delegates to. Undefined when no controller-bearing concrete ancestor exists. */
|
|
17
|
+
export declare function controllerBearingAncestor(def: ResourceDefinition | undefined, resolve: DefResolver): ResourceDefinition | undefined;
|
|
18
|
+
/** True when this definition inherits its controller by delegation: it declares
|
|
19
|
+
* `extends`, has no own controller/template body, and its nearest concrete
|
|
20
|
+
* ancestor is controller-bearing. */
|
|
21
|
+
export declare function isInheritedDelegation(def: ResourceDefinition | undefined, resolve: DefResolver): boolean;
|
|
22
|
+
/** The effective (possibly inherited) capability: the nearest self-or-ancestor
|
|
23
|
+
* that declares a `capability`. Undefined when none in the chain does. */
|
|
24
|
+
export declare function inheritedCapability(def: ResourceDefinition | undefined, resolve: DefResolver): string | undefined;
|
|
25
|
+
/** The author-facing schema for a definition:
|
|
26
|
+
* - with `base:` present → the definition's **own** schema (the parent's config
|
|
27
|
+
* fields are internal, set solely through `base:`).
|
|
28
|
+
* - without `base:` but with `extends` → `merge(parent-effective, own)` (a pure
|
|
29
|
+
* additive extension; child overrides on key conflicts), reusing the same
|
|
30
|
+
* `mergeTypeSchemas` that `Type.JsonSchema.extends` uses.
|
|
31
|
+
* - no `extends` → the own schema unchanged. */
|
|
32
|
+
export declare function effectiveAuthorSchema(def: ResourceDefinition | undefined, resolve: DefResolver): Record<string, any>;
|
|
33
|
+
//# sourceMappingURL=extends-resolution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extends-resolution.d.ts","sourceRoot":"","sources":["../src/extends-resolution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGvD;0EAC0E;AAC1E,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;AAqB3E;sDACsD;AACtD,wBAAgB,aAAa,CAC3B,GAAG,EAAE,kBAAkB,GAAG,SAAS,EACnC,OAAO,EAAE,WAAW,GACnB,kBAAkB,GAAG,SAAS,CAIhC;AAED;6EAC6E;AAC7E,wBAAgB,aAAa,CAC3B,GAAG,EAAE,kBAAkB,GAAG,SAAS,EACnC,OAAO,EAAE,WAAW,GACnB,kBAAkB,EAAE,CAUtB;AAED;iFACiF;AACjF,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,kBAAkB,GAAG,SAAS,GAAG,OAAO,CAUvF;AAED;;mFAEmF;AACnF,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,kBAAkB,GAAG,SAAS,EACnC,OAAO,EAAE,WAAW,GACnB,kBAAkB,GAAG,SAAS,CAMhC;AAED;;sCAEsC;AACtC,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,kBAAkB,GAAG,SAAS,EACnC,OAAO,EAAE,WAAW,GACnB,OAAO,CAGT;AAED;2EAC2E;AAC3E,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,kBAAkB,GAAG,SAAS,EACnC,OAAO,EAAE,WAAW,GACnB,MAAM,GAAG,SAAS,CAMpB;AAED;;;;;;iDAMiD;AACjD,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,kBAAkB,GAAG,SAAS,EACnC,OAAO,EAAE,WAAW,GACnB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAOrB"}
|