@telorun/analyzer 0.11.0 → 1.1.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 +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.map +1 -1
- package/dist/analyzer.js +44 -9
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +44 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/kernel-globals.d.ts.map +1 -1
- package/dist/kernel-globals.js +9 -11
- package/dist/normalize-inline-resources.d.ts.map +1 -1
- package/dist/normalize-inline-resources.js +26 -14
- package/dist/position-metadata.d.ts +5 -1
- package/dist/position-metadata.d.ts.map +1 -1
- package/dist/position-metadata.js +8 -1
- package/dist/reference-field-map.d.ts +21 -4
- package/dist/reference-field-map.d.ts.map +1 -1
- package/dist/reference-field-map.js +35 -19
- 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/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/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-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.js +24 -24
- package/package.json +5 -3
- package/src/analysis-registry.ts +37 -0
- package/src/analyzer.ts +45 -11
- package/src/builtins.ts +44 -1
- package/src/index.ts +1 -0
- package/src/kernel-globals.ts +9 -11
- package/src/normalize-inline-resources.ts +48 -13
- package/src/position-metadata.ts +8 -1
- package/src/reference-field-map.ts +46 -18
- package/src/residual-schema.ts +49 -0
- package/src/rewrite-synthetic-origins.ts +75 -0
- package/src/validate-cel-context.ts +28 -15
- package/src/validate-provider-coherence.ts +166 -0
- package/src/validate-references.ts +24 -24
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.4.0
|
|
65
65
|
---
|
|
66
66
|
kind: Telo.Import
|
|
67
67
|
metadata:
|
|
68
68
|
name: Sql
|
|
69
|
-
source:
|
|
69
|
+
source: std/sql@0.2.3
|
|
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.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;AAc9B,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,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IA6cvB,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;IAUxF,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,10 +7,12 @@ 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 { rewriteSyntheticOrigins } from "./rewrite-synthetic-origins.js";
|
|
10
11
|
import { celTypeSatisfiesJsonSchema, substituteCelFields, validateAgainstSchema, } from "./schema-compat.js";
|
|
11
12
|
import { DiagnosticSeverity } from "./types.js";
|
|
12
13
|
import { getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
|
|
13
14
|
import { validateExtends } from "./validate-extends.js";
|
|
15
|
+
import { validateProviderCoherence } from "./validate-provider-coherence.js";
|
|
14
16
|
import { validateReferences } from "./validate-references.js";
|
|
15
17
|
import { validateThrowsCoverage } from "./validate-throws-coverage.js";
|
|
16
18
|
const SELF_PREFIX = "Self.";
|
|
@@ -497,6 +499,36 @@ export class StaticAnalyzer {
|
|
|
497
499
|
byName.set(m.metadata.name, m);
|
|
498
500
|
}
|
|
499
501
|
}
|
|
502
|
+
// Library env: rejection — `env:` on a Library `variables` / `secrets`
|
|
503
|
+
// entry is forbidden. The Library entry schema is otherwise open so that
|
|
504
|
+
// any JSON Schema property schema is valid; this targeted check produces
|
|
505
|
+
// a clear diagnostic instead of a generic "additional property" error.
|
|
506
|
+
for (const m of allManifests) {
|
|
507
|
+
if (m.kind !== "Telo.Library")
|
|
508
|
+
continue;
|
|
509
|
+
const filePath = m.metadata?.source;
|
|
510
|
+
const moduleName = m.metadata?.name;
|
|
511
|
+
const resource = moduleName ? { kind: m.kind, name: moduleName } : undefined;
|
|
512
|
+
for (const block of ["variables", "secrets"]) {
|
|
513
|
+
const entries = m[block];
|
|
514
|
+
if (!entries || typeof entries !== "object" || Array.isArray(entries))
|
|
515
|
+
continue;
|
|
516
|
+
for (const [entryName, entry] of Object.entries(entries)) {
|
|
517
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
518
|
+
continue;
|
|
519
|
+
if ("env" in entry) {
|
|
520
|
+
diagnostics.push({
|
|
521
|
+
severity: DiagnosticSeverity.Error,
|
|
522
|
+
code: "LIBRARY_ENV_KEY_REJECTED",
|
|
523
|
+
source: SOURCE,
|
|
524
|
+
message: `Telo.Library ${block}/${entryName}: 'env:' is only permitted on Telo.Application entries. ` +
|
|
525
|
+
`Libraries must receive values from importers via the parent manifest's variables / secrets block.`,
|
|
526
|
+
data: { resource, filePath, path: `${block}.${entryName}.env` },
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
500
532
|
// Build typed kernel globals schema so x-telo-context chain validation
|
|
501
533
|
// recognises variables, secrets, resources, env automatically
|
|
502
534
|
const kernelGlobals = buildKernelGlobalsSchema(allManifests);
|
|
@@ -622,15 +654,14 @@ export class StaticAnalyzer {
|
|
|
622
654
|
emitTargetMismatch(dispatchKind, targetSchema, md.inputs, "inputs");
|
|
623
655
|
}
|
|
624
656
|
}
|
|
625
|
-
// Top-level `result:`
|
|
626
|
-
//
|
|
627
|
-
//
|
|
657
|
+
// Top-level `result:` is a post-call mapping that must satisfy the abstract
|
|
658
|
+
// this definition `extends` (`outputType`). It's a sibling of whichever
|
|
659
|
+
// dispatch entry-point declared a kind-typed target (`provide:` or
|
|
660
|
+
// `invoke:`). The target's outputType lives on the dispatcher's `kind`
|
|
628
661
|
// and is what `result` is typed against *inside* CEL — separate role.
|
|
629
|
-
|
|
630
|
-
typeof
|
|
631
|
-
|
|
632
|
-
md.result &&
|
|
633
|
-
typeof md.result === "object") {
|
|
662
|
+
const hasDispatchObject = (provide && typeof provide === "object" && !Array.isArray(provide)) ||
|
|
663
|
+
(invoke && typeof invoke === "object" && !Array.isArray(invoke));
|
|
664
|
+
if (hasDispatchObject && md.result && typeof md.result === "object") {
|
|
634
665
|
const extendsValue = md.extends;
|
|
635
666
|
if (typeof extendsValue === "string" && extendsValue.length > 0) {
|
|
636
667
|
const abstractSchema = lookupDefinitionTypeField(extendsValue, "outputType", defs, aliases, allManifests);
|
|
@@ -735,9 +766,13 @@ export class StaticAnalyzer {
|
|
|
735
766
|
diagnostics.push(...validateReferences(allManifests, { aliases, definitions: defs, aliasesByModule }));
|
|
736
767
|
// Validate `extends` fields and flag legacy `capability: <UserAbstract>` overload.
|
|
737
768
|
diagnostics.push(...validateExtends(allManifests, defs, aliases));
|
|
769
|
+
// Validate provider coherence rules for `provide:` template-target definitions.
|
|
770
|
+
diagnostics.push(...validateProviderCoherence(allManifests, defs, aliases));
|
|
738
771
|
// Validate throws: declarations and catches: coverage (rules 1, 2, 4, 7)
|
|
739
772
|
diagnostics.push(...validateThrowsCoverage(allManifests, defs, aliases, this.celEnv));
|
|
740
|
-
|
|
773
|
+
// Reroute diagnostics on synthetic (inline-extracted) resources back to
|
|
774
|
+
// the chain root so position-index lookups land on the parent doc.
|
|
775
|
+
return rewriteSyntheticOrigins(diagnostics, allManifests);
|
|
741
776
|
}
|
|
742
777
|
analyzeErrors(manifests, options, registry) {
|
|
743
778
|
return this.analyze(manifests, options, registry).filter((d) => d.severity === DiagnosticSeverity.Error);
|
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,EAmT/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
|
},
|
|
@@ -220,6 +225,44 @@ export const KERNEL_BUILTINS = [
|
|
|
220
225
|
type: "array",
|
|
221
226
|
items: { type: "string" },
|
|
222
227
|
},
|
|
228
|
+
// Application-level environment contract. Each entry layers `env:`
|
|
229
|
+
// (required, names the source env var) and `default:` (optional, used
|
|
230
|
+
// when the env var is unset) on top of an open JSON Schema property
|
|
231
|
+
// schema. `type:` constrains the coercion rule applied to the raw env
|
|
232
|
+
// string (scalars per-type; `object` / `array` via JSON.parse with the
|
|
233
|
+
// matching top-level type). All other JSON Schema keywords are passed
|
|
234
|
+
// through unchanged and applied to the coerced value via the standard
|
|
235
|
+
// schema validator. See kernel/nodejs/src/application-env.ts.
|
|
236
|
+
variables: {
|
|
237
|
+
type: "object",
|
|
238
|
+
additionalProperties: {
|
|
239
|
+
type: "object",
|
|
240
|
+
required: ["env", "type"],
|
|
241
|
+
properties: {
|
|
242
|
+
env: { type: "string" },
|
|
243
|
+
type: {
|
|
244
|
+
type: "string",
|
|
245
|
+
enum: ["string", "integer", "number", "boolean", "object", "array"],
|
|
246
|
+
},
|
|
247
|
+
default: {},
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
secrets: {
|
|
252
|
+
type: "object",
|
|
253
|
+
additionalProperties: {
|
|
254
|
+
type: "object",
|
|
255
|
+
required: ["env", "type"],
|
|
256
|
+
properties: {
|
|
257
|
+
env: { type: "string" },
|
|
258
|
+
type: {
|
|
259
|
+
type: "string",
|
|
260
|
+
enum: ["string", "integer", "number", "boolean", "object", "array"],
|
|
261
|
+
},
|
|
262
|
+
default: {},
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
},
|
|
223
266
|
},
|
|
224
267
|
required: ["metadata"],
|
|
225
268
|
additionalProperties: false,
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { isModuleKind, MODULE_KINDS } from "./module-kinds.js";
|
|
|
7
7
|
export type { ModuleKind } from "./module-kinds.js";
|
|
8
8
|
export { parseLoadedFile } from "./parse-loaded-file.js";
|
|
9
9
|
export type { ParseOptions } from "./parse-loaded-file.js";
|
|
10
|
+
export { residualEntrySchema, residualEntrySchemaMap } from "./residual-schema.js";
|
|
10
11
|
export { buildDocumentPositions, buildLineOffsets, buildPositionIndex, documentLineOffsets, } from "./position-metadata.js";
|
|
11
12
|
export type { DocumentPosition } from "./position-metadata.js";
|
|
12
13
|
export { HttpSource } from "./sources/http-source.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EACR,cAAc,EACd,UAAU,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,UAAU,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EACH,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC3E,YAAY,EACR,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,QAAQ,EACR,aAAa,EACb,KAAK,EACR,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EACR,cAAc,EACd,UAAU,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,UAAU,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EACH,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC3E,YAAY,EACR,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,QAAQ,EACR,aAAa,EACb,KAAK,EACR,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { flattenForAnalyzer, flattenLoadedModule } from "./flatten-for-analyzer.
|
|
|
4
4
|
export { Loader } from "./manifest-loader.js";
|
|
5
5
|
export { isModuleKind, MODULE_KINDS } from "./module-kinds.js";
|
|
6
6
|
export { parseLoadedFile } from "./parse-loaded-file.js";
|
|
7
|
+
export { residualEntrySchema, residualEntrySchemaMap } from "./residual-schema.js";
|
|
7
8
|
export { buildDocumentPositions, buildLineOffsets, buildPositionIndex, documentLineOffsets, } from "./position-metadata.js";
|
|
8
9
|
export { HttpSource } from "./sources/http-source.js";
|
|
9
10
|
export { RegistrySource } from "./sources/registry-source.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kernel-globals.d.ts","sourceRoot":"","sources":["../src/kernel-globals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"kernel-globals.d.ts","sourceRoot":"","sources":["../src/kernel-globals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,uDAAwD,CAAC;AASzF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,gBAAgB,EAAE,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAiCrB;AAwBD;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACjC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CASrB"}
|
package/dist/kernel-globals.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { residualEntrySchemaMap } from "./residual-schema.js";
|
|
1
2
|
/**
|
|
2
3
|
* Kernel global names available in every CEL evaluation context at runtime.
|
|
3
4
|
* Both `buildKernelGlobalsSchema` (chain-access validation) and
|
|
@@ -59,18 +60,15 @@ export function buildKernelGlobalsSchema(manifests) {
|
|
|
59
60
|
};
|
|
60
61
|
}
|
|
61
62
|
/** Wrap a JSON Schema property map (like `Telo.Application.variables`) into a
|
|
62
|
-
* closed object schema suitable for chain-access validation.
|
|
63
|
-
*
|
|
63
|
+
* closed object schema suitable for chain-access validation. For Application
|
|
64
|
+
* entries the per-entry shape carries kernel-specific keys (`env`, `default`)
|
|
65
|
+
* on top of an otherwise-standard JSON Schema property schema; those keys are
|
|
66
|
+
* stripped via `residualEntrySchemaMap` so CEL sees the coerced shape, not
|
|
67
|
+
* the env-binding wrapper. Library entries are pure JSON Schema property
|
|
68
|
+
* schemas and pass through the same call unchanged. Falls back to an open map
|
|
69
|
+
* when the module declares no variables/secrets. */
|
|
64
70
|
function buildSchemaMapSchema(schemaMap) {
|
|
65
|
-
|
|
66
|
-
return { type: "object", additionalProperties: true };
|
|
67
|
-
}
|
|
68
|
-
const props = {};
|
|
69
|
-
for (const [key, value] of Object.entries(schemaMap)) {
|
|
70
|
-
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
71
|
-
props[key] = value;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
71
|
+
const props = residualEntrySchemaMap(schemaMap);
|
|
74
72
|
if (Object.keys(props).length === 0) {
|
|
75
73
|
return { type: "object", additionalProperties: true };
|
|
76
74
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize-inline-resources.d.ts","sourceRoot":"","sources":["../src/normalize-inline-resources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAczD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"normalize-inline-resources.d.ts","sourceRoot":"","sources":["../src/normalize-inline-resources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAczD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,gBAAgB,EAAE,CA+DpB"}
|
|
@@ -60,7 +60,7 @@ export function normalizeInlineResources(resources, registry, aliases, aliasesBy
|
|
|
60
60
|
fieldPath.startsWith(prefix + ".") ||
|
|
61
61
|
fieldPath.startsWith(prefix + "["));
|
|
62
62
|
const invocationContext = isRefEntry(entry) ? entry.context : undefined;
|
|
63
|
-
const extracted = extractInlinesAtPath(resource, fieldPath, parentName, parentModule, invocationContext);
|
|
63
|
+
const extracted = extractInlinesAtPath(resource, fieldPath, parentName, resource.kind, parentModule, invocationContext);
|
|
64
64
|
for (const manifest of extracted) {
|
|
65
65
|
result.push(manifest);
|
|
66
66
|
queue.push(manifest);
|
|
@@ -75,11 +75,22 @@ export function normalizeInlineResources(resources, registry, aliases, aliasesBy
|
|
|
75
75
|
* Walks `resource` following `fieldPath` (dot notation, `[]` = array traversal).
|
|
76
76
|
* Mutates the resource in-place: replaces each inline value with `{kind, name}`.
|
|
77
77
|
* Returns the extracted manifests.
|
|
78
|
+
*
|
|
79
|
+
* Each extracted manifest carries `metadata.xTeloOrigin` so downstream
|
|
80
|
+
* diagnostics can be rerouted back to the parent doc's YAML position:
|
|
81
|
+
* - `parentKind` / `parentName` — the resource that owned the inline slot
|
|
82
|
+
* - `pathFromParent` — concrete dotted path with `[N]` indices, matching
|
|
83
|
+
* `buildPositionIndex` keys (e.g. `routes[0].handler`)
|
|
78
84
|
*/
|
|
79
|
-
function extractInlinesAtPath(resource, fieldPath, parentName, parentModule, invocationContext) {
|
|
85
|
+
function extractInlinesAtPath(resource, fieldPath, parentName, parentKind, parentModule, invocationContext) {
|
|
80
86
|
const extracted = [];
|
|
81
87
|
const parts = fieldPath.split(".");
|
|
82
|
-
function
|
|
88
|
+
function emit(inline, nameSegments, concretePath) {
|
|
89
|
+
const name = sanitizeName([parentName, ...nameSegments].join("_"));
|
|
90
|
+
extracted.push(buildManifest(inline, name, parentKind, parentName, concretePath, parentModule, invocationContext));
|
|
91
|
+
return name;
|
|
92
|
+
}
|
|
93
|
+
function traverse(obj, partsLeft, nameParts, pathSoFar) {
|
|
83
94
|
if (!obj || typeof obj !== "object" || partsLeft.length === 0)
|
|
84
95
|
return;
|
|
85
96
|
const [head, ...rest] = partsLeft;
|
|
@@ -92,15 +103,15 @@ function extractInlinesAtPath(resource, fieldPath, parentName, parentModule, inv
|
|
|
92
103
|
if (!elem || typeof elem !== "object")
|
|
93
104
|
continue;
|
|
94
105
|
const sanitizedKey = sanitizeName(mapKey);
|
|
106
|
+
const childPath = pathSoFar ? `${pathSoFar}.${mapKey}` : mapKey;
|
|
95
107
|
if (rest.length === 0) {
|
|
96
108
|
if (isInlineResource(elem)) {
|
|
97
|
-
const name =
|
|
98
|
-
extracted.push(buildManifest(elem, name, parentModule, invocationContext));
|
|
109
|
+
const name = emit(elem, [...nameParts, sanitizedKey], childPath);
|
|
99
110
|
container[mapKey] = { kind: elem.kind, name };
|
|
100
111
|
}
|
|
101
112
|
}
|
|
102
113
|
else {
|
|
103
|
-
traverse(elem, rest, [...nameParts, sanitizedKey]);
|
|
114
|
+
traverse(elem, rest, [...nameParts, sanitizedKey], childPath);
|
|
104
115
|
}
|
|
105
116
|
}
|
|
106
117
|
return;
|
|
@@ -111,6 +122,7 @@ function extractInlinesAtPath(resource, fieldPath, parentName, parentModule, inv
|
|
|
111
122
|
const val = container[key];
|
|
112
123
|
if (val == null)
|
|
113
124
|
return;
|
|
125
|
+
const keyPath = pathSoFar ? `${pathSoFar}.${key}` : key;
|
|
114
126
|
if (isArr) {
|
|
115
127
|
if (!Array.isArray(val))
|
|
116
128
|
return;
|
|
@@ -121,16 +133,16 @@ function extractInlinesAtPath(resource, fieldPath, parentName, parentModule, inv
|
|
|
121
133
|
const elemId = typeof elem.name === "string"
|
|
122
134
|
? elem.name
|
|
123
135
|
: String(idx);
|
|
136
|
+
const childPath = `${keyPath}[${idx}]`;
|
|
124
137
|
if (rest.length === 0) {
|
|
125
138
|
// Array element itself is the ref slot
|
|
126
139
|
if (isInlineResource(elem)) {
|
|
127
|
-
const name =
|
|
128
|
-
extracted.push(buildManifest(elem, name, parentModule, invocationContext));
|
|
140
|
+
const name = emit(elem, [...nameParts, key, elemId], childPath);
|
|
129
141
|
val[idx] = { kind: elem.kind, name };
|
|
130
142
|
}
|
|
131
143
|
}
|
|
132
144
|
else {
|
|
133
|
-
traverse(elem, rest, [...nameParts, key, elemId]);
|
|
145
|
+
traverse(elem, rest, [...nameParts, key, elemId], childPath);
|
|
134
146
|
}
|
|
135
147
|
}
|
|
136
148
|
}
|
|
@@ -138,20 +150,19 @@ function extractInlinesAtPath(resource, fieldPath, parentName, parentModule, inv
|
|
|
138
150
|
if (rest.length === 0) {
|
|
139
151
|
// val is the ref slot
|
|
140
152
|
if (val && typeof val === "object" && !Array.isArray(val) && isInlineResource(val)) {
|
|
141
|
-
const name =
|
|
142
|
-
extracted.push(buildManifest(val, name, parentModule, invocationContext));
|
|
153
|
+
const name = emit(val, [...nameParts, key], keyPath);
|
|
143
154
|
container[key] = { kind: val.kind, name };
|
|
144
155
|
}
|
|
145
156
|
}
|
|
146
157
|
else {
|
|
147
|
-
traverse(val, rest, [...nameParts, key]);
|
|
158
|
+
traverse(val, rest, [...nameParts, key], keyPath);
|
|
148
159
|
}
|
|
149
160
|
}
|
|
150
161
|
}
|
|
151
|
-
traverse(resource, parts, []);
|
|
162
|
+
traverse(resource, parts, [], "");
|
|
152
163
|
return extracted;
|
|
153
164
|
}
|
|
154
|
-
function buildManifest(inline, name, parentModule, invocationContext) {
|
|
165
|
+
function buildManifest(inline, name, parentKind, parentName, pathFromParent, parentModule, invocationContext) {
|
|
155
166
|
const existingMeta = inline.metadata && typeof inline.metadata === "object"
|
|
156
167
|
? inline.metadata
|
|
157
168
|
: {};
|
|
@@ -163,6 +174,7 @@ function buildManifest(inline, name, parentModule, invocationContext) {
|
|
|
163
174
|
// Inherit parent module only if the inline doesn't already declare one
|
|
164
175
|
...(parentModule && !existingMeta.module ? { module: parentModule } : {}),
|
|
165
176
|
...(invocationContext ? { xTeloInvocationContext: invocationContext } : {}),
|
|
177
|
+
xTeloOrigin: { parentKind, parentName, pathFromParent },
|
|
166
178
|
},
|
|
167
179
|
};
|
|
168
180
|
}
|
|
@@ -22,6 +22,10 @@ export declare function documentLineOffsets(text: string): number[];
|
|
|
22
22
|
* yaml-AST node range into Range coordinates. */
|
|
23
23
|
export declare function buildLineOffsets(text: string): number[];
|
|
24
24
|
/** Walks the YAML AST and records source ranges for every field value, keyed
|
|
25
|
-
* by dotted path (e.g. "kind", "config.handler", "config.routes[0].path").
|
|
25
|
+
* by dotted path (e.g. "kind", "config.handler", "config.routes[0].path").
|
|
26
|
+
* Map keys are also recorded under the `@key:<path>` namespace so diagnostic
|
|
27
|
+
* resolvers can squiggle just the key identifier instead of the full value
|
|
28
|
+
* block — used when a diagnostic targets a missing child property and the
|
|
29
|
+
* resolver has to fall back to the parent. */
|
|
26
30
|
export declare function buildPositionIndex(doc: Document, lineOffsets: number[]): PositionIndex;
|
|
27
31
|
//# sourceMappingURL=position-metadata.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"position-metadata.d.ts","sourceRoot":"","sources":["../src/position-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,MAAM,CAAC;AACrE,OAAO,KAAK,EAAY,aAAa,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;;oBAKoB;AAEpB,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,kEAAkE;AAClE,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,QAAQ,EAAE,GACrB,gBAAgB,EAAE,CAOpB;AAED;;8CAE8C;AAC9C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ1D;AAED;;kDAEkD;AAClD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAMvD;AAaD
|
|
1
|
+
{"version":3,"file":"position-metadata.d.ts","sourceRoot":"","sources":["../src/position-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,MAAM,CAAC;AACrE,OAAO,KAAK,EAAY,aAAa,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;;oBAKoB;AAEpB,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,kEAAkE;AAClE,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,QAAQ,EAAE,GACrB,gBAAgB,EAAE,CAOpB;AAED;;8CAE8C;AAC9C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ1D;AAED;;kDAEkD;AAClD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAMvD;AAaD;;;;;+CAK+C;AAC/C,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,aAAa,CA0CtF"}
|
|
@@ -45,7 +45,11 @@ function offsetToPosition(offset, lineOffsets) {
|
|
|
45
45
|
return { line: lo, character: offset - lineOffsets[lo] };
|
|
46
46
|
}
|
|
47
47
|
/** Walks the YAML AST and records source ranges for every field value, keyed
|
|
48
|
-
* by dotted path (e.g. "kind", "config.handler", "config.routes[0].path").
|
|
48
|
+
* by dotted path (e.g. "kind", "config.handler", "config.routes[0].path").
|
|
49
|
+
* Map keys are also recorded under the `@key:<path>` namespace so diagnostic
|
|
50
|
+
* resolvers can squiggle just the key identifier instead of the full value
|
|
51
|
+
* block — used when a diagnostic targets a missing child property and the
|
|
52
|
+
* resolver has to fall back to the parent. */
|
|
49
53
|
export function buildPositionIndex(doc, lineOffsets) {
|
|
50
54
|
const index = new Map();
|
|
51
55
|
function recordNode(node, path) {
|
|
@@ -66,6 +70,9 @@ export function buildPositionIndex(doc, lineOffsets) {
|
|
|
66
70
|
if (key == null)
|
|
67
71
|
continue;
|
|
68
72
|
const childPath = path ? `${path}.${key}` : key;
|
|
73
|
+
if (pair.key && pair.key.range) {
|
|
74
|
+
recordNode(pair.key, `@key:${childPath}`);
|
|
75
|
+
}
|
|
69
76
|
if (pair.value != null) {
|
|
70
77
|
recordNode(pair.value, childPath);
|
|
71
78
|
walk(pair.value, childPath);
|
|
@@ -43,12 +43,29 @@ export declare const REFERENCE_KEYS: Set<string>;
|
|
|
43
43
|
* A named reference (has string `name`) may carry extra keys (e.g. `inputs`)
|
|
44
44
|
* that are runtime call parameters — those are never inline resources. */
|
|
45
45
|
export declare function isInlineResource(val: Record<string, unknown>): boolean;
|
|
46
|
-
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
46
|
+
/** A value found at a field-map path, paired with the concrete path that
|
|
47
|
+
* produced it. `path` has every `[]` substituted with `[N]` and every `{}`
|
|
48
|
+
* substituted with the actual map key, matching the format produced by
|
|
49
|
+
* `buildPositionIndex`. Used so diagnostics emitted against a specific
|
|
50
|
+
* array element / map entry can be resolved back to a YAML range. */
|
|
51
|
+
export interface ResolvedFieldEntry {
|
|
52
|
+
value: unknown;
|
|
53
|
+
path: string;
|
|
54
|
+
}
|
|
55
|
+
/** Resolves all `{value, path}` entries at a field map path in a resource
|
|
56
|
+
* config. The returned `path` is the concrete dotted path produced by the
|
|
57
|
+
* substitutions below, matching the format `buildPositionIndex` keys on.
|
|
58
|
+
* Path-segment markers accepted in the input `path`:
|
|
59
|
+
* - `[]` iterate array values at this key, substituting `[N]` per item
|
|
60
|
+
* (e.g. `routes[]` → `routes[0]`, `routes[1]`, …).
|
|
49
61
|
* - `{}` iterate map values (every value in an `additionalProperties`-typed
|
|
50
62
|
* object — used for fields like `content[mime]` whose schema declares
|
|
51
|
-
* a key-as-MIME map).
|
|
63
|
+
* a key-as-MIME map). Substituted with the literal map key joined by
|
|
64
|
+
* a dot, so the input `content.{}.encoder` yields concrete paths
|
|
65
|
+
* like `content.application/json.encoder`. */
|
|
66
|
+
export declare function resolveFieldEntries(obj: unknown, path: string): ResolvedFieldEntry[];
|
|
67
|
+
/** Backwards-compat wrapper that drops the concrete path. Prefer
|
|
68
|
+
* `resolveFieldEntries` for new code that wants positions. */
|
|
52
69
|
export declare function resolveFieldValues(obj: unknown, path: string): unknown[];
|
|
53
70
|
/**
|
|
54
71
|
* Traverses a definition's JSON Schema once and returns a field map recording every
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reference-field-map.d.ts","sourceRoot":"","sources":["../src/reference-field-map.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B;sDACkD;IAClD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,0FAA0F;IAC1F,OAAO,EAAE,OAAO,CAAC;IACjB;8DAC0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B;2CACuC;IACvC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED;8CAC8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC;;qFAEiF;IACjF,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAEnF;0FAC0F;AAC1F,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE3D,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,aAAa,CAEvE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,eAAe,CAE3E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,oBAAoB,CAErF;AAED,oGAAoG;AACpG,eAAO,MAAM,cAAc,aAAwC,CAAC;AAEpE;;;;;;;;;2EAS2E;AAC3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAItE;AAED
|
|
1
|
+
{"version":3,"file":"reference-field-map.d.ts","sourceRoot":"","sources":["../src/reference-field-map.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B;sDACkD;IAClD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,0FAA0F;IAC1F,OAAO,EAAE,OAAO,CAAC;IACjB;8DAC0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B;2CACuC;IACvC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED;8CAC8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC;;qFAEiF;IACjF,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAEnF;0FAC0F;AAC1F,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE3D,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,aAAa,CAEvE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,eAAe,CAE3E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,oBAAoB,CAErF;AAED,oGAAoG;AACpG,eAAO,MAAM,cAAc,aAAwC,CAAC;AAEpE;;;;;;;;;2EAS2E;AAC3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAItE;AAED;;;;sEAIsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;wDAUwD;AACxD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAoCpF;AAED;+DAC+D;AAC/D,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAExE;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,iBAAiB,CAQrF;AAiBD;;;8CAG8C;AAC9C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,UAAU,EAAE,MAAM,GACjB,iBAAiB,CAInB"}
|
|
@@ -26,25 +26,30 @@ export function isInlineResource(val) {
|
|
|
26
26
|
return false;
|
|
27
27
|
return true;
|
|
28
28
|
}
|
|
29
|
-
/** Resolves all
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
/** Resolves all `{value, path}` entries at a field map path in a resource
|
|
30
|
+
* config. The returned `path` is the concrete dotted path produced by the
|
|
31
|
+
* substitutions below, matching the format `buildPositionIndex` keys on.
|
|
32
|
+
* Path-segment markers accepted in the input `path`:
|
|
33
|
+
* - `[]` iterate array values at this key, substituting `[N]` per item
|
|
34
|
+
* (e.g. `routes[]` → `routes[0]`, `routes[1]`, …).
|
|
32
35
|
* - `{}` iterate map values (every value in an `additionalProperties`-typed
|
|
33
36
|
* object — used for fields like `content[mime]` whose schema declares
|
|
34
|
-
* a key-as-MIME map).
|
|
35
|
-
|
|
37
|
+
* a key-as-MIME map). Substituted with the literal map key joined by
|
|
38
|
+
* a dot, so the input `content.{}.encoder` yields concrete paths
|
|
39
|
+
* like `content.application/json.encoder`. */
|
|
40
|
+
export function resolveFieldEntries(obj, path) {
|
|
36
41
|
const parts = path.split(".");
|
|
37
|
-
let current = [obj];
|
|
42
|
+
let current = [{ value: obj, path: "" }];
|
|
38
43
|
for (const part of parts) {
|
|
39
44
|
if (part === "{}") {
|
|
40
|
-
// Iterate the values of every map currently in `current`.
|
|
41
45
|
const next = [];
|
|
42
|
-
for (const
|
|
43
|
-
if (!
|
|
46
|
+
for (const entry of current) {
|
|
47
|
+
if (!entry.value || typeof entry.value !== "object")
|
|
44
48
|
continue;
|
|
45
|
-
for (const v of Object.
|
|
46
|
-
if (v != null)
|
|
47
|
-
next.push(v);
|
|
49
|
+
for (const [k, v] of Object.entries(entry.value)) {
|
|
50
|
+
if (v != null) {
|
|
51
|
+
next.push({ value: v, path: entry.path ? `${entry.path}.${k}` : k });
|
|
52
|
+
}
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
55
|
current = next;
|
|
@@ -53,21 +58,32 @@ export function resolveFieldValues(obj, path) {
|
|
|
53
58
|
const isArray = part.endsWith("[]");
|
|
54
59
|
const key = isArray ? part.slice(0, -2) : part;
|
|
55
60
|
const next = [];
|
|
56
|
-
for (const
|
|
57
|
-
if (!
|
|
61
|
+
for (const entry of current) {
|
|
62
|
+
if (!entry.value || typeof entry.value !== "object")
|
|
58
63
|
continue;
|
|
59
|
-
const val =
|
|
64
|
+
const val = entry.value[key];
|
|
60
65
|
if (val == null)
|
|
61
66
|
continue;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
const basePath = entry.path ? `${entry.path}.${key}` : key;
|
|
68
|
+
if (isArray && Array.isArray(val)) {
|
|
69
|
+
for (let i = 0; i < val.length; i++) {
|
|
70
|
+
if (val[i] != null)
|
|
71
|
+
next.push({ value: val[i], path: `${basePath}[${i}]` });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else if (!isArray) {
|
|
75
|
+
next.push({ value: val, path: basePath });
|
|
76
|
+
}
|
|
66
77
|
}
|
|
67
78
|
current = next;
|
|
68
79
|
}
|
|
69
80
|
return current;
|
|
70
81
|
}
|
|
82
|
+
/** Backwards-compat wrapper that drops the concrete path. Prefer
|
|
83
|
+
* `resolveFieldEntries` for new code that wants positions. */
|
|
84
|
+
export function resolveFieldValues(obj, path) {
|
|
85
|
+
return resolveFieldEntries(obj, path).map((e) => e.value);
|
|
86
|
+
}
|
|
71
87
|
/**
|
|
72
88
|
* Traverses a definition's JSON Schema once and returns a field map recording every
|
|
73
89
|
* x-telo-ref slot and every x-telo-scope slot.
|