@telorun/analyzer 0.56.1 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyzer.d.ts +5 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +232 -94
- package/dist/cel-environment.d.ts.map +1 -1
- package/dist/cel-environment.js +6 -3
- package/dist/definition-registry.d.ts +5 -0
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +9 -0
- package/dist/index.d.ts +14 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/invocation-contract.d.ts +21 -13
- package/dist/invocation-contract.d.ts.map +1 -1
- package/dist/invocation-contract.js +43 -46
- package/dist/loaded-types.d.ts +13 -0
- package/dist/loaded-types.d.ts.map +1 -1
- package/dist/manifest-loader.d.ts +8 -1
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +33 -6
- package/dist/manifest-visitor.d.ts +4 -0
- package/dist/manifest-visitor.d.ts.map +1 -1
- package/dist/manifest-visitor.js +3 -3
- package/dist/migrations/driver.d.ts +69 -0
- package/dist/migrations/driver.d.ts.map +1 -0
- package/dist/migrations/driver.js +200 -0
- package/dist/migrations/entries/index.d.ts +3 -0
- package/dist/migrations/entries/index.d.ts.map +1 -0
- package/dist/migrations/entries/index.js +9 -0
- package/dist/migrations/entries/normalize-value-types.json +48 -0
- package/dist/migrations/entries/ref-slot-scalar-type.json +20 -0
- package/dist/migrations/entry-data.d.ts +25 -0
- package/dist/migrations/entry-data.d.ts.map +1 -0
- package/dist/migrations/entry-data.js +171 -0
- package/dist/migrations/index.d.ts +22 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +19 -0
- package/dist/migrations/match.d.ts +162 -0
- package/dist/migrations/match.d.ts.map +1 -0
- package/dist/migrations/match.js +295 -0
- package/dist/migrations/patch.d.ts +68 -0
- package/dist/migrations/patch.d.ts.map +1 -0
- package/dist/migrations/patch.js +236 -0
- package/dist/migrations/provenance.d.ts +25 -0
- package/dist/migrations/provenance.d.ts.map +1 -0
- package/dist/migrations/provenance.js +118 -0
- package/dist/migrations/registry.d.ts +35 -0
- package/dist/migrations/registry.d.ts.map +1 -0
- package/dist/migrations/registry.js +35 -0
- package/dist/migrations/report.d.ts +28 -0
- package/dist/migrations/report.d.ts.map +1 -0
- package/dist/migrations/report.js +144 -0
- package/dist/migrations/types.d.ts +115 -0
- package/dist/migrations/types.d.ts.map +1 -0
- package/dist/migrations/types.js +24 -0
- package/dist/migrations/yaml-edit.d.ts +21 -0
- package/dist/migrations/yaml-edit.d.ts.map +1 -0
- package/dist/migrations/yaml-edit.js +245 -0
- package/dist/module-file-claims.d.ts +65 -0
- package/dist/module-file-claims.d.ts.map +1 -0
- package/dist/module-file-claims.js +106 -0
- package/dist/parse-loaded-file.d.ts +8 -0
- package/dist/parse-loaded-file.d.ts.map +1 -1
- package/dist/parse-loaded-file.js +22 -13
- package/dist/resolve-schema-type-refs.d.ts +29 -12
- package/dist/resolve-schema-type-refs.d.ts.map +1 -1
- package/dist/resolve-schema-type-refs.js +89 -16
- package/dist/schema-compat.d.ts +35 -9
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +181 -54
- package/dist/schema-region.d.ts +33 -0
- package/dist/schema-region.d.ts.map +1 -0
- package/dist/schema-region.js +45 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -0
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +29 -3
- package/dist/validate-include-placement.d.ts +26 -0
- package/dist/validate-include-placement.d.ts.map +1 -0
- package/dist/validate-include-placement.js +67 -0
- package/dist/validate-step-inputs.d.ts +8 -1
- package/dist/validate-step-inputs.d.ts.map +1 -1
- package/dist/validate-step-inputs.js +47 -2
- package/dist/validate-throws-coverage.d.ts.map +1 -1
- package/dist/validate-throws-coverage.js +15 -12
- package/dist/validate-value-type-slots.d.ts +43 -0
- package/dist/validate-value-type-slots.d.ts.map +1 -0
- package/dist/validate-value-type-slots.js +142 -0
- package/dist/value-type-keyword.d.ts +60 -0
- package/dist/value-type-keyword.d.ts.map +1 -0
- package/dist/value-type-keyword.js +119 -0
- package/dist/yaml-source-edit.d.ts +56 -0
- package/dist/yaml-source-edit.d.ts.map +1 -0
- package/dist/yaml-source-edit.js +93 -0
- package/package.json +4 -3
- package/src/analyzer.ts +276 -129
- package/src/cel-environment.ts +6 -3
- package/src/definition-registry.ts +10 -0
- package/src/index.ts +31 -3
- package/src/invocation-contract.ts +43 -48
- package/src/loaded-types.ts +13 -0
- package/src/manifest-loader.ts +37 -6
- package/src/manifest-visitor.ts +11 -3
- package/src/migrations/driver.ts +262 -0
- package/src/migrations/entries/index.ts +10 -0
- package/src/migrations/entries/normalize-value-types.json +48 -0
- package/src/migrations/entries/ref-slot-scalar-type.json +20 -0
- package/src/migrations/entry-data.ts +207 -0
- package/src/migrations/index.ts +22 -0
- package/src/migrations/match.ts +382 -0
- package/src/migrations/patch.ts +265 -0
- package/src/migrations/provenance.ts +167 -0
- package/src/migrations/registry.ts +40 -0
- package/src/migrations/report.ts +169 -0
- package/src/migrations/types.ts +144 -0
- package/src/migrations/yaml-edit.ts +260 -0
- package/src/module-file-claims.ts +168 -0
- package/src/parse-loaded-file.ts +31 -12
- package/src/resolve-schema-type-refs.ts +93 -16
- package/src/schema-compat.ts +218 -62
- package/src/schema-region.ts +46 -0
- package/src/types.ts +54 -0
- package/src/validate-cel-context.ts +36 -3
- package/src/validate-include-placement.ts +70 -0
- package/src/validate-step-inputs.ts +56 -1
- package/src/validate-throws-coverage.ts +16 -11
- package/src/validate-value-type-slots.ts +182 -0
- package/src/value-type-keyword.ts +135 -0
- package/src/yaml-source-edit.ts +106 -0
- package/dist/binary-slot.d.ts +0 -39
- package/dist/binary-slot.d.ts.map +0 -1
- package/dist/binary-slot.js +0 -58
- package/src/binary-slot.ts +0 -71
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where author-written JSON Schema lives in a manifest — one definition, read by
|
|
3
|
+
* every surface that has to bound itself to schema.
|
|
4
|
+
*
|
|
5
|
+
* The keys are the KERNEL's own schema-valued manifest keys, which no resource
|
|
6
|
+
* kind owns. That is what makes the rule generic: a surface using it learns no
|
|
7
|
+
* resource kind, and a module that invents a schema-bearing field of its own
|
|
8
|
+
* reaches it through one of these or not at all — against the topology-driven
|
|
9
|
+
* constraint, an enumeration of the standard library's kinds would be both
|
|
10
|
+
* incomplete and knowledge the analyzer must not hold.
|
|
11
|
+
*
|
|
12
|
+
* A schema fragment is NOT confined to kind documents. An inline `inputType:` /
|
|
13
|
+
* `outputType:` sits on any kind that declares one, an API route carries
|
|
14
|
+
* `request.schema.body`, a `Telo.JsonSchema` carries `schema`. So a check that
|
|
15
|
+
* walks a manifest's ROOT keys covers a fraction of the sites an author writes —
|
|
16
|
+
* which is a silent hole in exactly the checks that exist to stop a silent
|
|
17
|
+
* degrade. Containment is by ANCESTRY instead: a node is in a schema region when
|
|
18
|
+
* some key on the path to it is one of these.
|
|
19
|
+
*
|
|
20
|
+
* Browser-safe: no Node built-ins.
|
|
21
|
+
*/
|
|
22
|
+
/** The kernel's schema-valued manifest keys. */
|
|
23
|
+
export declare const SCHEMA_REGION_KEYS: readonly string[];
|
|
24
|
+
/**
|
|
25
|
+
* True when `path` reaches into a schema region — some ANCESTOR segment is a
|
|
26
|
+
* schema-valued key.
|
|
27
|
+
*
|
|
28
|
+
* Ancestors only, so a rule keyed on a region key itself still means "inside a
|
|
29
|
+
* schema" rather than "is one". `path` is the walk's own segment list; numeric
|
|
30
|
+
* segments (array indices) never equal a key name, so they need no special case.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isInSchemaRegion(path: readonly (string | number)[]): boolean;
|
|
33
|
+
//# sourceMappingURL=schema-region.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-region.d.ts","sourceRoot":"","sources":["../src/schema-region.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,gDAAgD;AAChD,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAM/C,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,OAAO,CAM5E"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where author-written JSON Schema lives in a manifest — one definition, read by
|
|
3
|
+
* every surface that has to bound itself to schema.
|
|
4
|
+
*
|
|
5
|
+
* The keys are the KERNEL's own schema-valued manifest keys, which no resource
|
|
6
|
+
* kind owns. That is what makes the rule generic: a surface using it learns no
|
|
7
|
+
* resource kind, and a module that invents a schema-bearing field of its own
|
|
8
|
+
* reaches it through one of these or not at all — against the topology-driven
|
|
9
|
+
* constraint, an enumeration of the standard library's kinds would be both
|
|
10
|
+
* incomplete and knowledge the analyzer must not hold.
|
|
11
|
+
*
|
|
12
|
+
* A schema fragment is NOT confined to kind documents. An inline `inputType:` /
|
|
13
|
+
* `outputType:` sits on any kind that declares one, an API route carries
|
|
14
|
+
* `request.schema.body`, a `Telo.JsonSchema` carries `schema`. So a check that
|
|
15
|
+
* walks a manifest's ROOT keys covers a fraction of the sites an author writes —
|
|
16
|
+
* which is a silent hole in exactly the checks that exist to stop a silent
|
|
17
|
+
* degrade. Containment is by ANCESTRY instead: a node is in a schema region when
|
|
18
|
+
* some key on the path to it is one of these.
|
|
19
|
+
*
|
|
20
|
+
* Browser-safe: no Node built-ins.
|
|
21
|
+
*/
|
|
22
|
+
/** The kernel's schema-valued manifest keys. */
|
|
23
|
+
export const SCHEMA_REGION_KEYS = [
|
|
24
|
+
"schema",
|
|
25
|
+
"status",
|
|
26
|
+
"inputType",
|
|
27
|
+
"outputType",
|
|
28
|
+
"itemType",
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* True when `path` reaches into a schema region — some ANCESTOR segment is a
|
|
32
|
+
* schema-valued key.
|
|
33
|
+
*
|
|
34
|
+
* Ancestors only, so a rule keyed on a region key itself still means "inside a
|
|
35
|
+
* schema" rather than "is one". `path` is the walk's own segment list; numeric
|
|
36
|
+
* segments (array indices) never equal a key name, so they need no special case.
|
|
37
|
+
*/
|
|
38
|
+
export function isInSchemaRegion(path) {
|
|
39
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
40
|
+
const segment = path[i];
|
|
41
|
+
if (typeof segment === "string" && SCHEMA_REGION_KEYS.includes(segment))
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -26,6 +26,37 @@ export interface Range {
|
|
|
26
26
|
export type PositionIndex = Map<string, Range>;
|
|
27
27
|
/** LSP-compatible Diagnostic shape. range is optional because parsed YAML may not carry
|
|
28
28
|
* position info when only the parsed object (not raw text) is available. */
|
|
29
|
+
/** A mechanically applicable repair, carried unchanged from whatever produced
|
|
30
|
+
* it (a templating engine, a kind-name suggestion) to every consumer: CLI
|
|
31
|
+
* JSON, IDE CodeActions, an agent applying it without re-deriving it from
|
|
32
|
+
* prose.
|
|
33
|
+
*
|
|
34
|
+
* `replacement` is the **whole** value at the diagnostic's `path`, corrected —
|
|
35
|
+
* never a fragment — so applying it needs no knowledge of the language inside.
|
|
36
|
+
* There is deliberately no sub-range: carrying one beside a whole-value
|
|
37
|
+
* replacement gives the field two readings, and the minimal-edit reading
|
|
38
|
+
* (splice `replacement` at `range`) produces garbage because the two measure
|
|
39
|
+
* different strings.
|
|
40
|
+
*
|
|
41
|
+
* One shape rather than one per producer: a `fix` field beside a
|
|
42
|
+
* `suggestedKind` field beside a CEL-specific one would leave every host
|
|
43
|
+
* wiring a separate action path for what is the same gesture. */
|
|
44
|
+
export interface DiagnosticFix {
|
|
45
|
+
readonly replacement: string;
|
|
46
|
+
}
|
|
47
|
+
/** The `data` stamp diagnostics carry. Loose by design — passes bolt their own
|
|
48
|
+
* keys on — but the fields every consumer reads are declared. */
|
|
49
|
+
export interface DiagnosticData {
|
|
50
|
+
resource?: {
|
|
51
|
+
kind: string;
|
|
52
|
+
name: string;
|
|
53
|
+
};
|
|
54
|
+
filePath?: string;
|
|
55
|
+
/** Dotted path of the offending value within its resource. */
|
|
56
|
+
path?: string;
|
|
57
|
+
fix?: DiagnosticFix;
|
|
58
|
+
[key: string]: unknown;
|
|
59
|
+
}
|
|
29
60
|
export interface AnalysisDiagnostic {
|
|
30
61
|
range?: Range;
|
|
31
62
|
severity?: DiagnosticSeverity;
|
|
@@ -36,6 +67,9 @@ export interface AnalysisDiagnostic {
|
|
|
36
67
|
/** Telo-specific extras such as { resource: { kind, name }, path } */
|
|
37
68
|
data?: unknown;
|
|
38
69
|
}
|
|
70
|
+
/** Single reader for a diagnostic's fix, so no consumer re-derives the shape
|
|
71
|
+
* by hand-casting `data`. */
|
|
72
|
+
export declare function diagnosticFix(d: AnalysisDiagnostic): DiagnosticFix | undefined;
|
|
39
73
|
export interface ManifestSource {
|
|
40
74
|
supports(url: string): boolean;
|
|
41
75
|
read(url: string): Promise<{
|
|
@@ -67,11 +101,28 @@ export interface LoadOptions {
|
|
|
67
101
|
* pairs manifests to YAML nodes by index. Folded into the file cache key so a
|
|
68
102
|
* desugared and a raw load of the same file never collide. */
|
|
69
103
|
desugarImports?: boolean;
|
|
104
|
+
/** When true, the loader's migration phase rewrites legacy spellings in each
|
|
105
|
+
* parsed document before anything else reads the tree. On for every resolved
|
|
106
|
+
* consumer — the kernel's analysis and runtime loads, `telo check`, the
|
|
107
|
+
* analyzer — so one rewrite serves the definition registry, the runtime and
|
|
108
|
+
* the editor's analysis alike.
|
|
109
|
+
*
|
|
110
|
+
* **Off for a round-trip view.** The editor pairs manifests to YAML nodes by
|
|
111
|
+
* index and writes the pair back on save; migrating one half of that pair
|
|
112
|
+
* would silently change the author's file. `telo migrate` is likewise a raw
|
|
113
|
+
* consumer — it rewrites the YAML itself and must see the legacy spelling to
|
|
114
|
+
* find it. Folded into the file cache key so a migrated and a raw load of
|
|
115
|
+
* the same file never collide. */
|
|
116
|
+
migrate?: boolean;
|
|
70
117
|
}
|
|
71
118
|
export interface LoaderInitOptions {
|
|
72
119
|
/** Handlers for CEL stdlib functions (e.g. `sha256`). Analyzer-only callers may
|
|
73
120
|
* omit this and get throwing stubs; runtime callers (kernel) must supply real impls. */
|
|
74
121
|
celHandlers?: import("./cel-environment.js").CelHandlers;
|
|
122
|
+
/** Migration set for `LoadOptions.migrate` loads. Defaults to the analyzer's
|
|
123
|
+
* own `CORE_MIGRATIONS`. A host supplies its own once module-shipped entries
|
|
124
|
+
* are aggregated alongside the core ones. */
|
|
125
|
+
migrations?: readonly import("./migrations/types.js").MigrationEntry[];
|
|
75
126
|
}
|
|
76
127
|
export interface AnalysisOptions {
|
|
77
128
|
strictContexts?: boolean;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE;qHACqH;AACrH,eAAO,MAAM,kBAAkB;;;;;CAKrB,CAAC;AACX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC;AAE9F,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,cAAc,CAAC;AAErD,MAAM,WAAW,QAAQ;IACvB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,QAAQ,CAAC;CACf;AAED;;oDAEoD;AACpD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE/C;6EAC6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAExD;;qEAEiE;IACjE,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEjE;;qEAEiE;IACjE,cAAc,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,WAAW;IAC1B;;;+EAG2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;mEAO+D;IAC/D,cAAc,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -8,3 +8,9 @@ export const DiagnosticSeverity = {
|
|
|
8
8
|
};
|
|
9
9
|
/** Default entry-point filename when a directory is given instead of a file. */
|
|
10
10
|
export const DEFAULT_MANIFEST_FILENAME = "telo.yaml";
|
|
11
|
+
/** Single reader for a diagnostic's fix, so no consumer re-derives the shape
|
|
12
|
+
* by hand-casting `data`. */
|
|
13
|
+
export function diagnosticFix(d) {
|
|
14
|
+
const fix = d.data?.fix;
|
|
15
|
+
return fix && typeof fix.replacement === "string" ? fix : undefined;
|
|
16
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-cel-context.d.ts","sourceRoot":"","sources":["../src/validate-cel-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAItF,MAAM,WAAW,kBAAkB;IACjC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC;;kDAE8C;IAC9C,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;KACxD,CAAC;IACF,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KAC/C,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CACtC;AA4CD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACnC,QAAQ,GAAE,WAAW,CAAC,MAAM,CAAa,GACxC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"validate-cel-context.d.ts","sourceRoot":"","sources":["../src/validate-cel-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAItF,MAAM,WAAW,kBAAkB;IACjC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC;;kDAE8C;IAC9C,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;KACxD,CAAC;IACF,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KAC/C,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CACtC;AA4CD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACnC,QAAQ,GAAE,WAAW,CAAC,MAAM,CAAa,GACxC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA0EjC;AAuFD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,IAAI,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAChD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAiJrB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAQrB;AAWD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,SAAM,GACT,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAAC,CAGvD;AAUD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,SAAM,GAAG,MAAM,EAAE,CAqBxF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { extractAccessChains, validateChainAgainstSchema } from "@telorun/templating";
|
|
2
|
-
import { mergeTypeSchemas } from "@telorun/sdk";
|
|
2
|
+
import { mergeTypeSchemas, parseCanonicalTypeSchemaId } from "@telorun/sdk";
|
|
3
3
|
import { KERNEL_BUILTINS } from "./builtins.js";
|
|
4
4
|
/**
|
|
5
5
|
* Resolve a type field value (string name, inline type, or raw schema) to a JSON Schema.
|
|
@@ -63,6 +63,31 @@ export function resolveTypeFieldToSchema(value, allManifests, ancestry = new Set
|
|
|
63
63
|
}
|
|
64
64
|
if (typeof value === "object" && value !== null) {
|
|
65
65
|
const obj = value;
|
|
66
|
+
// A canonical, module-scoped id — what `resolveSchemaTypeRefs` normalizes
|
|
67
|
+
// both the reference tag and the legacy `telo://` authority form into.
|
|
68
|
+
//
|
|
69
|
+
// This is the ALIAS-AWARE path, and it is why the canonical form exists:
|
|
70
|
+
// the id names the OWNING MODULE, so two libraries declaring a shape of the
|
|
71
|
+
// same name stay distinct. The bare-name lookup below cannot do that — it
|
|
72
|
+
// matches the first manifest with that `metadata.name` in a flattened list,
|
|
73
|
+
// which is how an alias got silently dropped.
|
|
74
|
+
const canonical = parseCanonicalTypeSchemaId(obj.$ref);
|
|
75
|
+
if (canonical) {
|
|
76
|
+
if (ancestry.has(obj.$ref))
|
|
77
|
+
return undefined;
|
|
78
|
+
const owned = allManifests.find((m) => m.metadata?.name === canonical.typeName &&
|
|
79
|
+
m.metadata?.module === canonical.moduleName &&
|
|
80
|
+
isTypeKind(m.kind, allManifests) &&
|
|
81
|
+
typeof m.schema === "object" &&
|
|
82
|
+
m.schema !== null);
|
|
83
|
+
if (owned) {
|
|
84
|
+
return applyExtends(owned.schema, owned.extends, allManifests, new Set(ancestry).add(obj.$ref));
|
|
85
|
+
}
|
|
86
|
+
// A module that is not in this set — a single-file analysis, or an import
|
|
87
|
+
// whose library was not forwarded. Fall through to the bare name, which is
|
|
88
|
+
// what this resolved before the canonical form existed.
|
|
89
|
+
return resolveTypeFieldToSchema(canonical.typeName, allManifests, ancestry);
|
|
90
|
+
}
|
|
66
91
|
// Inline type resource: { kind: "Type.JsonSchema", schema: {...} }
|
|
67
92
|
if (obj.schema && typeof obj.schema === "object") {
|
|
68
93
|
return applyExtends(obj.schema, obj.extends, allManifests, ancestry);
|
|
@@ -71,8 +96,9 @@ export function resolveTypeFieldToSchema(value, allManifests, ancestry = new Set
|
|
|
71
96
|
if (obj.type || obj.properties) {
|
|
72
97
|
return obj;
|
|
73
98
|
}
|
|
74
|
-
// Named type reference resolved from a `!ref` → { kind, name }
|
|
75
|
-
//
|
|
99
|
+
// Named type reference resolved from a `!ref` → { kind, name }. Reached only
|
|
100
|
+
// where the canonical rewrite could not run (a scope whose aliases are not in
|
|
101
|
+
// hand); resolves by bare name, as it always has.
|
|
76
102
|
if (typeof obj.name === "string") {
|
|
77
103
|
return resolveTypeFieldToSchema(obj.name, allManifests, ancestry);
|
|
78
104
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ResourceManifest } from "@telorun/sdk";
|
|
2
|
+
import { type AnalysisDiagnostic } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* An `!include-text` / `!include-bytes` in a doc that is never instantiated is
|
|
5
|
+
* never read.
|
|
6
|
+
*
|
|
7
|
+
* The two tags resolve when the resource owning them is created — deferred so
|
|
8
|
+
* that loading a manifest does not pull payload layers, which is the property
|
|
9
|
+
* the artifact spec protects by giving `telo.yaml` a layer of its own. The cost
|
|
10
|
+
* of that choice is this dead spot: a doc with no `create()` has no moment at
|
|
11
|
+
* which the file would be read, so the value stays an unresolved marker and
|
|
12
|
+
* whatever reads it sees a sentinel object instead of the file's contents.
|
|
13
|
+
*
|
|
14
|
+
* Nothing else would report it. There is no controller to validate against a
|
|
15
|
+
* schema and no runtime consumer to fail, so the manifest ships looking correct
|
|
16
|
+
* — exactly the silent-no-op failure mode that makes a descriptive field worth
|
|
17
|
+
* checking. Hence an error at the one place that can see it.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately narrow: it reports only where the argument is complete ("this
|
|
20
|
+
* doc is never instantiated"). A JSON-Schema region *inside* a definition —
|
|
21
|
+
* `schema:`, `inputType:` — is equally unreadable, but that is a general
|
|
22
|
+
* question about tags in schema metadata rather than one about these two, and
|
|
23
|
+
* no pass answers it for any tag today.
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateIncludePlacement(manifests: ResourceManifest[]): AnalysisDiagnostic[];
|
|
26
|
+
//# sourceMappingURL=validate-include-placement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-include-placement.d.ts","sourceRoot":"","sources":["../src/validate-include-placement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAmBzE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,gBAAgB,EAAE,GAAG,kBAAkB,EAAE,CA0B5F"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { INCLUDE_ENGINE_NAMES, walkCelExpressions } from "@telorun/templating";
|
|
2
|
+
import { DiagnosticSeverity } from "./types.js";
|
|
3
|
+
const SOURCE = "telo-analyzer";
|
|
4
|
+
/**
|
|
5
|
+
* Docs that are never instantiated as resources.
|
|
6
|
+
*
|
|
7
|
+
* `Telo.Application` / `Telo.Library` are module declarations, and `Telo.Import`
|
|
8
|
+
* is a dependency edge — none of the three reaches a controller's `create()`.
|
|
9
|
+
* `Telo.Definition` and `Telo.Abstract` are deliberately absent: a definition's
|
|
10
|
+
* template body (`resources:` / `invoke:` / `run:` / `provide:`) DOES become
|
|
11
|
+
* resources, so an embed there resolves normally.
|
|
12
|
+
*/
|
|
13
|
+
const NEVER_INSTANTIATED = new Set([
|
|
14
|
+
"Telo.Application",
|
|
15
|
+
"Telo.Library",
|
|
16
|
+
"Telo.Import",
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* An `!include-text` / `!include-bytes` in a doc that is never instantiated is
|
|
20
|
+
* never read.
|
|
21
|
+
*
|
|
22
|
+
* The two tags resolve when the resource owning them is created — deferred so
|
|
23
|
+
* that loading a manifest does not pull payload layers, which is the property
|
|
24
|
+
* the artifact spec protects by giving `telo.yaml` a layer of its own. The cost
|
|
25
|
+
* of that choice is this dead spot: a doc with no `create()` has no moment at
|
|
26
|
+
* which the file would be read, so the value stays an unresolved marker and
|
|
27
|
+
* whatever reads it sees a sentinel object instead of the file's contents.
|
|
28
|
+
*
|
|
29
|
+
* Nothing else would report it. There is no controller to validate against a
|
|
30
|
+
* schema and no runtime consumer to fail, so the manifest ships looking correct
|
|
31
|
+
* — exactly the silent-no-op failure mode that makes a descriptive field worth
|
|
32
|
+
* checking. Hence an error at the one place that can see it.
|
|
33
|
+
*
|
|
34
|
+
* Deliberately narrow: it reports only where the argument is complete ("this
|
|
35
|
+
* doc is never instantiated"). A JSON-Schema region *inside* a definition —
|
|
36
|
+
* `schema:`, `inputType:` — is equally unreadable, but that is a general
|
|
37
|
+
* question about tags in schema metadata rather than one about these two, and
|
|
38
|
+
* no pass answers it for any tag today.
|
|
39
|
+
*/
|
|
40
|
+
export function validateIncludePlacement(manifests) {
|
|
41
|
+
const out = [];
|
|
42
|
+
for (const manifest of manifests) {
|
|
43
|
+
if (!NEVER_INSTANTIATED.has(manifest.kind))
|
|
44
|
+
continue;
|
|
45
|
+
const name = manifest.metadata?.name;
|
|
46
|
+
const filePath = manifest.metadata?.source;
|
|
47
|
+
walkCelExpressions(manifest, "", (source, path, engineName) => {
|
|
48
|
+
if (!INCLUDE_ENGINE_NAMES.has(engineName))
|
|
49
|
+
return;
|
|
50
|
+
out.push({
|
|
51
|
+
severity: DiagnosticSeverity.Error,
|
|
52
|
+
code: "INCLUDE_OUTSIDE_RESOURCE",
|
|
53
|
+
source: SOURCE,
|
|
54
|
+
message: `${manifest.kind}${name ? `/${name}` : ""}: \`!${engineName} ${source}\` at '${path}' is ` +
|
|
55
|
+
`never read — a ${manifest.kind} doc is not instantiated, and a file embed is resolved ` +
|
|
56
|
+
`when the resource holding it is created. Move it onto the resource that needs the ` +
|
|
57
|
+
`file, or read the file at runtime with Fs.File.`,
|
|
58
|
+
data: {
|
|
59
|
+
resource: { kind: manifest.kind, name: name ?? "" },
|
|
60
|
+
filePath,
|
|
61
|
+
path,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
@@ -4,6 +4,9 @@ export interface StepInputIssue {
|
|
|
4
4
|
path: string;
|
|
5
5
|
targetLabel: string;
|
|
6
6
|
message: string;
|
|
7
|
+
/** Set when the issue is a type-argument disagreement rather than a contract
|
|
8
|
+
* shape violation — the two read differently and deserve their own code. */
|
|
9
|
+
code?: "CEL_TYPE_ARGUMENT_MISMATCH";
|
|
7
10
|
}
|
|
8
11
|
/**
|
|
9
12
|
* Validate every step's `inputs:` against the invoked target's declared input
|
|
@@ -20,5 +23,9 @@ export interface StepInputIssue {
|
|
|
20
23
|
* from `x-telo-step-context`, and the paired inputs field from whichever sibling
|
|
21
24
|
* property carries `x-telo-topology-role: inputs`.
|
|
22
25
|
*/
|
|
23
|
-
export declare function collectStepInputIssues(manifest: Record<string, any>, defSchema: Record<string, any>, allManifests: Record<string, any>[], defs: DefinitionRegistry, aliases: AliasResolver, scopes: ModuleScopes
|
|
26
|
+
export declare function collectStepInputIssues(manifest: Record<string, any>, defSchema: Record<string, any>, allManifests: Record<string, any>[], defs: DefinitionRegistry, aliases: AliasResolver, scopes: ModuleScopes,
|
|
27
|
+
/** The typed `steps.<name>.result` context for this resource. Supplied by the
|
|
28
|
+
* caller because building it is analyzer state; without it the contract check
|
|
29
|
+
* still runs and only the type-argument comparison is skipped. */
|
|
30
|
+
stepContext?: Record<string, any>): StepInputIssue[];
|
|
24
31
|
//# sourceMappingURL=validate-step-inputs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-step-inputs.d.ts","sourceRoot":"","sources":["../src/validate-step-inputs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"validate-step-inputs.d.ts","sourceRoot":"","sources":["../src/validate-step-inputs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAoBnE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB;iFAC6E;IAC7E,IAAI,CAAC,EAAE,4BAA4B,CAAC;CACrC;AAGD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC7B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC9B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACnC,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,YAAY;AACpB;;mEAEmE;AACnE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAChC,cAAc,EAAE,CAiHlB"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { resolveContract } from "./invocation-contract.js";
|
|
2
|
-
import { substituteCelFields, validateAgainstSchema } from "./schema-compat.js";
|
|
2
|
+
import { checkSchemaCompatibility, navigateSchemaToExprPath, substituteCelFields, validateAgainstSchema, } from "./schema-compat.js";
|
|
3
|
+
import { plainChainOf } from "@telorun/templating";
|
|
4
|
+
import { valueTypeOf } from "@telorun/sdk";
|
|
3
5
|
import { analyzerContractScope, containerOf, gatherPropertySchemas, missingRequired, resolveLocalRef, walkStepArray, } from "./analyzer.js";
|
|
4
6
|
/**
|
|
5
7
|
* Validate every step's `inputs:` against the invoked target's declared input
|
|
@@ -16,7 +18,11 @@ import { analyzerContractScope, containerOf, gatherPropertySchemas, missingRequi
|
|
|
16
18
|
* from `x-telo-step-context`, and the paired inputs field from whichever sibling
|
|
17
19
|
* property carries `x-telo-topology-role: inputs`.
|
|
18
20
|
*/
|
|
19
|
-
export function collectStepInputIssues(manifest, defSchema, allManifests, defs, aliases, scopes
|
|
21
|
+
export function collectStepInputIssues(manifest, defSchema, allManifests, defs, aliases, scopes,
|
|
22
|
+
/** The typed `steps.<name>.result` context for this resource. Supplied by the
|
|
23
|
+
* caller because building it is analyzer state; without it the contract check
|
|
24
|
+
* still runs and only the type-argument comparison is skipped. */
|
|
25
|
+
stepContext) {
|
|
20
26
|
const out = [];
|
|
21
27
|
const props = defSchema.properties;
|
|
22
28
|
if (!props)
|
|
@@ -67,6 +73,45 @@ export function collectStepInputIssues(manifest, defSchema, allManifests, defs,
|
|
|
67
73
|
// container and survive the filter.
|
|
68
74
|
const celPaths = new Set();
|
|
69
75
|
const substituted = substituteCelFields(values, contract.schema, undefined, (p) => celPaths.add(p));
|
|
76
|
+
// The type-argument check, at the one site where a produced value's schema
|
|
77
|
+
// meets a consuming slot's. A CEL leaf's placeholder says nothing about
|
|
78
|
+
// what the expression yields, so AJV above is silent here by design — and
|
|
79
|
+
// that silence is exactly where a stream of the wrong element used to
|
|
80
|
+
// flow. The comparison is covariant and gradual: an omitted argument is
|
|
81
|
+
// *any* in both directions, so only a definite conflict is reported.
|
|
82
|
+
if (stepContext) {
|
|
83
|
+
for (const [inputName, inputValue] of Object.entries(values)) {
|
|
84
|
+
const chain = plainChainOf(inputValue);
|
|
85
|
+
// The step context is rooted at the STEP MAP, so a `steps.` prefix is
|
|
86
|
+
// the namespace name and not a property of it. Only that namespace is
|
|
87
|
+
// navigated: `inputs.` and a named binding resolve elsewhere, and
|
|
88
|
+
// guessing at a root this does not hold would compare the wrong schema.
|
|
89
|
+
if (!chain?.startsWith("steps."))
|
|
90
|
+
continue;
|
|
91
|
+
const produced = navigateSchemaToExprPath(stepContext, chain.slice("steps.".length));
|
|
92
|
+
const slotSchema = contract.schema.properties?.[inputName];
|
|
93
|
+
if (!produced || !slotSchema)
|
|
94
|
+
continue;
|
|
95
|
+
// ONLY a type-argument disagreement, which is what the code says. The
|
|
96
|
+
// comparator is a general structural comparison, so running it on any
|
|
97
|
+
// pair would report a missing required property as "disagreeing type
|
|
98
|
+
// arguments" — and would turn every plain-chain wiring site into a
|
|
99
|
+
// broad new Error-severity check hidden behind an argument-specific
|
|
100
|
+
// name. Both sides must declare a value type for the question to be
|
|
101
|
+
// about arguments at all.
|
|
102
|
+
if (!valueTypeOf(produced) || !valueTypeOf(slotSchema))
|
|
103
|
+
continue;
|
|
104
|
+
const { compatible, issues } = checkSchemaCompatibility(produced, slotSchema, (ref) => defs.schemaForId(ref));
|
|
105
|
+
if (compatible)
|
|
106
|
+
continue;
|
|
107
|
+
out.push({
|
|
108
|
+
path: `${stepPath}.${inputsField}.${inputName}`,
|
|
109
|
+
targetLabel: invokedName ?? invokedKind ?? "the invoked resource",
|
|
110
|
+
message: issues.join("; "),
|
|
111
|
+
code: "CEL_TYPE_ARGUMENT_MISMATCH",
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
70
115
|
for (const issue of validateAgainstSchema(substituted, contract.schema)) {
|
|
71
116
|
if (celPaths.has(issue.path))
|
|
72
117
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-throws-coverage.d.ts","sourceRoot":"","sources":["../src/validate-throws-coverage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEjE,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AACtB,OAAO,EAA0B,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"validate-throws-coverage.d.ts","sourceRoot":"","sources":["../src/validate-throws-coverage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEjE,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AACtB,OAAO,EAA0B,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA+ezE,oDAAoD;AACpD,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,WAAW,EAChB,eAAe,GAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAa,EACvD,WAAW,GAAE,GAAG,CAAC,MAAM,CAAa,GACnC,kBAAkB,EAAE,CAkDtB"}
|
|
@@ -171,7 +171,7 @@ function checkCatchAllPlacement(entries, resource, channel, filePath, arrayPath)
|
|
|
171
171
|
* in coverage-proving `when:` clauses. Phase 2 accepts inherit/passthrough
|
|
172
172
|
* handler unions too — when the resolved union is unbounded, a catch-all is
|
|
173
173
|
* required (rule 4 extension). */
|
|
174
|
-
function checkCatchesCoverage(entries, union, resource, filePath, arrayPath, env) {
|
|
174
|
+
function checkCatchesCoverage(entries, union, resource, filePath, arrayPath, env, handler) {
|
|
175
175
|
const diagnostics = [];
|
|
176
176
|
const declaredCodes = new Set(union.codes.keys());
|
|
177
177
|
const covered = new Set();
|
|
@@ -221,16 +221,19 @@ function checkCatchesCoverage(entries, union, resource, filePath, arrayPath, env
|
|
|
221
221
|
});
|
|
222
222
|
}
|
|
223
223
|
if (!hasCatchAll) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
224
|
+
// One diagnostic per block, not per code: every uncovered code sits at the
|
|
225
|
+
// same `catches:` array, and one catch-all answers all of them at once. A
|
|
226
|
+
// diagnostic each repeated the same location and the same fix N times.
|
|
227
|
+
const uncovered = [...declaredCodes].filter((c) => !covered.has(c)).sort();
|
|
228
|
+
if (uncovered.length > 0) {
|
|
229
|
+
diagnostics.push({
|
|
230
|
+
severity: DiagnosticSeverity.Error,
|
|
231
|
+
code: "UNCOVERED_THROW_CODE",
|
|
232
|
+
source: SOURCE,
|
|
233
|
+
message: `handler ${handler?.name ? `\`!ref ${handler.name}\`` : `\`${handler?.kind ?? "?"}\``} can throw ${uncovered.length} code${uncovered.length === 1 ? "" : "s"} that no catches: entry handles: ${uncovered.map((c) => `'${c}'`).join(", ")}. ` +
|
|
234
|
+
`Give each a matching \`when:\` (e.g. \`when: !cel "error.code == '${uncovered[0]}'"\`), or add a catch-all entry — one with no \`when:\`, placed last.`,
|
|
235
|
+
data: { resource, filePath, path: arrayPath, uncovered },
|
|
236
|
+
});
|
|
234
237
|
}
|
|
235
238
|
}
|
|
236
239
|
return diagnostics;
|
|
@@ -447,7 +450,7 @@ export function validateThrowsCoverage(manifests, defs, aliases, env, aliasesByM
|
|
|
447
450
|
diagnostics.push(...checkCatchAllPlacement(entries, resource, "catches", filePath, arrayPath));
|
|
448
451
|
const handlerRef = resolveHandlerRef(siblingData[catchesFor]);
|
|
449
452
|
const union = handlerRefUnion(handlerRef, manifests, resolveCtx, scopeResolver);
|
|
450
|
-
diagnostics.push(...checkCatchesCoverage(entries, union, resource, filePath, arrayPath, env));
|
|
453
|
+
diagnostics.push(...checkCatchesCoverage(entries, union, resource, filePath, arrayPath, env, handlerRef));
|
|
451
454
|
diagnostics.push(...checkTypedErrorData(entries, union, resource, filePath, arrayPath, env));
|
|
452
455
|
});
|
|
453
456
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static validation of the `x-telo-type` annotation itself — the strict half of
|
|
3
|
+
* the accessor split, and the `validate-ref-slots.ts` precedent.
|
|
4
|
+
*
|
|
5
|
+
* `readValueTypeSlot` is deliberately lenient: it reports whatever it can read,
|
|
6
|
+
* returning a slot with no `entry` for a name it does not know. That leniency is
|
|
7
|
+
* what lets every surface keep working; this pass is what stops it becoming
|
|
8
|
+
* silence. Before the unification an unrecognized brand simply resolved to
|
|
9
|
+
* `undefined` and the slot quietly lost its identity — the same class of failure
|
|
10
|
+
* `X_TELO_REF_INVALID_USE` exists to prevent for `use` tokens.
|
|
11
|
+
*
|
|
12
|
+
* The vocabulary is CLOSED and `Telo.`-qualified, so there is nothing here to
|
|
13
|
+
* resolve against an alias scope: a name is a built-in or it is a mistake. A
|
|
14
|
+
* SHAPE is a different thing entirely and is named with the reference tag, which
|
|
15
|
+
* carries its own resolution and its own diagnostics — this pass never sees one,
|
|
16
|
+
* because `resolveSchemaTypeRefs` has already turned it into a `$ref`.
|
|
17
|
+
*
|
|
18
|
+
* Scoping follows `X_TELO_REF_UNRESOLVED`: reported only for manifests in the
|
|
19
|
+
* entry's own modules, since a published dependency is not the consumer's to fix.
|
|
20
|
+
*
|
|
21
|
+
* Browser-safe: no Node built-ins.
|
|
22
|
+
*/
|
|
23
|
+
import { type ResourceManifest } from "@telorun/sdk";
|
|
24
|
+
export interface ValueTypeSlotIssue {
|
|
25
|
+
code: "X_TELO_TYPE_UNKNOWN" | "X_TELO_TYPE_ARGUMENT_UNKNOWN";
|
|
26
|
+
manifest: ResourceManifest;
|
|
27
|
+
/** Dotted path to the annotated schema node, e.g. `schema.properties.body`. */
|
|
28
|
+
path: string;
|
|
29
|
+
message: string;
|
|
30
|
+
/** The whole-value replacement that repairs it, when one is derivable. A
|
|
31
|
+
* misspelled name has a single correct spelling and the annotation's value is
|
|
32
|
+
* that name, so the repair is the primitive `DiagnosticFix` already carries —
|
|
33
|
+
* computing a suggestion and printing it in prose alone leaves the author to
|
|
34
|
+
* retype what the analyzer already knows. Only for the bare-name spelling: the
|
|
35
|
+
* object form's name is nested, and a whole-value replacement there would
|
|
36
|
+
* discard the type arguments beside it. */
|
|
37
|
+
fix?: {
|
|
38
|
+
replacement: string;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Every `x-telo-type` problem in one manifest, wherever a schema is written. */
|
|
42
|
+
export declare function validateValueTypeSlots(manifest: ResourceManifest): ValueTypeSlotIssue[];
|
|
43
|
+
//# sourceMappingURL=validate-value-type-slots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-value-type-slots.d.ts","sourceRoot":"","sources":["../src/validate-value-type-slots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAA+C,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIlG,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,qBAAqB,GAAG,8BAA8B,CAAC;IAC7D,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;gDAM4C;IAC5C,GAAG,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/B;AAwID,iFAAiF;AACjF,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,kBAAkB,EAAE,CAIvF"}
|