@telorun/analyzer 0.11.0 → 1.2.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/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.map +1 -1
- package/dist/analyzer.js +53 -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/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 +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/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-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 +7 -4
- package/src/analysis-registry.ts +37 -0
- package/src/analyzer.ts +55 -11
- package/src/builtins.ts +44 -1
- package/src/index.ts +1 -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 +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/types.ts +12 -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/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.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;IAudvB,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.";
|
|
@@ -490,6 +492,15 @@ export class StaticAnalyzer {
|
|
|
490
492
|
}
|
|
491
493
|
// Phase 2: extract inline resources from x-telo-ref slots into first-class manifests
|
|
492
494
|
const allManifests = normalizeInlineResources(manifests, defs, aliases, aliasesByModule);
|
|
495
|
+
// Trusted-input fast path: when the caller has already attested that
|
|
496
|
+
// this exact manifest set passes analysis (e.g. via the kernel's
|
|
497
|
+
// hash-stamped `.validated.json` cache), skip the validation walk.
|
|
498
|
+
// Registration of identities / aliases / definitions and inline-resource
|
|
499
|
+
// normalisation have already run above; that's all downstream
|
|
500
|
+
// consumers (prepare, init loop) require.
|
|
501
|
+
if (options?.skipValidation) {
|
|
502
|
+
return diagnostics;
|
|
503
|
+
}
|
|
493
504
|
// Build a name→manifest map for looking up referenced resources
|
|
494
505
|
const byName = new Map();
|
|
495
506
|
for (const m of allManifests) {
|
|
@@ -497,6 +508,36 @@ export class StaticAnalyzer {
|
|
|
497
508
|
byName.set(m.metadata.name, m);
|
|
498
509
|
}
|
|
499
510
|
}
|
|
511
|
+
// Library env: rejection — `env:` on a Library `variables` / `secrets`
|
|
512
|
+
// entry is forbidden. The Library entry schema is otherwise open so that
|
|
513
|
+
// any JSON Schema property schema is valid; this targeted check produces
|
|
514
|
+
// a clear diagnostic instead of a generic "additional property" error.
|
|
515
|
+
for (const m of allManifests) {
|
|
516
|
+
if (m.kind !== "Telo.Library")
|
|
517
|
+
continue;
|
|
518
|
+
const filePath = m.metadata?.source;
|
|
519
|
+
const moduleName = m.metadata?.name;
|
|
520
|
+
const resource = moduleName ? { kind: m.kind, name: moduleName } : undefined;
|
|
521
|
+
for (const block of ["variables", "secrets"]) {
|
|
522
|
+
const entries = m[block];
|
|
523
|
+
if (!entries || typeof entries !== "object" || Array.isArray(entries))
|
|
524
|
+
continue;
|
|
525
|
+
for (const [entryName, entry] of Object.entries(entries)) {
|
|
526
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
527
|
+
continue;
|
|
528
|
+
if ("env" in entry) {
|
|
529
|
+
diagnostics.push({
|
|
530
|
+
severity: DiagnosticSeverity.Error,
|
|
531
|
+
code: "LIBRARY_ENV_KEY_REJECTED",
|
|
532
|
+
source: SOURCE,
|
|
533
|
+
message: `Telo.Library ${block}/${entryName}: 'env:' is only permitted on Telo.Application entries. ` +
|
|
534
|
+
`Libraries must receive values from importers via the parent manifest's variables / secrets block.`,
|
|
535
|
+
data: { resource, filePath, path: `${block}.${entryName}.env` },
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
500
541
|
// Build typed kernel globals schema so x-telo-context chain validation
|
|
501
542
|
// recognises variables, secrets, resources, env automatically
|
|
502
543
|
const kernelGlobals = buildKernelGlobalsSchema(allManifests);
|
|
@@ -622,15 +663,14 @@ export class StaticAnalyzer {
|
|
|
622
663
|
emitTargetMismatch(dispatchKind, targetSchema, md.inputs, "inputs");
|
|
623
664
|
}
|
|
624
665
|
}
|
|
625
|
-
// Top-level `result:`
|
|
626
|
-
//
|
|
627
|
-
//
|
|
666
|
+
// Top-level `result:` is a post-call mapping that must satisfy the abstract
|
|
667
|
+
// this definition `extends` (`outputType`). It's a sibling of whichever
|
|
668
|
+
// dispatch entry-point declared a kind-typed target (`provide:` or
|
|
669
|
+
// `invoke:`). The target's outputType lives on the dispatcher's `kind`
|
|
628
670
|
// and is what `result` is typed against *inside* CEL — separate role.
|
|
629
|
-
|
|
630
|
-
typeof
|
|
631
|
-
|
|
632
|
-
md.result &&
|
|
633
|
-
typeof md.result === "object") {
|
|
671
|
+
const hasDispatchObject = (provide && typeof provide === "object" && !Array.isArray(provide)) ||
|
|
672
|
+
(invoke && typeof invoke === "object" && !Array.isArray(invoke));
|
|
673
|
+
if (hasDispatchObject && md.result && typeof md.result === "object") {
|
|
634
674
|
const extendsValue = md.extends;
|
|
635
675
|
if (typeof extendsValue === "string" && extendsValue.length > 0) {
|
|
636
676
|
const abstractSchema = lookupDefinitionTypeField(extendsValue, "outputType", defs, aliases, allManifests);
|
|
@@ -735,9 +775,13 @@ export class StaticAnalyzer {
|
|
|
735
775
|
diagnostics.push(...validateReferences(allManifests, { aliases, definitions: defs, aliasesByModule }));
|
|
736
776
|
// Validate `extends` fields and flag legacy `capability: <UserAbstract>` overload.
|
|
737
777
|
diagnostics.push(...validateExtends(allManifests, defs, aliases));
|
|
778
|
+
// Validate provider coherence rules for `provide:` template-target definitions.
|
|
779
|
+
diagnostics.push(...validateProviderCoherence(allManifests, defs, aliases));
|
|
738
780
|
// Validate throws: declarations and catches: coverage (rules 1, 2, 4, 7)
|
|
739
781
|
diagnostics.push(...validateThrowsCoverage(allManifests, defs, aliases, this.celEnv));
|
|
740
|
-
|
|
782
|
+
// Reroute diagnostics on synthetic (inline-extracted) resources back to
|
|
783
|
+
// the chain root so position-index lookups land on the parent doc.
|
|
784
|
+
return rewriteSyntheticOrigins(diagnostics, allManifests);
|
|
741
785
|
}
|
|
742
786
|
analyzeErrors(manifests, options, registry) {
|
|
743
787
|
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
|
}
|
|
@@ -6,12 +6,25 @@ export declare class Loader {
|
|
|
6
6
|
* caller (kernel) and a raw-mode caller (analyzer/editor) on the same file
|
|
7
7
|
* get distinct entries, so neither sees the wrong manifest tree. */
|
|
8
8
|
private readonly fileCache;
|
|
9
|
+
/** requestUrl → canonical `source`. Lets `loadFile` skip the source read
|
|
10
|
+
* when a URL it has already canonicalised is requested again — kernel
|
|
11
|
+
* load → boot and the import-controller each ask the loader for the same
|
|
12
|
+
* modules. Without this fast path every duplicate request re-runs the
|
|
13
|
+
* source's `read()` (a `fetch` for `RegistrySource`, a disk read for
|
|
14
|
+
* `LocalFileSource`). */
|
|
15
|
+
private readonly urlToSource;
|
|
9
16
|
protected sources: ManifestSource[];
|
|
10
17
|
private readonly celEnv;
|
|
11
18
|
constructor(extraSourcesOrOptions?: ManifestSource[] | LoaderInitOptions);
|
|
12
19
|
register(source: ManifestSource): this;
|
|
13
20
|
private pick;
|
|
14
21
|
resolveEntryPoint(url: string): Promise<string>;
|
|
22
|
+
/** Returns the canonical source URL the loader has already mapped `url`
|
|
23
|
+
* to during a prior `loadFile`/`loadModule`/`loadGraph` call, or
|
|
24
|
+
* `undefined` when the URL has not been seen. Callers use it to test
|
|
25
|
+
* set-membership against a previous graph walk's modules without
|
|
26
|
+
* triggering an extra source read. */
|
|
27
|
+
canonicalize(url: string): string | undefined;
|
|
15
28
|
/** Read one file via the source chain and parse it into a LoadedFile.
|
|
16
29
|
* The result is shared with `Loader.fileCache`. Callers that want a
|
|
17
30
|
* private mutable copy must call `parseLoadedFile` directly with the
|
|
@@ -26,7 +39,16 @@ export declare class Loader {
|
|
|
26
39
|
* `importEdges` mapping each importing file's PascalCase aliases to their
|
|
27
40
|
* target's canonical source. */
|
|
28
41
|
loadGraph(entryUrl: string, options?: LoadOptions): Promise<LoadedGraph>;
|
|
29
|
-
|
|
42
|
+
/** Resolve an `import` URL against the file it appears in. Relative /
|
|
43
|
+
* absolute-path forms run through the owning `ManifestSource`'s
|
|
44
|
+
* `resolveRelative`; registry refs and full URLs pass through
|
|
45
|
+
* unchanged. Exposed so the import-controller (and any other
|
|
46
|
+
* caller-side resolver) lands on the *exact same* canonical URL the
|
|
47
|
+
* loader used when walking the entry graph — divergent resolution
|
|
48
|
+
* would silently break optimizations like `canonicalize()`-keyed
|
|
49
|
+
* cache hits whenever a non-trivial `ManifestSource.resolveRelative`
|
|
50
|
+
* is in play. */
|
|
51
|
+
resolveImportUrl(fromSource: string, importSource: string): string;
|
|
30
52
|
private assertSingleModuleDeclaration;
|
|
31
53
|
private assertNoSystemKindsInPartialContext;
|
|
32
54
|
private assertImportTargetIsLibrary;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-loader.d.ts","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EACX,YAAY,EACb,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AASpB,qBAAa,MAAM;IACjB;;;yEAGqE;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAE3D,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,qBAAqB,GAAE,cAAc,EAAE,GAAG,iBAAsB;IAmB5E,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAKtC,OAAO,CAAC,IAAI;IAMN,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest-loader.d.ts","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EACX,YAAY,EACb,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AASpB,qBAAa,MAAM;IACjB;;;yEAGqE;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAE3D;;;;;8BAK0B;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAEzD,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,qBAAqB,GAAE,cAAc,EAAE,GAAG,iBAAsB;IAmB5E,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAKtC,OAAO,CAAC,IAAI;IAMN,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUrD;;;;2CAIuC;IACvC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAM7C;;;+BAG2B;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAgDvE;;wEAEoE;IAC9D,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAsB3E;;;qCAGiC;IAC3B,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAwG9E;;;;;;;;sBAQkB;IAClB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAOlE,OAAO,CAAC,6BAA6B;IAarC,OAAO,CAAC,mCAAmC;IAc3C,OAAO,CAAC,2BAA2B;YAkCrB,eAAe;IAmB7B;;;0CAGsC;IAChC,gBAAgB,CACpB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CA0B5D"}
|
package/dist/manifest-loader.js
CHANGED
|
@@ -16,6 +16,13 @@ export class Loader {
|
|
|
16
16
|
* caller (kernel) and a raw-mode caller (analyzer/editor) on the same file
|
|
17
17
|
* get distinct entries, so neither sees the wrong manifest tree. */
|
|
18
18
|
fileCache = new Map();
|
|
19
|
+
/** requestUrl → canonical `source`. Lets `loadFile` skip the source read
|
|
20
|
+
* when a URL it has already canonicalised is requested again — kernel
|
|
21
|
+
* load → boot and the import-controller each ask the loader for the same
|
|
22
|
+
* modules. Without this fast path every duplicate request re-runs the
|
|
23
|
+
* source's `read()` (a `fetch` for `RegistrySource`, a disk read for
|
|
24
|
+
* `LocalFileSource`). */
|
|
25
|
+
urlToSource = new Map();
|
|
19
26
|
sources;
|
|
20
27
|
celEnv;
|
|
21
28
|
constructor(extraSourcesOrOptions = []) {
|
|
@@ -45,8 +52,21 @@ export class Loader {
|
|
|
45
52
|
return s;
|
|
46
53
|
}
|
|
47
54
|
async resolveEntryPoint(url) {
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
// Route through `loadFile` so the resolved source URL and parsed
|
|
56
|
+
// entry are populated in `urlToSource` + `fileCache` in one read.
|
|
57
|
+
// Callers (kernel.load) immediately call `loadGraph(entryUrl)`
|
|
58
|
+
// afterwards — without this priming, the entry file would be read
|
|
59
|
+
// twice (twice over the network for `RegistrySource`).
|
|
60
|
+
const file = await this.loadFile(url);
|
|
61
|
+
return file.source;
|
|
62
|
+
}
|
|
63
|
+
/** Returns the canonical source URL the loader has already mapped `url`
|
|
64
|
+
* to during a prior `loadFile`/`loadModule`/`loadGraph` call, or
|
|
65
|
+
* `undefined` when the URL has not been seen. Callers use it to test
|
|
66
|
+
* set-membership against a previous graph walk's modules without
|
|
67
|
+
* triggering an extra source read. */
|
|
68
|
+
canonicalize(url) {
|
|
69
|
+
return this.urlToSource.get(url);
|
|
50
70
|
}
|
|
51
71
|
// --- New API: returns LoadedFile / LoadedModule / LoadedGraph ----------
|
|
52
72
|
/** Read one file via the source chain and parse it into a LoadedFile.
|
|
@@ -54,8 +74,42 @@ export class Loader {
|
|
|
54
74
|
* private mutable copy must call `parseLoadedFile` directly with the
|
|
55
75
|
* LoadedFile's `text`. */
|
|
56
76
|
async loadFile(url, options) {
|
|
77
|
+
const compileKey = options?.compile ? "compiled" : "raw";
|
|
78
|
+
const knownSource = this.urlToSource.get(url);
|
|
79
|
+
if (knownSource) {
|
|
80
|
+
const cached = this.fileCache.get(`${compileKey}:${knownSource}`);
|
|
81
|
+
if (cached)
|
|
82
|
+
return cached;
|
|
83
|
+
// The other compile-mode entry is cached — reparse from its text
|
|
84
|
+
// instead of re-reading the source.
|
|
85
|
+
//
|
|
86
|
+
// NOTE for watch-mode reactivation (cli/nodejs/src/commands/run.ts
|
|
87
|
+
// currently has `setupWatchMode` commented out): this branch
|
|
88
|
+
// assumes file contents don't change underneath a single Loader.
|
|
89
|
+
// Reviving watch mode will need a public `invalidate(url)` (or
|
|
90
|
+
// similar) that drops both `urlToSource[url]` and the cached
|
|
91
|
+
// entries for its canonical source before the loader serves the
|
|
92
|
+
// file again.
|
|
93
|
+
const altKey = `${compileKey === "compiled" ? "raw" : "compiled"}:${knownSource}`;
|
|
94
|
+
const alt = this.fileCache.get(altKey);
|
|
95
|
+
if (alt) {
|
|
96
|
+
const reparsed = parseLoadedFile(knownSource, url, alt.text, {
|
|
97
|
+
compile: options?.compile,
|
|
98
|
+
celEnv: this.celEnv,
|
|
99
|
+
});
|
|
100
|
+
this.fileCache.set(`${compileKey}:${knownSource}`, reparsed);
|
|
101
|
+
return reparsed;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
57
104
|
const { text, source } = await this.pick(url).read(url);
|
|
58
|
-
|
|
105
|
+
this.urlToSource.set(url, source);
|
|
106
|
+
// Also map the canonical source to itself so subsequent `loadFile`
|
|
107
|
+
// calls that already received a canonical URL — `kernel.load` passes
|
|
108
|
+
// the result of `resolveEntryPoint` to `loadGraph`, which then asks
|
|
109
|
+
// for that exact URL — hit the urlToSource fast path instead of
|
|
110
|
+
// falling through to a redundant `pick(url).read(url)`.
|
|
111
|
+
this.urlToSource.set(source, source);
|
|
112
|
+
const cacheKey = `${compileKey}:${source}`;
|
|
59
113
|
const cached = this.fileCache.get(cacheKey);
|
|
60
114
|
if (cached && cached.text === text)
|
|
61
115
|
return cached;
|
|
@@ -190,6 +244,15 @@ export class Loader {
|
|
|
190
244
|
}
|
|
191
245
|
return { rootSource, entry, modules, importEdges, errors };
|
|
192
246
|
}
|
|
247
|
+
/** Resolve an `import` URL against the file it appears in. Relative /
|
|
248
|
+
* absolute-path forms run through the owning `ManifestSource`'s
|
|
249
|
+
* `resolveRelative`; registry refs and full URLs pass through
|
|
250
|
+
* unchanged. Exposed so the import-controller (and any other
|
|
251
|
+
* caller-side resolver) lands on the *exact same* canonical URL the
|
|
252
|
+
* loader used when walking the entry graph — divergent resolution
|
|
253
|
+
* would silently break optimizations like `canonicalize()`-keyed
|
|
254
|
+
* cache hits whenever a non-trivial `ManifestSource.resolveRelative`
|
|
255
|
+
* is in play. */
|
|
193
256
|
resolveImportUrl(fromSource, importSource) {
|
|
194
257
|
if (importSource.startsWith(".") || importSource.startsWith("/")) {
|
|
195
258
|
return this.pick(fromSource).resolveRelative(fromSource, importSource);
|
|
@@ -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
|