@telorun/analyzer 0.12.0 → 0.13.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/README.md +2 -2
- package/dist/analysis-registry.d.ts +12 -0
- package/dist/analysis-registry.d.ts.map +1 -1
- package/dist/analysis-registry.js +15 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +131 -85
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +25 -0
- package/dist/cel-environment.d.ts +1 -1
- package/dist/cel-environment.d.ts.map +1 -1
- package/dist/cel-environment.js +40 -2
- package/dist/definition-registry.d.ts +12 -1
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +20 -1
- package/dist/dependency-graph.d.ts.map +1 -1
- package/dist/dependency-graph.js +41 -62
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/kernel-globals.d.ts +1 -1
- package/dist/kernel-globals.d.ts.map +1 -1
- package/dist/kernel-globals.js +19 -1
- package/dist/manifest-visitor.d.ts +109 -0
- package/dist/manifest-visitor.d.ts.map +1 -0
- package/dist/manifest-visitor.js +110 -0
- package/dist/reference-field-map.d.ts +1 -0
- package/dist/reference-field-map.d.ts.map +1 -1
- package/dist/reference-field-map.js +1 -1
- package/dist/schema-compat.d.ts +14 -0
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +38 -2
- package/dist/validate-cel-context.d.ts +14 -0
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +38 -0
- package/dist/validate-nested-inline.d.ts +30 -0
- package/dist/validate-nested-inline.d.ts.map +1 -0
- package/dist/validate-nested-inline.js +129 -0
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +117 -160
- package/dist/validate-unused-declarations.d.ts +25 -0
- package/dist/validate-unused-declarations.d.ts.map +1 -0
- package/dist/validate-unused-declarations.js +91 -0
- package/package.json +2 -2
- package/src/analysis-registry.ts +20 -0
- package/src/analyzer.ts +217 -158
- package/src/builtins.ts +25 -0
- package/src/cel-environment.ts +42 -1
- package/src/definition-registry.ts +20 -1
- package/src/dependency-graph.ts +37 -52
- package/src/index.ts +11 -0
- package/src/kernel-globals.ts +22 -1
- package/src/manifest-visitor.ts +251 -0
- package/src/reference-field-map.ts +1 -1
- package/src/schema-compat.ts +38 -2
- package/src/validate-cel-context.ts +50 -0
- package/src/validate-nested-inline.ts +158 -0
- package/src/validate-references.ts +168 -211
- package/src/validate-unused-declarations.ts +95 -0
- package/dist/adapters/http-adapter.d.ts +0 -10
- package/dist/adapters/http-adapter.d.ts.map +0 -1
- package/dist/adapters/http-adapter.js +0 -18
- package/dist/adapters/node-adapter.d.ts +0 -17
- package/dist/adapters/node-adapter.d.ts.map +0 -1
- package/dist/adapters/node-adapter.js +0 -71
- package/dist/adapters/registry-adapter.d.ts +0 -15
- package/dist/adapters/registry-adapter.d.ts.map +0 -1
- package/dist/adapters/registry-adapter.js +0 -53
package/README.md
CHANGED
|
@@ -61,12 +61,12 @@ targets:
|
|
|
61
61
|
kind: Telo.Import
|
|
62
62
|
metadata:
|
|
63
63
|
name: Http
|
|
64
|
-
source: std/http-server@0.
|
|
64
|
+
source: std/http-server@0.5.0
|
|
65
65
|
---
|
|
66
66
|
kind: Telo.Import
|
|
67
67
|
metadata:
|
|
68
68
|
name: Sql
|
|
69
|
-
source: std/sql@0.
|
|
69
|
+
source: std/sql@0.3.0
|
|
70
70
|
---
|
|
71
71
|
# SQLite database — swap driver/host/database for PostgreSQL with zero YAML changes
|
|
72
72
|
kind: Sql.Connection
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ResourceDefinition, ResourceManifest } from "@telorun/sdk";
|
|
2
|
+
import { type ManifestVisitor } from "./manifest-visitor.js";
|
|
2
3
|
import type { AnalysisContext } from "./types.js";
|
|
3
4
|
/**
|
|
4
5
|
* Accumulates type and alias knowledge for a running kernel or analysis session.
|
|
@@ -22,6 +23,17 @@ export declare class AnalysisRegistry {
|
|
|
22
23
|
* sub-schema (e.g. Server.notFoundHandler.returns[].content[mime].encoder).
|
|
23
24
|
*/
|
|
24
25
|
iterateFieldEntries(resource: ResourceManifest, onRef: (fieldPath: string) => void, onScope: (fieldPath: string) => void): void;
|
|
26
|
+
/**
|
|
27
|
+
* Walks a manifest's annotation sites (refs, scopes, schema-from, CEL) via
|
|
28
|
+
* the shared manifest visitor, bound to this registry's definitions and
|
|
29
|
+
* aliases. The public seam for hosts (editor overview graph, tooling) that
|
|
30
|
+
* need the same site discovery the analyzer's own passes use, without
|
|
31
|
+
* reaching into the internal DefinitionRegistry.
|
|
32
|
+
*/
|
|
33
|
+
visitManifest(resources: ResourceManifest[], visitor: ManifestVisitor, opts?: {
|
|
34
|
+
skipKinds?: ReadonlySet<string>;
|
|
35
|
+
expand?: boolean;
|
|
36
|
+
}): void;
|
|
25
37
|
/**
|
|
26
38
|
* Returns the built-in kernel definitions. The underlying DefinitionRegistry already
|
|
27
39
|
* seeds these on construction; this method exposes them so callers (e.g. the kernel's
|
|
@@ -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;
|
|
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;AAElD;;;;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,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,CAAA;KAAE,GAC3D,IAAI;IAQP;;;;OAIG;IACH,kBAAkB,IAAI,kBAAkB,EAAE;IAI1C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAM/D,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;;;;;gEAK4D;IAC5D,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;IA+B7D,qFAAqF;IACrF,QAAQ,IAAI,eAAe;CAG5B"}
|
|
@@ -2,6 +2,7 @@ import { AliasResolver } from "./alias-resolver.js";
|
|
|
2
2
|
import { KERNEL_BUILTINS } from "./builtins.js";
|
|
3
3
|
import { DefinitionRegistry } from "./definition-registry.js";
|
|
4
4
|
import { computeSuggestKind, computeValidUserFacingKinds } from "./kind-suggest.js";
|
|
5
|
+
import { visitManifest as runVisitManifest } from "./manifest-visitor.js";
|
|
5
6
|
import { isRefEntry, isScopeEntry } from "./reference-field-map.js";
|
|
6
7
|
/**
|
|
7
8
|
* Accumulates type and alias knowledge for a running kernel or analysis session.
|
|
@@ -46,6 +47,20 @@ export class AnalysisRegistry {
|
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Walks a manifest's annotation sites (refs, scopes, schema-from, CEL) via
|
|
52
|
+
* the shared manifest visitor, bound to this registry's definitions and
|
|
53
|
+
* aliases. The public seam for hosts (editor overview graph, tooling) that
|
|
54
|
+
* need the same site discovery the analyzer's own passes use, without
|
|
55
|
+
* reaching into the internal DefinitionRegistry.
|
|
56
|
+
*/
|
|
57
|
+
visitManifest(resources, visitor, opts) {
|
|
58
|
+
runVisitManifest(resources, this.defs, visitor, {
|
|
59
|
+
aliases: this.aliases,
|
|
60
|
+
aliasesByModule: this.aliasesByModule,
|
|
61
|
+
...opts,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
49
64
|
/**
|
|
50
65
|
* Returns the built-in kernel definitions. The underlying DefinitionRegistry already
|
|
51
66
|
* seeds these on construction; this method exposes them so callers (e.g. the kernel's
|
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;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAGL,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;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAiB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA+c/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;IAyjBvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAMvB,SAAS,CAAC,SAAS,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,EAAE;IAexF,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,10 +1,11 @@
|
|
|
1
|
-
import { defaultRegistry,
|
|
1
|
+
import { defaultRegistry, isTaggedSentinel } from "@telorun/templating";
|
|
2
2
|
import { AliasResolver } from "./alias-resolver.js";
|
|
3
3
|
import { buildCelEnvironment, buildTypedCelEnvironment, } from "./cel-environment.js";
|
|
4
4
|
import { DefinitionRegistry } from "./definition-registry.js";
|
|
5
5
|
import { buildDependencyGraph, formatCycle } from "./dependency-graph.js";
|
|
6
6
|
import { buildKernelGlobalsSchema, mergeKernelGlobalsIntoContext } from "./kernel-globals.js";
|
|
7
7
|
import { computeSuggestKind } from "./kind-suggest.js";
|
|
8
|
+
import { visitManifest } from "./manifest-visitor.js";
|
|
8
9
|
import { isModuleKind } from "./module-kinds.js";
|
|
9
10
|
import { normalizeInlineResources } from "./normalize-inline-resources.js";
|
|
10
11
|
import { REF_VALIDATION_SKIP_KINDS } from "./system-kinds.js";
|
|
@@ -12,10 +13,12 @@ import { resolveRefSentinels } from "./resolve-ref-sentinels.js";
|
|
|
12
13
|
import { rewriteSyntheticOrigins } from "./rewrite-synthetic-origins.js";
|
|
13
14
|
import { celTypeSatisfiesJsonSchema, substituteCelFields, validateAgainstSchema, } from "./schema-compat.js";
|
|
14
15
|
import { DiagnosticSeverity } from "./types.js";
|
|
15
|
-
import { getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
|
|
16
|
+
import { extractContextsFromSchema, getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
|
|
16
17
|
import { validateExtends } from "./validate-extends.js";
|
|
18
|
+
import { validateNestedInlineResources } from "./validate-nested-inline.js";
|
|
17
19
|
import { validateProviderCoherence } from "./validate-provider-coherence.js";
|
|
18
20
|
import { validateReferences } from "./validate-references.js";
|
|
21
|
+
import { validateUnusedDeclarations } from "./validate-unused-declarations.js";
|
|
19
22
|
import { validateThrowsCoverage } from "./validate-throws-coverage.js";
|
|
20
23
|
const SELF_PREFIX = "Self.";
|
|
21
24
|
/**
|
|
@@ -141,44 +144,6 @@ function manifestRootForResolver(m, defs, aliases, allManifests) {
|
|
|
141
144
|
...(inputs ? { inputType: inputs } : {}),
|
|
142
145
|
};
|
|
143
146
|
}
|
|
144
|
-
/**
|
|
145
|
-
* Walk a JSON Schema tree and collect all `x-telo-context` annotations,
|
|
146
|
-
* returning them as `{ scope, schema }` pairs using JSONPath-style scopes —
|
|
147
|
-
* the same format the analyzer uses for CEL context validation.
|
|
148
|
-
*
|
|
149
|
-
* Result is sorted by scope specificity (longer scope first) so that the
|
|
150
|
-
* per-expression resolver's first-match-wins logic picks the most-specific
|
|
151
|
-
* context. Without this, a broader ancestor scope (e.g. `$.resources[*]`)
|
|
152
|
-
* could shadow a narrower descendant scope whose activation differs.
|
|
153
|
-
*/
|
|
154
|
-
function extractContextsFromSchema(schema, path = "$") {
|
|
155
|
-
const all = collectContexts(schema, path);
|
|
156
|
-
return all.sort((a, b) => b.scope.length - a.scope.length);
|
|
157
|
-
}
|
|
158
|
-
function collectContexts(schema, path) {
|
|
159
|
-
if (!schema || typeof schema !== "object")
|
|
160
|
-
return [];
|
|
161
|
-
const results = [];
|
|
162
|
-
if (schema["x-telo-context"]) {
|
|
163
|
-
results.push({ scope: path, schema: schema["x-telo-context"] });
|
|
164
|
-
}
|
|
165
|
-
if (schema.properties) {
|
|
166
|
-
for (const [key, value] of Object.entries(schema.properties)) {
|
|
167
|
-
results.push(...collectContexts(value, `${path}.${key}`));
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
if (schema.items && typeof schema.items === "object") {
|
|
171
|
-
results.push(...collectContexts(schema.items, `${path}[*]`));
|
|
172
|
-
}
|
|
173
|
-
for (const key of ["oneOf", "anyOf", "allOf"]) {
|
|
174
|
-
if (Array.isArray(schema[key])) {
|
|
175
|
-
for (const subschema of schema[key]) {
|
|
176
|
-
results.push(...collectContexts(subschema, path));
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
return results;
|
|
181
|
-
}
|
|
182
147
|
/** Resolve a local `$ref` (only `#/$defs/<name>` form) against the root schema.
|
|
183
148
|
* Non-refs and unresolved refs pass through unchanged. */
|
|
184
149
|
function resolveLocalRef(schema, root) {
|
|
@@ -346,19 +311,30 @@ const CEL_PURE_RE = /^\s*\$\{\{[^}]*\}\}\s*$/;
|
|
|
346
311
|
const CEL_EXPR_RE = /\$\{\{\s*([^}]+?)\s*\}\}/;
|
|
347
312
|
/** Recursively walk `data`+`schema` together, type-checking every pure CEL template
|
|
348
313
|
* string via `env.check()`. Returns `SchemaIssue[]` for any type mismatches found. */
|
|
349
|
-
function collectCelTypeIssues(data, schema, path, definition, manifest, baseTypedEnv, rootEnv) {
|
|
314
|
+
function collectCelTypeIssues(data, schema, path, definition, manifest, baseTypedEnv, rootEnv, rootModuleManifest) {
|
|
350
315
|
const issues = [];
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
316
|
+
// A pure CEL value type-checks the same regardless of surface form: a
|
|
317
|
+
// `${{ … }}` string and a `!cel`-tagged sentinel must behave identically.
|
|
318
|
+
let celExpr;
|
|
319
|
+
if (isTaggedSentinel(data)) {
|
|
320
|
+
// Non-CEL engines (e.g. `!literal`) are analyzed by their own engine pass.
|
|
321
|
+
if (data.engine !== "cel")
|
|
322
|
+
return issues;
|
|
323
|
+
celExpr = data.source;
|
|
324
|
+
}
|
|
325
|
+
else if (typeof data === "string" && CEL_PURE_RE.test(data)) {
|
|
326
|
+
celExpr = data.match(CEL_EXPR_RE)?.[1]?.trim();
|
|
327
|
+
}
|
|
328
|
+
if (celExpr !== undefined) {
|
|
329
|
+
{
|
|
330
|
+
const expr = celExpr;
|
|
355
331
|
// Merge x-telo-context variables for this path if applicable
|
|
356
332
|
let typedEnv = baseTypedEnv;
|
|
357
333
|
if (definition.schema) {
|
|
358
334
|
for (const ctx of extractContextsFromSchema(definition.schema)) {
|
|
359
335
|
if (!pathMatchesScope(path, ctx.scope))
|
|
360
336
|
continue;
|
|
361
|
-
typedEnv = buildTypedCelEnvironment(rootEnv, manifest, ctx.schema);
|
|
337
|
+
typedEnv = buildTypedCelEnvironment(rootEnv, manifest, ctx.schema, rootModuleManifest);
|
|
362
338
|
break;
|
|
363
339
|
}
|
|
364
340
|
}
|
|
@@ -381,8 +357,9 @@ function collectCelTypeIssues(data, schema, path, definition, manifest, baseType
|
|
|
381
357
|
else if (checkResult?.valid && checkResult.type && schema) {
|
|
382
358
|
const celType = checkResult.type.split("<")[0];
|
|
383
359
|
if (!celTypeSatisfiesJsonSchema(celType, schema)) {
|
|
360
|
+
const expected = schema["x-telo-type"] ?? schema.type ?? "unknown";
|
|
384
361
|
issues.push({
|
|
385
|
-
message: `CEL returns '${checkResult.type}' but field expects '${
|
|
362
|
+
message: `CEL returns '${checkResult.type}' but field expects '${expected}'`,
|
|
386
363
|
path,
|
|
387
364
|
});
|
|
388
365
|
}
|
|
@@ -393,13 +370,13 @@ function collectCelTypeIssues(data, schema, path, definition, manifest, baseType
|
|
|
393
370
|
if (Array.isArray(data)) {
|
|
394
371
|
const itemSchema = (schema.items ?? {});
|
|
395
372
|
for (let i = 0; i < data.length; i++) {
|
|
396
|
-
issues.push(...collectCelTypeIssues(data[i], itemSchema, `${path}[${i}]`, definition, manifest, baseTypedEnv, rootEnv));
|
|
373
|
+
issues.push(...collectCelTypeIssues(data[i], itemSchema, `${path}[${i}]`, definition, manifest, baseTypedEnv, rootEnv, rootModuleManifest));
|
|
397
374
|
}
|
|
398
375
|
}
|
|
399
376
|
else if (data !== null && typeof data === "object") {
|
|
400
377
|
const props = (schema.properties ?? {});
|
|
401
378
|
for (const [k, v] of Object.entries(data)) {
|
|
402
|
-
issues.push(...collectCelTypeIssues(v, (props[k] ?? {}), path ? `${path}.${k}` : k, definition, manifest, baseTypedEnv, rootEnv));
|
|
379
|
+
issues.push(...collectCelTypeIssues(v, (props[k] ?? {}), path ? `${path}.${k}` : k, definition, manifest, baseTypedEnv, rootEnv, rootModuleManifest));
|
|
403
380
|
}
|
|
404
381
|
}
|
|
405
382
|
return issues;
|
|
@@ -563,6 +540,34 @@ export class StaticAnalyzer {
|
|
|
563
540
|
byName.set(m.metadata.name, m);
|
|
564
541
|
}
|
|
565
542
|
}
|
|
543
|
+
// Fail loud on definition schemas AJV cannot compile. `validateAgainstSchema`
|
|
544
|
+
// and `validateWithRefs` swallow compile failures (returning no issues),
|
|
545
|
+
// which would silently skip schema validation for every resource of that
|
|
546
|
+
// kind — surface the broken schema once, anchored on the definition itself.
|
|
547
|
+
for (const m of allManifests) {
|
|
548
|
+
if (m.kind !== "Telo.Definition" && m.kind !== "Telo.Abstract")
|
|
549
|
+
continue;
|
|
550
|
+
const schema = m.schema;
|
|
551
|
+
if (!schema || typeof schema !== "object")
|
|
552
|
+
continue;
|
|
553
|
+
const name = m.metadata?.name;
|
|
554
|
+
if (!name)
|
|
555
|
+
continue;
|
|
556
|
+
const compileError = defs.schemaCompileError(schema);
|
|
557
|
+
if (compileError) {
|
|
558
|
+
diagnostics.push({
|
|
559
|
+
severity: DiagnosticSeverity.Error,
|
|
560
|
+
code: "SCHEMA_COMPILE_ERROR",
|
|
561
|
+
source: SOURCE,
|
|
562
|
+
message: `${m.kind}/${name}: definition schema failed to compile: ${compileError}`,
|
|
563
|
+
data: {
|
|
564
|
+
resource: { kind: m.kind, name },
|
|
565
|
+
filePath: m.metadata?.source,
|
|
566
|
+
path: "schema",
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
}
|
|
566
571
|
// Library env: rejection — `env:` on a Library `variables` / `secrets`
|
|
567
572
|
// entry is forbidden. The Library entry schema is otherwise open so that
|
|
568
573
|
// any JSON Schema property schema is valid; this targeted check produces
|
|
@@ -596,6 +601,11 @@ export class StaticAnalyzer {
|
|
|
596
601
|
// Build typed kernel globals schema so x-telo-context chain validation
|
|
597
602
|
// recognises variables, secrets, resources, env automatically
|
|
598
603
|
const kernelGlobals = buildKernelGlobalsSchema(allManifests);
|
|
604
|
+
// The module doc (Application/Library) carries the Application-only `ports`
|
|
605
|
+
// namespace; threaded into per-resource CEL typing so `${{ ports.X }}`
|
|
606
|
+
// resolves its nominal brand cross-doc.
|
|
607
|
+
const moduleManifest = allManifests.find((mm) => mm.kind === "Telo.Application") ??
|
|
608
|
+
allManifests.find((mm) => mm.kind === "Telo.Library");
|
|
599
609
|
// Validate each non-definition, non-system resource
|
|
600
610
|
for (const m of allManifests) {
|
|
601
611
|
const filePath = m.metadata?.source;
|
|
@@ -649,8 +659,8 @@ export class StaticAnalyzer {
|
|
|
649
659
|
}
|
|
650
660
|
: definition.schema;
|
|
651
661
|
// Phase 1: CEL type checking — walk data+schema together, check env.check() return types
|
|
652
|
-
const baseTypedEnv = buildTypedCelEnvironment(this.celEnv, m);
|
|
653
|
-
const celIssues = collectCelTypeIssues(m, schema, "", definition, m, baseTypedEnv, this.celEnv);
|
|
662
|
+
const baseTypedEnv = buildTypedCelEnvironment(this.celEnv, m, undefined, moduleManifest);
|
|
663
|
+
const celIssues = collectCelTypeIssues(m, schema, "", definition, m, baseTypedEnv, this.celEnv, moduleManifest);
|
|
654
664
|
// Phase 2+3: AJV on substituted data — CEL fields replaced with typed placeholders
|
|
655
665
|
const ajvIssues = validateAgainstSchema(substituteCelFields(m, schema), schema);
|
|
656
666
|
const issues = [...celIssues, ...ajvIssues];
|
|
@@ -664,6 +674,33 @@ export class StaticAnalyzer {
|
|
|
664
674
|
});
|
|
665
675
|
}
|
|
666
676
|
}
|
|
677
|
+
// Validate inline resources nested inside this resource's body (e.g. a
|
|
678
|
+
// Run.Sequence step's `invoke: { kind, ...config }`). These sit at
|
|
679
|
+
// x-telo-ref slots reached only through local `$ref`s, which the
|
|
680
|
+
// reference field map intentionally does not follow, so they escape both
|
|
681
|
+
// inline-extraction and the per-resource schema check above.
|
|
682
|
+
if (definition.schema) {
|
|
683
|
+
// Resolve inline kinds in the parent resource's scope: direct kind
|
|
684
|
+
// first, then the parent module's own aliases (for resources declared
|
|
685
|
+
// inside an imported module), then the root aliases. Mirrors how the
|
|
686
|
+
// analyzer resolves kinds elsewhere so module-scoped aliases don't
|
|
687
|
+
// produce false UNDEFINED_KIND diagnostics.
|
|
688
|
+
const ownModule = m.metadata?.module;
|
|
689
|
+
const scopeResolver = ownModule && !rootModules.has(ownModule) ? aliasesByModule.get(ownModule) : undefined;
|
|
690
|
+
diagnostics.push(...validateNestedInlineResources(m, definition.schema, (kind) => {
|
|
691
|
+
const direct = defs.resolve(kind);
|
|
692
|
+
if (direct)
|
|
693
|
+
return direct;
|
|
694
|
+
const viaScope = scopeResolver?.resolveKind(kind);
|
|
695
|
+
if (viaScope) {
|
|
696
|
+
const scoped = defs.resolve(viaScope);
|
|
697
|
+
if (scoped)
|
|
698
|
+
return scoped;
|
|
699
|
+
}
|
|
700
|
+
const viaRoot = aliases.resolveKind(kind);
|
|
701
|
+
return viaRoot ? defs.resolve(viaRoot) : undefined;
|
|
702
|
+
}));
|
|
703
|
+
}
|
|
667
704
|
// (Invocation context compatibility check is handled via x-telo-context in the CEL pass below)
|
|
668
705
|
}
|
|
669
706
|
// Template-body structural validations: check that template entry-points produce
|
|
@@ -735,42 +772,39 @@ export class StaticAnalyzer {
|
|
|
735
772
|
}
|
|
736
773
|
}
|
|
737
774
|
}
|
|
738
|
-
// Validate CEL syntax and context variable access in all manifests
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
if (!matchedContext)
|
|
766
|
-
matchedContext = invocationContext;
|
|
767
|
-
if (stepContextSchema) {
|
|
775
|
+
// Validate CEL syntax and context variable access in all manifests. The
|
|
776
|
+
// walker discovers every compiled CEL node by scanning the value tree and
|
|
777
|
+
// hands back the `x-telo-context` schema matched at the enclosing path; the
|
|
778
|
+
// per-path resolution (step context, kernel-globals merge, x-telo-context-*
|
|
779
|
+
// annotation resolution) stays here because it depends on analyzer-internal
|
|
780
|
+
// state (definitions, aliases, the typed CEL env).
|
|
781
|
+
// Per-resource state computed at enter and read by that resource's CEL
|
|
782
|
+
// sites. The manifest / resource / filePath come straight off each CelSite's
|
|
783
|
+
// `source` (no need to capture them); only the derived step / invocation
|
|
784
|
+
// context — which require analyzer state to build — are stashed here.
|
|
785
|
+
let celStepContextSchema;
|
|
786
|
+
let celInvocationContext;
|
|
787
|
+
visitManifest(allManifests, defs, {
|
|
788
|
+
onResourceEnter: (e) => {
|
|
789
|
+
const m = e.source;
|
|
790
|
+
celInvocationContext = m.metadata?.xTeloInvocationContext;
|
|
791
|
+
celStepContextSchema = e.definition?.schema
|
|
792
|
+
? buildStepContextSchema(m, e.definition.schema, allManifests, defs, aliases)
|
|
793
|
+
: undefined;
|
|
794
|
+
},
|
|
795
|
+
onCel: (e) => {
|
|
796
|
+
const m = e.source;
|
|
797
|
+
const resource = { kind: m.kind, name: m.metadata?.name };
|
|
798
|
+
const filePath = m.metadata?.source;
|
|
799
|
+
const { expr, path, engineName, matchedScope } = e;
|
|
800
|
+
let matchedContext = e.contextSchema ?? celInvocationContext;
|
|
801
|
+
if (celStepContextSchema) {
|
|
768
802
|
const base = matchedContext ?? { type: "object", properties: {}, additionalProperties: true };
|
|
769
803
|
matchedContext = {
|
|
770
804
|
...base,
|
|
771
805
|
properties: {
|
|
772
806
|
...(base.properties ?? {}),
|
|
773
|
-
steps:
|
|
807
|
+
steps: celStepContextSchema,
|
|
774
808
|
},
|
|
775
809
|
};
|
|
776
810
|
}
|
|
@@ -789,8 +823,18 @@ export class StaticAnalyzer {
|
|
|
789
823
|
effectiveContext = mergeKernelGlobalsIntoContext(resolvedContext, kernelGlobals);
|
|
790
824
|
}
|
|
791
825
|
const engine = defaultRegistry().get(engineName);
|
|
792
|
-
if (!engine)
|
|
826
|
+
if (!engine) {
|
|
827
|
+
// No registered engine owns this tag — the expression would go
|
|
828
|
+
// entirely unanalyzed. Surface it rather than skipping silently.
|
|
829
|
+
diagnostics.push({
|
|
830
|
+
severity: DiagnosticSeverity.Error,
|
|
831
|
+
code: "UNKNOWN_ENGINE",
|
|
832
|
+
source: SOURCE,
|
|
833
|
+
message: `${m.kind}/${resource.name}: no templating engine registered for '!${engineName}' at '${path}'.`,
|
|
834
|
+
data: { resource, filePath, path },
|
|
835
|
+
});
|
|
793
836
|
return;
|
|
837
|
+
}
|
|
794
838
|
const findings = engine.analyze(expr, { celEnv: this.celEnv, contextSchema: effectiveContext });
|
|
795
839
|
for (const f of findings) {
|
|
796
840
|
if (f.code === "CEL_SYNTAX_ERROR") {
|
|
@@ -824,8 +868,8 @@ export class StaticAnalyzer {
|
|
|
824
868
|
});
|
|
825
869
|
}
|
|
826
870
|
}
|
|
827
|
-
}
|
|
828
|
-
}
|
|
871
|
+
},
|
|
872
|
+
}, { aliases });
|
|
829
873
|
// Validate resource references (Phase 3)
|
|
830
874
|
diagnostics.push(...validateReferences(allManifests, { aliases, definitions: defs, aliasesByModule }));
|
|
831
875
|
// Validate `extends` fields and flag legacy `capability: <UserAbstract>` overload.
|
|
@@ -834,6 +878,8 @@ export class StaticAnalyzer {
|
|
|
834
878
|
diagnostics.push(...validateProviderCoherence(allManifests, defs, aliases));
|
|
835
879
|
// Validate throws: declarations and catches: coverage (rules 1, 2, 4, 7)
|
|
836
880
|
diagnostics.push(...validateThrowsCoverage(allManifests, defs, aliases, this.celEnv));
|
|
881
|
+
// Warn about declared variables / secrets / ports that no CEL references.
|
|
882
|
+
diagnostics.push(...validateUnusedDeclarations(allManifests, this.celEnv));
|
|
837
883
|
// Reroute diagnostics on synthetic (inline-extracted) resources back to
|
|
838
884
|
// the chain root so position-index lookups land on the parent doc.
|
|
839
885
|
return rewriteSyntheticOrigins(diagnostics, allManifests);
|
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,EA0V/C,CAAC"}
|
package/dist/builtins.js
CHANGED
|
@@ -277,6 +277,31 @@ export const KERNEL_BUILTINS = [
|
|
|
277
277
|
},
|
|
278
278
|
},
|
|
279
279
|
},
|
|
280
|
+
// Inbound ports the Application listens on. A name-keyed map mirroring
|
|
281
|
+
// `variables`: each entry binds a host env var (`env:`) that supplies a
|
|
282
|
+
// port integer (implicitly typed `integer`, 1–65535), with an optional
|
|
283
|
+
// `default:` used when the env var is unset. `protocol:` (default `tcp`)
|
|
284
|
+
// selects the transport — the runner reads this list to know the
|
|
285
|
+
// exposed ports before launch, and the analyzer brands the resolved
|
|
286
|
+
// `ports.<name>` value (tcp → TcpPort, udp → UdpPort) for static wiring
|
|
287
|
+
// checks. Application-only. See kernel/nodejs/src/application-env.ts.
|
|
288
|
+
ports: {
|
|
289
|
+
type: "object",
|
|
290
|
+
additionalProperties: {
|
|
291
|
+
type: "object",
|
|
292
|
+
required: ["env"],
|
|
293
|
+
properties: {
|
|
294
|
+
env: { type: "string" },
|
|
295
|
+
protocol: {
|
|
296
|
+
type: "string",
|
|
297
|
+
enum: ["tcp", "udp"],
|
|
298
|
+
default: "tcp",
|
|
299
|
+
},
|
|
300
|
+
default: { type: "integer", minimum: 1, maximum: 65535 },
|
|
301
|
+
},
|
|
302
|
+
additionalProperties: false,
|
|
303
|
+
},
|
|
304
|
+
},
|
|
280
305
|
},
|
|
281
306
|
required: ["metadata"],
|
|
282
307
|
additionalProperties: false,
|
|
@@ -12,5 +12,5 @@ export type { CelHandlers } from "@telorun/templating";
|
|
|
12
12
|
*
|
|
13
13
|
* NOTE: The set of kernel globals registered here must match `KERNEL_GLOBAL_NAMES`
|
|
14
14
|
* in kernel-globals.ts, which is used for chain-access validation. */
|
|
15
|
-
export declare function buildTypedCelEnvironment(baseEnv: Environment, manifest: ResourceManifest, extraContextSchema?: Record<string, any> | null): Environment;
|
|
15
|
+
export declare function buildTypedCelEnvironment(baseEnv: Environment, manifest: ResourceManifest, extraContextSchema?: Record<string, any> | null, rootModuleManifest?: ResourceManifest): Environment;
|
|
16
16
|
//# sourceMappingURL=cel-environment.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cel-environment.d.ts","sourceRoot":"","sources":["../src/cel-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"cel-environment.d.ts","sourceRoot":"","sources":["../src/cel-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAUrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;uEASuE;AACvE,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAI/C,kBAAkB,CAAC,EAAE,gBAAgB,GACpC,WAAW,CAuEb"}
|
package/dist/cel-environment.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import { jsonSchemaToCelType } from "./schema-compat.js";
|
|
1
|
+
import { jsonSchemaToCelType, VALUE_BRAND_BASE } from "./schema-compat.js";
|
|
2
|
+
/** Transport protocol on a `ports` entry → the nominal CEL brand its resolved
|
|
3
|
+
* value carries. Mirrors the `protocol` enum in the Application schema. */
|
|
4
|
+
const PORT_PROTOCOL_BRAND = {
|
|
5
|
+
tcp: "TcpPort",
|
|
6
|
+
udp: "UdpPort",
|
|
7
|
+
};
|
|
2
8
|
export { buildCelEnvironment } from "@telorun/templating";
|
|
3
9
|
/** Clone `baseEnv` and register typed variable declarations so that
|
|
4
10
|
* `env.check(expr)` can infer return types for expressions referencing known variables.
|
|
@@ -10,9 +16,21 @@ export { buildCelEnvironment } from "@telorun/templating";
|
|
|
10
16
|
*
|
|
11
17
|
* NOTE: The set of kernel globals registered here must match `KERNEL_GLOBAL_NAMES`
|
|
12
18
|
* in kernel-globals.ts, which is used for chain-access validation. */
|
|
13
|
-
export function buildTypedCelEnvironment(baseEnv, manifest, extraContextSchema
|
|
19
|
+
export function buildTypedCelEnvironment(baseEnv, manifest, extraContextSchema,
|
|
20
|
+
// The `ports` namespace is Application-only and lives on the module doc, not
|
|
21
|
+
// on the resource being analyzed. When validating a resource, the caller
|
|
22
|
+
// passes the module manifest here so `${{ ports.X }}` types cross-doc.
|
|
23
|
+
rootModuleManifest) {
|
|
14
24
|
try {
|
|
15
25
|
const env = baseEnv.clone();
|
|
26
|
+
// Register nominal value brands (TcpPort/UdpPort/…) on the *clone* so the
|
|
27
|
+
// type-checker can distinguish structurally-identical values. The base env
|
|
28
|
+
// (shared with the kernel runtime) is untouched — a branded value flows as
|
|
29
|
+
// a plain integer at runtime, so only static checking needs these. cel-js
|
|
30
|
+
// auto-generates a field-less wrapper class; no runtime constructor needed.
|
|
31
|
+
for (const brand of Object.keys(VALUE_BRAND_BASE)) {
|
|
32
|
+
env.registerType(brand, { fields: {} });
|
|
33
|
+
}
|
|
16
34
|
// Build typed ObjectSchema from manifest.variables if it looks like a schema map
|
|
17
35
|
const vars = manifest.variables;
|
|
18
36
|
if (vars !== null && typeof vars === "object" && !Array.isArray(vars)) {
|
|
@@ -31,6 +49,26 @@ export function buildTypedCelEnvironment(baseEnv, manifest, extraContextSchema)
|
|
|
31
49
|
else {
|
|
32
50
|
env.registerVariable("variables", "map");
|
|
33
51
|
}
|
|
52
|
+
// `ports` namespace: each entry types as the brand its `protocol` selects
|
|
53
|
+
// (tcp → TcpPort, udp → UdpPort), so `${{ ports.http }}` carries a nominal
|
|
54
|
+
// type that consuming fields can check against.
|
|
55
|
+
const portsManifest = (rootModuleManifest ?? manifest).ports;
|
|
56
|
+
if (portsManifest !== null && typeof portsManifest === "object" && !Array.isArray(portsManifest)) {
|
|
57
|
+
const portEntries = Object.entries(portsManifest).filter(([, v]) => v !== null && typeof v === "object" && !Array.isArray(v));
|
|
58
|
+
if (portEntries.length > 0) {
|
|
59
|
+
const schema = {};
|
|
60
|
+
for (const [k, v] of portEntries) {
|
|
61
|
+
schema[k] = PORT_PROTOCOL_BRAND[v.protocol ?? "tcp"] ?? "int";
|
|
62
|
+
}
|
|
63
|
+
env.registerVariable({ name: "ports", schema });
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
env.registerVariable("ports", "map");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
env.registerVariable("ports", "map");
|
|
71
|
+
}
|
|
34
72
|
env.registerVariable("secrets", "map");
|
|
35
73
|
env.registerVariable("resources", "map");
|
|
36
74
|
env.registerVariable("env", "map");
|
|
@@ -31,8 +31,19 @@ export declare class DefinitionRegistry {
|
|
|
31
31
|
* Returns undefined when the module identity is not yet registered. */
|
|
32
32
|
computeId(moduleName: string, typeName: string): string | undefined;
|
|
33
33
|
/** Validates data against a schema using this registry's AJV instance, which has all
|
|
34
|
-
* registered definition schemas loaded — enabling cross-module $ref resolution.
|
|
34
|
+
* registered definition schemas loaded — enabling cross-module $ref resolution.
|
|
35
|
+
* A compile failure returns `[]` here; it is surfaced loudly (once, on the
|
|
36
|
+
* owning definition) by `schemaCompileError` via the analyzer's
|
|
37
|
+
* definition-schema compile check, so resources are never silently skipped. */
|
|
35
38
|
validateWithRefs(data: unknown, schema: Record<string, any>): string[];
|
|
39
|
+
/** Returns the AJV compile error for `schema`, or `undefined` when it compiles.
|
|
40
|
+
* Compiles on this registry's instance, which has every loaded module schema
|
|
41
|
+
* plus the manifest root registered, so local `#/$defs`, `telo://manifest`,
|
|
42
|
+
* and cross-module `$ref`s all resolve. Used to fail loud on a definition
|
|
43
|
+
* schema that AJV cannot compile — otherwise `validateAgainstSchema` /
|
|
44
|
+
* `validateWithRefs` would swallow the failure and silently skip every
|
|
45
|
+
* resource of that kind. */
|
|
46
|
+
schemaCompileError(schema: Record<string, any>): string | undefined;
|
|
36
47
|
private tryRegisterSchema;
|
|
37
48
|
/** Resolves an x-telo-ref string to a canonical registry kind key.
|
|
38
49
|
* Splits on "#", looks up the left side in the identity table, and returns
|
|
@@ -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;AAGlC,+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;IAiC9C,OAAO,CAAC,aAAa;IASrB;;;yFAGqF;IACrF,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IA+B1E;4EACwE;IACxE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMnE;
|
|
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;AAGlC,+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;IAiC9C,OAAO,CAAC,aAAa;IASrB;;;yFAGqF;IACrF,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IA+B1E;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,+FAA+F;IAC/F,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIxD,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;IAuBhC,OAAO,CAAC,uBAAuB;IA+B/B;;qEAEiE;IACjE,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAgBxD,KAAK,IAAI,MAAM,EAAE;CAGlB"}
|
|
@@ -105,7 +105,10 @@ export class DefinitionRegistry {
|
|
|
105
105
|
return `${identity}/${typeName}`;
|
|
106
106
|
}
|
|
107
107
|
/** Validates data against a schema using this registry's AJV instance, which has all
|
|
108
|
-
* registered definition schemas loaded — enabling cross-module $ref resolution.
|
|
108
|
+
* registered definition schemas loaded — enabling cross-module $ref resolution.
|
|
109
|
+
* A compile failure returns `[]` here; it is surfaced loudly (once, on the
|
|
110
|
+
* owning definition) by `schemaCompileError` via the analyzer's
|
|
111
|
+
* definition-schema compile check, so resources are never silently skipped. */
|
|
109
112
|
validateWithRefs(data, schema) {
|
|
110
113
|
let validate;
|
|
111
114
|
try {
|
|
@@ -118,6 +121,22 @@ export class DefinitionRegistry {
|
|
|
118
121
|
return [];
|
|
119
122
|
return (validate.errors ?? []).map(formatSingleError);
|
|
120
123
|
}
|
|
124
|
+
/** Returns the AJV compile error for `schema`, or `undefined` when it compiles.
|
|
125
|
+
* Compiles on this registry's instance, which has every loaded module schema
|
|
126
|
+
* plus the manifest root registered, so local `#/$defs`, `telo://manifest`,
|
|
127
|
+
* and cross-module `$ref`s all resolve. Used to fail loud on a definition
|
|
128
|
+
* schema that AJV cannot compile — otherwise `validateAgainstSchema` /
|
|
129
|
+
* `validateWithRefs` would swallow the failure and silently skip every
|
|
130
|
+
* resource of that kind. */
|
|
131
|
+
schemaCompileError(schema) {
|
|
132
|
+
try {
|
|
133
|
+
this.ajv.compile(schema);
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
return err instanceof Error ? err.message : String(err);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
121
140
|
tryRegisterSchema(moduleName, typeName, schema) {
|
|
122
141
|
const id = this.computeId(moduleName, typeName);
|
|
123
142
|
if (!id || this.registeredSchemaIds.has(id))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../src/dependency-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAInE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B;kDAC8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACpC;oFACgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACrC;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,eAAe,
|
|
1
|
+
{"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../src/dependency-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAInE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B;kDAC8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACpC;oFACgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACrC;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,eAAe,CAmGjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,MAAM,CAOtE"}
|