@telorun/analyzer 0.11.0 → 0.12.1
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/LICENSE +2 -2
- package/README.md +3 -3
- package/dist/analysis-registry.d.ts +7 -0
- package/dist/analysis-registry.d.ts.map +1 -1
- package/dist/analysis-registry.js +38 -0
- package/dist/analyzer.d.ts +15 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +181 -11
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +58 -1
- package/dist/definition-registry.d.ts +12 -1
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +36 -1
- package/dist/dependency-graph.d.ts.map +1 -1
- package/dist/dependency-graph.js +27 -13
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/kernel-globals.d.ts.map +1 -1
- package/dist/kernel-globals.js +9 -11
- package/dist/manifest-loader.d.ts +23 -1
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +66 -3
- package/dist/normalize-inline-resources.d.ts.map +1 -1
- package/dist/normalize-inline-resources.js +26 -14
- package/dist/position-metadata.d.ts +11 -2
- package/dist/position-metadata.d.ts.map +1 -1
- package/dist/position-metadata.js +18 -3
- package/dist/precompile.d.ts.map +1 -1
- package/dist/precompile.js +9 -1
- package/dist/reference-field-map.d.ts +22 -4
- package/dist/reference-field-map.d.ts.map +1 -1
- package/dist/reference-field-map.js +94 -26
- package/dist/residual-schema.d.ts +23 -0
- package/dist/residual-schema.d.ts.map +1 -0
- package/dist/residual-schema.js +45 -0
- package/dist/resolve-ref-sentinels.d.ts +27 -0
- package/dist/resolve-ref-sentinels.d.ts.map +1 -0
- package/dist/resolve-ref-sentinels.js +114 -0
- package/dist/rewrite-synthetic-origins.d.ts +10 -0
- package/dist/rewrite-synthetic-origins.d.ts.map +1 -0
- package/dist/rewrite-synthetic-origins.js +55 -0
- package/dist/schema-compat.d.ts +11 -1
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +25 -4
- package/dist/system-kinds.d.ts +25 -0
- package/dist/system-kinds.d.ts.map +1 -0
- package/dist/system-kinds.js +34 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validate-cel-context.d.ts +5 -0
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +27 -15
- 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-provider-coherence.d.ts +23 -0
- package/dist/validate-provider-coherence.d.ts.map +1 -0
- package/dist/validate-provider-coherence.js +148 -0
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +141 -36
- package/dist/with-synthetic-positions.d.ts +28 -0
- package/dist/with-synthetic-positions.d.ts.map +1 -0
- package/dist/with-synthetic-positions.js +45 -0
- package/package.json +7 -4
- package/src/analysis-registry.ts +37 -0
- package/src/analyzer.ts +190 -13
- package/src/builtins.ts +58 -1
- package/src/definition-registry.ts +35 -1
- package/src/dependency-graph.ts +27 -14
- package/src/index.ts +2 -0
- package/src/kernel-globals.ts +9 -11
- package/src/manifest-loader.ts +69 -4
- package/src/normalize-inline-resources.ts +48 -13
- package/src/position-metadata.ts +18 -3
- package/src/precompile.ts +8 -1
- package/src/reference-field-map.ts +130 -25
- package/src/residual-schema.ts +49 -0
- package/src/resolve-ref-sentinels.ts +127 -0
- package/src/rewrite-synthetic-origins.ts +75 -0
- package/src/schema-compat.ts +25 -4
- package/src/system-kinds.ts +37 -0
- package/src/types.ts +12 -0
- package/src/validate-cel-context.ts +28 -15
- package/src/validate-nested-inline.ts +158 -0
- package/src/validate-provider-coherence.ts +166 -0
- package/src/validate-references.ts +138 -35
- package/src/with-synthetic-positions.ts +48 -0
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SUSTAINABLE USE LICENSE (Fair-code)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 CodeNet Sp. z o.o.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify, and distribute the Software for any purpose—including commercial purposes—subject to the following conditions:
|
|
6
6
|
|
|
@@ -14,4 +14,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
|
14
14
|
|
|
15
15
|
5. DISCLAIMER: The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the Software or the use or other dealings in the Software.
|
|
16
16
|
|
|
17
|
-
For commercial licensing, managed hosting exemptions, or enterprise inquiries, please contact
|
|
17
|
+
For commercial licensing, managed hosting exemptions, or enterprise inquiries, please contact <contact@codenet.pl>.
|
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="
|
|
2
|
+
<img src="https://raw.githubusercontent.com/telorun/telo/main/assets/telo.png" alt="Telo" width="200" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">Telo</h1>
|
|
@@ -61,12 +61,12 @@ targets:
|
|
|
61
61
|
kind: Telo.Import
|
|
62
62
|
metadata:
|
|
63
63
|
name: Http
|
|
64
|
-
source:
|
|
64
|
+
source: std/http-server@0.5.0
|
|
65
65
|
---
|
|
66
66
|
kind: Telo.Import
|
|
67
67
|
metadata:
|
|
68
68
|
name: Sql
|
|
69
|
-
source:
|
|
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
|
|
@@ -41,6 +41,13 @@ export declare class AnalysisRegistry {
|
|
|
41
41
|
/** Returns the closest user-facing kind to `badKind`, or undefined when nothing
|
|
42
42
|
* is close enough (or multiple candidates tie). Case-sensitive. */
|
|
43
43
|
suggestKind(badKind: string): string | undefined;
|
|
44
|
+
/** Returns every user-facing (alias-form) kind that satisfies the given
|
|
45
|
+
* `x-telo-ref` constraint string (e.g. `"telo#Invocable"`, `"std/sql#Connection"`).
|
|
46
|
+
* Resolution mirrors `validateReferences.checkKind`: abstract targets expand to
|
|
47
|
+
* the set of definitions extending them; concrete targets yield just themselves.
|
|
48
|
+
* Returns `undefined` when the ref can't be resolved (e.g. unregistered identity),
|
|
49
|
+
* so callers can fall back to the unfiltered kind list. */
|
|
50
|
+
userFacingKindsForRef(xTeloRef: string): string[] | undefined;
|
|
44
51
|
/** @internal Bridge for StaticAnalyzer — do not use outside the analyzer package. */
|
|
45
52
|
_context(): AnalysisContext;
|
|
46
53
|
}
|
|
@@ -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;AAMzE,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;;;;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,qFAAqF;IACrF,QAAQ,IAAI,eAAe;CAG5B"}
|
|
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;AAMzE,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;;;;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"}
|
|
@@ -79,6 +79,44 @@ export class AnalysisRegistry {
|
|
|
79
79
|
suggestKind(badKind) {
|
|
80
80
|
return computeSuggestKind(badKind, this.aliases, this.defs);
|
|
81
81
|
}
|
|
82
|
+
/** Returns every user-facing (alias-form) kind that satisfies the given
|
|
83
|
+
* `x-telo-ref` constraint string (e.g. `"telo#Invocable"`, `"std/sql#Connection"`).
|
|
84
|
+
* Resolution mirrors `validateReferences.checkKind`: abstract targets expand to
|
|
85
|
+
* the set of definitions extending them; concrete targets yield just themselves.
|
|
86
|
+
* Returns `undefined` when the ref can't be resolved (e.g. unregistered identity),
|
|
87
|
+
* so callers can fall back to the unfiltered kind list. */
|
|
88
|
+
userFacingKindsForRef(xTeloRef) {
|
|
89
|
+
const targetKind = this.defs.resolveRef(xTeloRef);
|
|
90
|
+
if (!targetKind)
|
|
91
|
+
return undefined;
|
|
92
|
+
const targetDef = this.defs.resolve(targetKind);
|
|
93
|
+
if (!targetDef)
|
|
94
|
+
return undefined;
|
|
95
|
+
const canonicalKinds = [];
|
|
96
|
+
if (targetDef.kind === "Telo.Abstract") {
|
|
97
|
+
for (const def of this.defs.getByExtends(targetKind)) {
|
|
98
|
+
const module = def.metadata?.module;
|
|
99
|
+
if (module && def.metadata?.name) {
|
|
100
|
+
canonicalKinds.push(`${module}.${def.metadata.name}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
canonicalKinds.push(targetKind);
|
|
106
|
+
}
|
|
107
|
+
const out = new Set();
|
|
108
|
+
for (const kind of canonicalKinds) {
|
|
109
|
+
const dot = kind.indexOf(".");
|
|
110
|
+
if (dot === -1)
|
|
111
|
+
continue;
|
|
112
|
+
const moduleName = kind.slice(0, dot);
|
|
113
|
+
const typeName = kind.slice(dot + 1);
|
|
114
|
+
for (const alias of this.aliases.aliasesFor(moduleName)) {
|
|
115
|
+
out.add(`${alias}.${typeName}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return Array.from(out);
|
|
119
|
+
}
|
|
82
120
|
/** @internal Bridge for StaticAnalyzer — do not use outside the analyzer package. */
|
|
83
121
|
_context() {
|
|
84
122
|
return { aliases: this.aliases, definitions: this.defs, aliasesByModule: this.aliasesByModule };
|
package/dist/analyzer.d.ts
CHANGED
|
@@ -8,6 +8,21 @@ export interface StaticAnalyzerOptions {
|
|
|
8
8
|
export declare class StaticAnalyzer {
|
|
9
9
|
private readonly celEnv;
|
|
10
10
|
constructor(options?: StaticAnalyzerOptions);
|
|
11
|
+
/**
|
|
12
|
+
* Run static analysis over a flattened manifest list.
|
|
13
|
+
*
|
|
14
|
+
* **Contract**: every non-system manifest (anything outside `Telo.Definition`,
|
|
15
|
+
* `Telo.Abstract`) must carry `metadata.source` (non-empty string) and
|
|
16
|
+
* `metadata.sourceLine` (number). The dedup that backs
|
|
17
|
+
* `DUPLICATE_RESOURCE_NAME` reads those fields to tell a pipeline echo
|
|
18
|
+
* apart from a genuine collision, and downstream diagnostic positioning
|
|
19
|
+
* depends on them too. Real callers stamp positions already (the `Loader`,
|
|
20
|
+
* `flattenForAnalyzer`, the telo-editor's `emitDocsFor`, the VSCode
|
|
21
|
+
* extension). Programmatic callers — tests, ad-hoc scripts — should pass
|
|
22
|
+
* their inputs through `withSyntheticPositions(...)` before calling
|
|
23
|
+
* `analyze()`. A missing position throws a clear error rather than
|
|
24
|
+
* silently producing wrong diagnostics.
|
|
25
|
+
*/
|
|
11
26
|
analyze(manifests: ResourceManifest[], options?: AnalysisOptions, registry?: AnalysisRegistry): AnalysisDiagnostic[];
|
|
12
27
|
analyzeErrors(manifests: ResourceManifest[], options?: AnalysisOptions, registry?: AnalysisRegistry): AnalysisDiagnostic[];
|
|
13
28
|
normalize(manifests: ResourceManifest[], registry: AnalysisRegistry): ResourceManifest[];
|
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;AAgB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAif/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;IAoiBvB,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
|
@@ -7,13 +7,50 @@ import { buildKernelGlobalsSchema, mergeKernelGlobalsIntoContext } from "./kerne
|
|
|
7
7
|
import { computeSuggestKind } from "./kind-suggest.js";
|
|
8
8
|
import { isModuleKind } from "./module-kinds.js";
|
|
9
9
|
import { normalizeInlineResources } from "./normalize-inline-resources.js";
|
|
10
|
+
import { REF_VALIDATION_SKIP_KINDS } from "./system-kinds.js";
|
|
11
|
+
import { resolveRefSentinels } from "./resolve-ref-sentinels.js";
|
|
12
|
+
import { rewriteSyntheticOrigins } from "./rewrite-synthetic-origins.js";
|
|
10
13
|
import { celTypeSatisfiesJsonSchema, substituteCelFields, validateAgainstSchema, } from "./schema-compat.js";
|
|
11
14
|
import { DiagnosticSeverity } from "./types.js";
|
|
12
15
|
import { getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
|
|
13
16
|
import { validateExtends } from "./validate-extends.js";
|
|
17
|
+
import { validateNestedInlineResources } from "./validate-nested-inline.js";
|
|
18
|
+
import { validateProviderCoherence } from "./validate-provider-coherence.js";
|
|
14
19
|
import { validateReferences } from "./validate-references.js";
|
|
15
20
|
import { validateThrowsCoverage } from "./validate-throws-coverage.js";
|
|
16
21
|
const SELF_PREFIX = "Self.";
|
|
22
|
+
/**
|
|
23
|
+
* `StaticAnalyzer.analyze()` requires `metadata.source` (non-empty) and
|
|
24
|
+
* `metadata.sourceLine` (number) on every non-system manifest — see the
|
|
25
|
+
* JSDoc on `analyze()`. Production callers stamp these via the `Loader` /
|
|
26
|
+
* `flattenForAnalyzer` / `emitDocsFor` paths; programmatic callers (tests,
|
|
27
|
+
* scripts) should pre-process inputs with `withSyntheticPositions(...)`.
|
|
28
|
+
* Surfacing the violation here turns silent dedup misbehaviour into a
|
|
29
|
+
* loud, actionable error.
|
|
30
|
+
*/
|
|
31
|
+
function assertManifestPositions(manifests) {
|
|
32
|
+
for (let i = 0; i < manifests.length; i++) {
|
|
33
|
+
const m = manifests[i];
|
|
34
|
+
if (REF_VALIDATION_SKIP_KINDS.has(m.kind))
|
|
35
|
+
continue;
|
|
36
|
+
const meta = m.metadata;
|
|
37
|
+
const okSource = typeof meta?.source === "string" && meta.source.length > 0;
|
|
38
|
+
const okLine = typeof meta?.sourceLine === "number";
|
|
39
|
+
if (okSource && okLine)
|
|
40
|
+
continue;
|
|
41
|
+
const label = `${m.kind}/${m.metadata?.name ?? "(unnamed)"}`;
|
|
42
|
+
const missing = [
|
|
43
|
+
!okSource ? "metadata.source" : null,
|
|
44
|
+
!okLine ? "metadata.sourceLine" : null,
|
|
45
|
+
]
|
|
46
|
+
.filter(Boolean)
|
|
47
|
+
.join(" and ");
|
|
48
|
+
throw new Error(`StaticAnalyzer.analyze(): manifest #${i} (${label}) is missing ${missing}. ` +
|
|
49
|
+
`Real callers stamp positions automatically; programmatic callers ` +
|
|
50
|
+
`(tests, ad-hoc scripts) should pass inputs through ` +
|
|
51
|
+
`\`withSyntheticPositions(manifests)\` before calling analyze().`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
17
54
|
/** Resolve an alias-prefixed kind value (e.g. `Self.Encoder` or `Ai.Model`)
|
|
18
55
|
* to its canonical form. `Self.<Name>` resolves to `<ownModule>.<Name>` —
|
|
19
56
|
* the magic alias for "this library's own module" — and other prefixes
|
|
@@ -373,7 +410,23 @@ export class StaticAnalyzer {
|
|
|
373
410
|
constructor(options = {}) {
|
|
374
411
|
this.celEnv = buildCelEnvironment(options.celHandlers);
|
|
375
412
|
}
|
|
413
|
+
/**
|
|
414
|
+
* Run static analysis over a flattened manifest list.
|
|
415
|
+
*
|
|
416
|
+
* **Contract**: every non-system manifest (anything outside `Telo.Definition`,
|
|
417
|
+
* `Telo.Abstract`) must carry `metadata.source` (non-empty string) and
|
|
418
|
+
* `metadata.sourceLine` (number). The dedup that backs
|
|
419
|
+
* `DUPLICATE_RESOURCE_NAME` reads those fields to tell a pipeline echo
|
|
420
|
+
* apart from a genuine collision, and downstream diagnostic positioning
|
|
421
|
+
* depends on them too. Real callers stamp positions already (the `Loader`,
|
|
422
|
+
* `flattenForAnalyzer`, the telo-editor's `emitDocsFor`, the VSCode
|
|
423
|
+
* extension). Programmatic callers — tests, ad-hoc scripts — should pass
|
|
424
|
+
* their inputs through `withSyntheticPositions(...)` before calling
|
|
425
|
+
* `analyze()`. A missing position throws a clear error rather than
|
|
426
|
+
* silently producing wrong diagnostics.
|
|
427
|
+
*/
|
|
376
428
|
analyze(manifests, options, registry) {
|
|
429
|
+
assertManifestPositions(manifests);
|
|
377
430
|
const diagnostics = [];
|
|
378
431
|
// Use pre-seeded registries from the provided AnalysisRegistry, or create fresh ones.
|
|
379
432
|
// New aliases/definitions found in the manifests are accumulated into the provided instance
|
|
@@ -490,6 +543,20 @@ export class StaticAnalyzer {
|
|
|
490
543
|
}
|
|
491
544
|
// Phase 2: extract inline resources from x-telo-ref slots into first-class manifests
|
|
492
545
|
const allManifests = normalizeInlineResources(manifests, defs, aliases, aliasesByModule);
|
|
546
|
+
// Phase 2.5: resolve `!ref <name>` sentinels at every ref slot to canonical
|
|
547
|
+
// {kind, name} objects so downstream phases (validation, dependency graph,
|
|
548
|
+
// kernel controllers) see a uniform shape. Runs after normalize so both
|
|
549
|
+
// original and inline-extracted manifests have their sentinels resolved.
|
|
550
|
+
resolveRefSentinels(allManifests, defs, aliases, aliasesByModule);
|
|
551
|
+
// Trusted-input fast path: when the caller has already attested that
|
|
552
|
+
// this exact manifest set passes analysis (e.g. via the kernel's
|
|
553
|
+
// hash-stamped `.validated.json` cache), skip the validation walk.
|
|
554
|
+
// Registration of identities / aliases / definitions and inline-resource
|
|
555
|
+
// normalisation have already run above; that's all downstream
|
|
556
|
+
// consumers (prepare, init loop) require.
|
|
557
|
+
if (options?.skipValidation) {
|
|
558
|
+
return diagnostics;
|
|
559
|
+
}
|
|
493
560
|
// Build a name→manifest map for looking up referenced resources
|
|
494
561
|
const byName = new Map();
|
|
495
562
|
for (const m of allManifests) {
|
|
@@ -497,6 +564,64 @@ export class StaticAnalyzer {
|
|
|
497
564
|
byName.set(m.metadata.name, m);
|
|
498
565
|
}
|
|
499
566
|
}
|
|
567
|
+
// Fail loud on definition schemas AJV cannot compile. `validateAgainstSchema`
|
|
568
|
+
// and `validateWithRefs` swallow compile failures (returning no issues),
|
|
569
|
+
// which would silently skip schema validation for every resource of that
|
|
570
|
+
// kind — surface the broken schema once, anchored on the definition itself.
|
|
571
|
+
for (const m of allManifests) {
|
|
572
|
+
if (m.kind !== "Telo.Definition" && m.kind !== "Telo.Abstract")
|
|
573
|
+
continue;
|
|
574
|
+
const schema = m.schema;
|
|
575
|
+
if (!schema || typeof schema !== "object")
|
|
576
|
+
continue;
|
|
577
|
+
const name = m.metadata?.name;
|
|
578
|
+
if (!name)
|
|
579
|
+
continue;
|
|
580
|
+
const compileError = defs.schemaCompileError(schema);
|
|
581
|
+
if (compileError) {
|
|
582
|
+
diagnostics.push({
|
|
583
|
+
severity: DiagnosticSeverity.Error,
|
|
584
|
+
code: "SCHEMA_COMPILE_ERROR",
|
|
585
|
+
source: SOURCE,
|
|
586
|
+
message: `${m.kind}/${name}: definition schema failed to compile: ${compileError}`,
|
|
587
|
+
data: {
|
|
588
|
+
resource: { kind: m.kind, name },
|
|
589
|
+
filePath: m.metadata?.source,
|
|
590
|
+
path: "schema",
|
|
591
|
+
},
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
// Library env: rejection — `env:` on a Library `variables` / `secrets`
|
|
596
|
+
// entry is forbidden. The Library entry schema is otherwise open so that
|
|
597
|
+
// any JSON Schema property schema is valid; this targeted check produces
|
|
598
|
+
// a clear diagnostic instead of a generic "additional property" error.
|
|
599
|
+
for (const m of allManifests) {
|
|
600
|
+
if (m.kind !== "Telo.Library")
|
|
601
|
+
continue;
|
|
602
|
+
const filePath = m.metadata?.source;
|
|
603
|
+
const moduleName = m.metadata?.name;
|
|
604
|
+
const resource = moduleName ? { kind: m.kind, name: moduleName } : undefined;
|
|
605
|
+
for (const block of ["variables", "secrets"]) {
|
|
606
|
+
const entries = m[block];
|
|
607
|
+
if (!entries || typeof entries !== "object" || Array.isArray(entries))
|
|
608
|
+
continue;
|
|
609
|
+
for (const [entryName, entry] of Object.entries(entries)) {
|
|
610
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
611
|
+
continue;
|
|
612
|
+
if ("env" in entry) {
|
|
613
|
+
diagnostics.push({
|
|
614
|
+
severity: DiagnosticSeverity.Error,
|
|
615
|
+
code: "LIBRARY_ENV_KEY_REJECTED",
|
|
616
|
+
source: SOURCE,
|
|
617
|
+
message: `Telo.Library ${block}/${entryName}: 'env:' is only permitted on Telo.Application entries. ` +
|
|
618
|
+
`Libraries must receive values from importers via the parent manifest's variables / secrets block.`,
|
|
619
|
+
data: { resource, filePath, path: `${block}.${entryName}.env` },
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
500
625
|
// Build typed kernel globals schema so x-telo-context chain validation
|
|
501
626
|
// recognises variables, secrets, resources, env automatically
|
|
502
627
|
const kernelGlobals = buildKernelGlobalsSchema(allManifests);
|
|
@@ -568,6 +693,33 @@ export class StaticAnalyzer {
|
|
|
568
693
|
});
|
|
569
694
|
}
|
|
570
695
|
}
|
|
696
|
+
// Validate inline resources nested inside this resource's body (e.g. a
|
|
697
|
+
// Run.Sequence step's `invoke: { kind, ...config }`). These sit at
|
|
698
|
+
// x-telo-ref slots reached only through local `$ref`s, which the
|
|
699
|
+
// reference field map intentionally does not follow, so they escape both
|
|
700
|
+
// inline-extraction and the per-resource schema check above.
|
|
701
|
+
if (definition.schema) {
|
|
702
|
+
// Resolve inline kinds in the parent resource's scope: direct kind
|
|
703
|
+
// first, then the parent module's own aliases (for resources declared
|
|
704
|
+
// inside an imported module), then the root aliases. Mirrors how the
|
|
705
|
+
// analyzer resolves kinds elsewhere so module-scoped aliases don't
|
|
706
|
+
// produce false UNDEFINED_KIND diagnostics.
|
|
707
|
+
const ownModule = m.metadata?.module;
|
|
708
|
+
const scopeResolver = ownModule && !rootModules.has(ownModule) ? aliasesByModule.get(ownModule) : undefined;
|
|
709
|
+
diagnostics.push(...validateNestedInlineResources(m, definition.schema, (kind) => {
|
|
710
|
+
const direct = defs.resolve(kind);
|
|
711
|
+
if (direct)
|
|
712
|
+
return direct;
|
|
713
|
+
const viaScope = scopeResolver?.resolveKind(kind);
|
|
714
|
+
if (viaScope) {
|
|
715
|
+
const scoped = defs.resolve(viaScope);
|
|
716
|
+
if (scoped)
|
|
717
|
+
return scoped;
|
|
718
|
+
}
|
|
719
|
+
const viaRoot = aliases.resolveKind(kind);
|
|
720
|
+
return viaRoot ? defs.resolve(viaRoot) : undefined;
|
|
721
|
+
}));
|
|
722
|
+
}
|
|
571
723
|
// (Invocation context compatibility check is handled via x-telo-context in the CEL pass below)
|
|
572
724
|
}
|
|
573
725
|
// Template-body structural validations: check that template entry-points produce
|
|
@@ -622,15 +774,14 @@ export class StaticAnalyzer {
|
|
|
622
774
|
emitTargetMismatch(dispatchKind, targetSchema, md.inputs, "inputs");
|
|
623
775
|
}
|
|
624
776
|
}
|
|
625
|
-
// Top-level `result:`
|
|
626
|
-
//
|
|
627
|
-
//
|
|
777
|
+
// Top-level `result:` is a post-call mapping that must satisfy the abstract
|
|
778
|
+
// this definition `extends` (`outputType`). It's a sibling of whichever
|
|
779
|
+
// dispatch entry-point declared a kind-typed target (`provide:` or
|
|
780
|
+
// `invoke:`). The target's outputType lives on the dispatcher's `kind`
|
|
628
781
|
// and is what `result` is typed against *inside* CEL — separate role.
|
|
629
|
-
|
|
630
|
-
typeof
|
|
631
|
-
|
|
632
|
-
md.result &&
|
|
633
|
-
typeof md.result === "object") {
|
|
782
|
+
const hasDispatchObject = (provide && typeof provide === "object" && !Array.isArray(provide)) ||
|
|
783
|
+
(invoke && typeof invoke === "object" && !Array.isArray(invoke));
|
|
784
|
+
if (hasDispatchObject && md.result && typeof md.result === "object") {
|
|
634
785
|
const extendsValue = md.extends;
|
|
635
786
|
if (typeof extendsValue === "string" && extendsValue.length > 0) {
|
|
636
787
|
const abstractSchema = lookupDefinitionTypeField(extendsValue, "outputType", defs, aliases, allManifests);
|
|
@@ -694,8 +845,18 @@ export class StaticAnalyzer {
|
|
|
694
845
|
effectiveContext = mergeKernelGlobalsIntoContext(resolvedContext, kernelGlobals);
|
|
695
846
|
}
|
|
696
847
|
const engine = defaultRegistry().get(engineName);
|
|
697
|
-
if (!engine)
|
|
848
|
+
if (!engine) {
|
|
849
|
+
// No registered engine owns this tag — the expression would go
|
|
850
|
+
// entirely unanalyzed. Surface it rather than skipping silently.
|
|
851
|
+
diagnostics.push({
|
|
852
|
+
severity: DiagnosticSeverity.Error,
|
|
853
|
+
code: "UNKNOWN_ENGINE",
|
|
854
|
+
source: SOURCE,
|
|
855
|
+
message: `${m.kind}/${resource.name}: no templating engine registered for '!${engineName}' at '${path}'.`,
|
|
856
|
+
data: { resource, filePath, path },
|
|
857
|
+
});
|
|
698
858
|
return;
|
|
859
|
+
}
|
|
699
860
|
const findings = engine.analyze(expr, { celEnv: this.celEnv, contextSchema: effectiveContext });
|
|
700
861
|
for (const f of findings) {
|
|
701
862
|
if (f.code === "CEL_SYNTAX_ERROR") {
|
|
@@ -735,16 +896,25 @@ export class StaticAnalyzer {
|
|
|
735
896
|
diagnostics.push(...validateReferences(allManifests, { aliases, definitions: defs, aliasesByModule }));
|
|
736
897
|
// Validate `extends` fields and flag legacy `capability: <UserAbstract>` overload.
|
|
737
898
|
diagnostics.push(...validateExtends(allManifests, defs, aliases));
|
|
899
|
+
// Validate provider coherence rules for `provide:` template-target definitions.
|
|
900
|
+
diagnostics.push(...validateProviderCoherence(allManifests, defs, aliases));
|
|
738
901
|
// Validate throws: declarations and catches: coverage (rules 1, 2, 4, 7)
|
|
739
902
|
diagnostics.push(...validateThrowsCoverage(allManifests, defs, aliases, this.celEnv));
|
|
740
|
-
|
|
903
|
+
// Reroute diagnostics on synthetic (inline-extracted) resources back to
|
|
904
|
+
// the chain root so position-index lookups land on the parent doc.
|
|
905
|
+
return rewriteSyntheticOrigins(diagnostics, allManifests);
|
|
741
906
|
}
|
|
742
907
|
analyzeErrors(manifests, options, registry) {
|
|
743
908
|
return this.analyze(manifests, options, registry).filter((d) => d.severity === DiagnosticSeverity.Error);
|
|
744
909
|
}
|
|
745
910
|
normalize(manifests, registry) {
|
|
746
911
|
const ctx = registry._context();
|
|
747
|
-
|
|
912
|
+
const normalized = normalizeInlineResources(manifests, ctx.definitions, ctx.aliases, ctx.aliasesByModule);
|
|
913
|
+
// Resolve !ref sentinels after normalize so both the original and
|
|
914
|
+
// inline-extracted manifests get their refs canonicalized to
|
|
915
|
+
// {kind, name} for the kernel that consumes this output.
|
|
916
|
+
resolveRefSentinels(normalized, ctx.definitions, ctx.aliases, ctx.aliasesByModule);
|
|
917
|
+
return normalized;
|
|
748
918
|
}
|
|
749
919
|
prepare(manifests, registry) {
|
|
750
920
|
const ctx = registry._context();
|
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,EAiU/C,CAAC"}
|
package/dist/builtins.js
CHANGED
|
@@ -147,7 +147,12 @@ export const KERNEL_BUILTINS = [
|
|
|
147
147
|
additionalProperties: false,
|
|
148
148
|
properties: {
|
|
149
149
|
self: { "x-telo-context-from-root": "schema" },
|
|
150
|
-
result: {
|
|
150
|
+
result: {
|
|
151
|
+
"x-telo-context-from-ref-kind": [
|
|
152
|
+
"provide/kind#outputType",
|
|
153
|
+
"invoke/kind#outputType",
|
|
154
|
+
],
|
|
155
|
+
},
|
|
151
156
|
},
|
|
152
157
|
},
|
|
153
158
|
},
|
|
@@ -213,6 +218,20 @@ export const KERNEL_BUILTINS = [
|
|
|
213
218
|
anyOf: [
|
|
214
219
|
{ type: "string", "x-telo-ref": "telo#Runnable" },
|
|
215
220
|
{ type: "string", "x-telo-ref": "telo#Service" },
|
|
221
|
+
// Post-resolution shape that `resolveRefSentinels`
|
|
222
|
+
// substitutes a `!ref <name>` sentinel into. The
|
|
223
|
+
// adjacent `x-telo-ref` constraints govern the kind
|
|
224
|
+
// check; this branch only admits the structural form so
|
|
225
|
+
// AJV doesn't reject a resolved ref.
|
|
226
|
+
{
|
|
227
|
+
type: "object",
|
|
228
|
+
required: ["kind", "name"],
|
|
229
|
+
properties: {
|
|
230
|
+
kind: { type: "string" },
|
|
231
|
+
name: { type: "string" },
|
|
232
|
+
},
|
|
233
|
+
additionalProperties: true,
|
|
234
|
+
},
|
|
216
235
|
],
|
|
217
236
|
},
|
|
218
237
|
},
|
|
@@ -220,6 +239,44 @@ export const KERNEL_BUILTINS = [
|
|
|
220
239
|
type: "array",
|
|
221
240
|
items: { type: "string" },
|
|
222
241
|
},
|
|
242
|
+
// Application-level environment contract. Each entry layers `env:`
|
|
243
|
+
// (required, names the source env var) and `default:` (optional, used
|
|
244
|
+
// when the env var is unset) on top of an open JSON Schema property
|
|
245
|
+
// schema. `type:` constrains the coercion rule applied to the raw env
|
|
246
|
+
// string (scalars per-type; `object` / `array` via JSON.parse with the
|
|
247
|
+
// matching top-level type). All other JSON Schema keywords are passed
|
|
248
|
+
// through unchanged and applied to the coerced value via the standard
|
|
249
|
+
// schema validator. See kernel/nodejs/src/application-env.ts.
|
|
250
|
+
variables: {
|
|
251
|
+
type: "object",
|
|
252
|
+
additionalProperties: {
|
|
253
|
+
type: "object",
|
|
254
|
+
required: ["env", "type"],
|
|
255
|
+
properties: {
|
|
256
|
+
env: { type: "string" },
|
|
257
|
+
type: {
|
|
258
|
+
type: "string",
|
|
259
|
+
enum: ["string", "integer", "number", "boolean", "object", "array"],
|
|
260
|
+
},
|
|
261
|
+
default: {},
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
secrets: {
|
|
266
|
+
type: "object",
|
|
267
|
+
additionalProperties: {
|
|
268
|
+
type: "object",
|
|
269
|
+
required: ["env", "type"],
|
|
270
|
+
properties: {
|
|
271
|
+
env: { type: "string" },
|
|
272
|
+
type: {
|
|
273
|
+
type: "string",
|
|
274
|
+
enum: ["string", "integer", "number", "boolean", "object", "array"],
|
|
275
|
+
},
|
|
276
|
+
default: {},
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
223
280
|
},
|
|
224
281
|
required: ["metadata"],
|
|
225
282
|
additionalProperties: false,
|
|
@@ -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;
|
|
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"}
|
|
@@ -70,6 +70,22 @@ export class DefinitionRegistry {
|
|
|
70
70
|
* @param namespace The module's metadata.namespace (e.g. "std"), or null for telo built-ins.
|
|
71
71
|
* @param moduleName The module's metadata.name (e.g. "pipeline", "http-server"). */
|
|
72
72
|
registerModuleIdentity(namespace, moduleName) {
|
|
73
|
+
// The "telo" identity is reserved for the Telo built-in module and gets
|
|
74
|
+
// populated automatically when a Telo.Abstract definition registers (see
|
|
75
|
+
// `register` below). A user app / library without a namespace must NOT
|
|
76
|
+
// claim it — silently overwriting the built-in entry breaks every
|
|
77
|
+
// x-telo-ref that resolves through "telo#…". Concretely, the
|
|
78
|
+
// `Http.Api.routes[].handler` slot in the http-server schema carries
|
|
79
|
+
// `x-telo-ref: "telo#Invocable"`. If the entry application is, say,
|
|
80
|
+
// `Telo.Application/HelloApi` (no namespace), this method previously
|
|
81
|
+
// overwrote `"telo" → "Telo"` with `"telo" → "HelloApi"`. The handler's
|
|
82
|
+
// ref then resolved to a nonexistent `HelloApi.Invocable`, the
|
|
83
|
+
// kind-mismatch check inside `validate-references.ts` short-circuited
|
|
84
|
+
// on partial context, and the analyzer reported zero issues for a
|
|
85
|
+
// manifest that explodes at runtime. Skip non-Telo no-namespace modules;
|
|
86
|
+
// they have no x-telo-ref identity to declare anyway.
|
|
87
|
+
if (!namespace && moduleName !== "Telo")
|
|
88
|
+
return;
|
|
73
89
|
const identity = namespace ? `${namespace}/${moduleName}` : "telo";
|
|
74
90
|
this.identityMap.set(identity, moduleName);
|
|
75
91
|
this.reverseIdentityMap.set(moduleName, identity);
|
|
@@ -89,7 +105,10 @@ export class DefinitionRegistry {
|
|
|
89
105
|
return `${identity}/${typeName}`;
|
|
90
106
|
}
|
|
91
107
|
/** Validates data against a schema using this registry's AJV instance, which has all
|
|
92
|
-
* 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. */
|
|
93
112
|
validateWithRefs(data, schema) {
|
|
94
113
|
let validate;
|
|
95
114
|
try {
|
|
@@ -102,6 +121,22 @@ export class DefinitionRegistry {
|
|
|
102
121
|
return [];
|
|
103
122
|
return (validate.errors ?? []).map(formatSingleError);
|
|
104
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
|
+
}
|
|
105
140
|
tryRegisterSchema(moduleName, typeName, schema) {
|
|
106
141
|
const id = this.computeId(moduleName, typeName);
|
|
107
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;
|
|
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,CAkHjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,MAAM,CAOtE"}
|