@telorun/analyzer 0.10.1 → 0.12.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/adapters/http-adapter.d.ts +10 -0
- package/dist/adapters/http-adapter.d.ts.map +1 -0
- package/dist/adapters/http-adapter.js +18 -0
- package/dist/adapters/node-adapter.d.ts +17 -0
- package/dist/adapters/node-adapter.d.ts.map +1 -0
- package/dist/adapters/node-adapter.js +71 -0
- package/dist/adapters/registry-adapter.d.ts +15 -0
- package/dist/adapters/registry-adapter.d.ts.map +1 -0
- package/dist/adapters/registry-adapter.js +53 -0
- package/dist/analysis-registry.d.ts +7 -0
- package/dist/analysis-registry.d.ts.map +1 -1
- package/dist/analysis-registry.js +38 -0
- package/dist/analyzer.d.ts +15 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +268 -7
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +172 -1
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +16 -0
- package/dist/dependency-graph.d.ts.map +1 -1
- package/dist/dependency-graph.js +27 -13
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/kernel-globals.d.ts.map +1 -1
- package/dist/kernel-globals.js +9 -11
- package/dist/manifest-loader.d.ts +23 -1
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +66 -3
- package/dist/normalize-inline-resources.d.ts.map +1 -1
- package/dist/normalize-inline-resources.js +26 -14
- package/dist/position-metadata.d.ts +11 -2
- package/dist/position-metadata.d.ts.map +1 -1
- package/dist/position-metadata.js +18 -3
- package/dist/precompile.d.ts.map +1 -1
- package/dist/precompile.js +9 -1
- package/dist/reference-field-map.d.ts +21 -4
- package/dist/reference-field-map.d.ts.map +1 -1
- package/dist/reference-field-map.js +93 -25
- package/dist/residual-schema.d.ts +23 -0
- package/dist/residual-schema.d.ts.map +1 -0
- package/dist/residual-schema.js +45 -0
- package/dist/resolve-ref-sentinels.d.ts +27 -0
- package/dist/resolve-ref-sentinels.d.ts.map +1 -0
- package/dist/resolve-ref-sentinels.js +114 -0
- package/dist/rewrite-synthetic-origins.d.ts +10 -0
- package/dist/rewrite-synthetic-origins.d.ts.map +1 -0
- package/dist/rewrite-synthetic-origins.js +55 -0
- package/dist/schema-compat.d.ts +7 -1
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +19 -2
- package/dist/system-kinds.d.ts +25 -0
- package/dist/system-kinds.d.ts.map +1 -0
- package/dist/system-kinds.js +34 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validate-cel-context.d.ts +61 -7
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +90 -8
- package/dist/validate-provider-coherence.d.ts +23 -0
- package/dist/validate-provider-coherence.d.ts.map +1 -0
- package/dist/validate-provider-coherence.js +148 -0
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +141 -36
- package/dist/with-synthetic-positions.d.ts +28 -0
- package/dist/with-synthetic-positions.d.ts.map +1 -0
- package/dist/with-synthetic-positions.js +45 -0
- package/package.json +7 -4
- package/src/analysis-registry.ts +37 -0
- package/src/analyzer.ts +313 -9
- package/src/builtins.ts +172 -1
- package/src/definition-registry.ts +15 -0
- package/src/dependency-graph.ts +27 -14
- package/src/index.ts +2 -0
- package/src/kernel-globals.ts +9 -11
- package/src/manifest-loader.ts +69 -4
- package/src/normalize-inline-resources.ts +48 -13
- package/src/position-metadata.ts +18 -3
- package/src/precompile.ts +8 -1
- package/src/reference-field-map.ts +129 -24
- package/src/residual-schema.ts +49 -0
- package/src/resolve-ref-sentinels.ts +127 -0
- package/src/rewrite-synthetic-origins.ts +75 -0
- package/src/schema-compat.ts +19 -2
- package/src/system-kinds.ts +37 -0
- package/src/types.ts +12 -0
- package/src/validate-cel-context.ts +111 -8
- package/src/validate-provider-coherence.ts +166 -0
- package/src/validate-references.ts +138 -35
- package/src/with-synthetic-positions.ts +48 -0
package/dist/dependency-graph.js
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
+
import { isRefSentinel } from "@telorun/templating";
|
|
1
2
|
import { isRefEntry, isScopeEntry, resolveFieldValues } from "./reference-field-map.js";
|
|
2
|
-
|
|
3
|
-
* Module-identity docs (Telo.Application, Telo.Library) are intentionally
|
|
4
|
-
* not in this set: an Application's `targets` use `x-telo-ref` to real
|
|
5
|
-
* Runnable/Service resources, so the Application legitimately depends on
|
|
6
|
-
* them in boot order — modeling that as a graph edge is correct. A Library
|
|
7
|
-
* has no `targets`, so it becomes a zero-edge node, which is harmless.
|
|
8
|
-
* If the graph is ever consumed as "things to init", skip these kinds at
|
|
9
|
-
* the consumer site; the controller already runs them separately. */
|
|
10
|
-
const SYSTEM_KINDS = new Set(["Telo.Definition", "Telo.Import"]);
|
|
3
|
+
import { DEPENDENCY_GRAPH_SKIP_KINDS as SYSTEM_KINDS } from "./system-kinds.js";
|
|
11
4
|
const nodeKey = (kind, name) => `${kind}\0${name}`;
|
|
12
5
|
/**
|
|
13
6
|
* Builds a directed acyclic graph (DAG) of runtime resource dependencies and
|
|
@@ -22,13 +15,19 @@ const nodeKey = (kind, name) => `${kind}\0${name}`;
|
|
|
22
15
|
* not pre-compute or pass field maps separately.
|
|
23
16
|
*/
|
|
24
17
|
export function buildDependencyGraph(resources, registry, aliases, aliasesByModule) {
|
|
25
|
-
// --- Build node set ---
|
|
18
|
+
// --- Build node set + name index ---
|
|
26
19
|
const nodes = new Map();
|
|
20
|
+
// Sentinel lookup (`!ref <name>`) needs to resolve a bare name to its
|
|
21
|
+
// declared kind. Names are unique within a manifest scope, so a flat
|
|
22
|
+
// map suffices and lets the sentinel branch below avoid a full
|
|
23
|
+
// O(N) scan of the node set on every reference.
|
|
24
|
+
const nodesByName = new Map();
|
|
27
25
|
for (const r of resources) {
|
|
28
26
|
if (!r.metadata?.name || !r.kind || SYSTEM_KINDS.has(r.kind))
|
|
29
27
|
continue;
|
|
30
|
-
const
|
|
31
|
-
nodes.set(
|
|
28
|
+
const node = { kind: r.kind, name: r.metadata.name };
|
|
29
|
+
nodes.set(nodeKey(node.kind, node.name), node);
|
|
30
|
+
nodesByName.set(node.name, node);
|
|
32
31
|
}
|
|
33
32
|
// --- Build adjacency: from → deps (from depends on dep) ---
|
|
34
33
|
const deps = new Map();
|
|
@@ -66,7 +65,22 @@ export function buildDependencyGraph(resources, registry, aliases, aliasesByModu
|
|
|
66
65
|
if (!isRefEntry(entry))
|
|
67
66
|
continue;
|
|
68
67
|
for (const val of resolveFieldValues(r, fieldPath)) {
|
|
69
|
-
if (!val
|
|
68
|
+
if (!val)
|
|
69
|
+
continue;
|
|
70
|
+
// `!ref <name>` sentinel — look up the target's kind from the
|
|
71
|
+
// name (resources are unique by name) so the edge carries the
|
|
72
|
+
// concrete kind, matching the {kind, name} edge shape below.
|
|
73
|
+
if (isRefSentinel(val)) {
|
|
74
|
+
const refName = val.source;
|
|
75
|
+
if (scopedNames.has(refName))
|
|
76
|
+
continue;
|
|
77
|
+
const node = nodesByName.get(refName);
|
|
78
|
+
if (node) {
|
|
79
|
+
deps.get(sourceKey).add(nodeKey(node.kind, node.name));
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (typeof val !== "object")
|
|
70
84
|
continue;
|
|
71
85
|
const ref = val;
|
|
72
86
|
if (!ref.kind || !ref.name)
|
package/dist/index.d.ts
CHANGED
|
@@ -7,10 +7,12 @@ 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";
|
|
13
14
|
export { RegistrySource } from "./sources/registry-source.js";
|
|
15
|
+
export { withSyntheticPositions } from "./with-synthetic-positions.js";
|
|
14
16
|
export { DEFAULT_MANIFEST_FILENAME, DiagnosticSeverity } from "./types.js";
|
|
15
17
|
export type { AnalysisDiagnostic, AnalysisOptions, LoaderInitOptions, LoadOptions, ManifestSource, Position, PositionIndex, Range } from "./types.js";
|
|
16
18
|
//# sourceMappingURL=index.d.ts.map
|
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,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,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,7 +4,9 @@ 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";
|
|
11
|
+
export { withSyntheticPositions } from "./with-synthetic-positions.js";
|
|
10
12
|
export { DEFAULT_MANIFEST_FILENAME, DiagnosticSeverity } from "./types.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
|
}
|
|
@@ -15,13 +15,22 @@ export interface DocumentPosition {
|
|
|
15
15
|
export declare function buildDocumentPositions(text: string, parsedDocs: Document[]): DocumentPosition[];
|
|
16
16
|
/** Line numbers (0-indexed) where each YAML document in a multi-doc file
|
|
17
17
|
* starts. The first document is always at line 0; subsequent entries point
|
|
18
|
-
* to the line after each `---`
|
|
18
|
+
* to the line after each `---` separator.
|
|
19
|
+
*
|
|
20
|
+
* A `---` at line 0 is the doc-start marker for doc 0 (the parser still
|
|
21
|
+
* emits a single document), not a separator before an empty doc — skipping
|
|
22
|
+
* it keeps `offsets.length === parsedDocs.length` so diagnostics for doc N
|
|
23
|
+
* don't land inside doc N-1's text. */
|
|
19
24
|
export declare function documentLineOffsets(text: string): number[];
|
|
20
25
|
/** Byte-offset → start-of-line lookup table. Index `i` is the byte offset of
|
|
21
26
|
* the first character on line `i`. Used with `offsetToPosition` to turn a
|
|
22
27
|
* yaml-AST node range into Range coordinates. */
|
|
23
28
|
export declare function buildLineOffsets(text: string): number[];
|
|
24
29
|
/** 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").
|
|
30
|
+
* by dotted path (e.g. "kind", "config.handler", "config.routes[0].path").
|
|
31
|
+
* Map keys are also recorded under the `@key:<path>` namespace so diagnostic
|
|
32
|
+
* resolvers can squiggle just the key identifier instead of the full value
|
|
33
|
+
* block — used when a diagnostic targets a missing child property and the
|
|
34
|
+
* resolver has to fall back to the parent. */
|
|
26
35
|
export declare function buildPositionIndex(doc: Document, lineOffsets: number[]): PositionIndex;
|
|
27
36
|
//# 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
|
|
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;;;;;;;wCAOwC;AACxC,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAW1D;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"}
|
|
@@ -10,14 +10,22 @@ export function buildDocumentPositions(text, parsedDocs) {
|
|
|
10
10
|
}
|
|
11
11
|
/** Line numbers (0-indexed) where each YAML document in a multi-doc file
|
|
12
12
|
* starts. The first document is always at line 0; subsequent entries point
|
|
13
|
-
* to the line after each `---`
|
|
13
|
+
* to the line after each `---` separator.
|
|
14
|
+
*
|
|
15
|
+
* A `---` at line 0 is the doc-start marker for doc 0 (the parser still
|
|
16
|
+
* emits a single document), not a separator before an empty doc — skipping
|
|
17
|
+
* it keeps `offsets.length === parsedDocs.length` so diagnostics for doc N
|
|
18
|
+
* don't land inside doc N-1's text. */
|
|
14
19
|
export function documentLineOffsets(text) {
|
|
15
20
|
const offsets = [0];
|
|
16
21
|
const lines = text.split("\n");
|
|
17
22
|
for (let i = 0; i < lines.length; i++) {
|
|
18
23
|
const t = lines[i].trimEnd();
|
|
19
|
-
if (t === "---" || t.startsWith("--- "))
|
|
24
|
+
if (t === "---" || t.startsWith("--- ")) {
|
|
25
|
+
if (i === 0)
|
|
26
|
+
continue;
|
|
20
27
|
offsets.push(i + 1);
|
|
28
|
+
}
|
|
21
29
|
}
|
|
22
30
|
return offsets;
|
|
23
31
|
}
|
|
@@ -45,7 +53,11 @@ function offsetToPosition(offset, lineOffsets) {
|
|
|
45
53
|
return { line: lo, character: offset - lineOffsets[lo] };
|
|
46
54
|
}
|
|
47
55
|
/** 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").
|
|
56
|
+
* by dotted path (e.g. "kind", "config.handler", "config.routes[0].path").
|
|
57
|
+
* Map keys are also recorded under the `@key:<path>` namespace so diagnostic
|
|
58
|
+
* resolvers can squiggle just the key identifier instead of the full value
|
|
59
|
+
* block — used when a diagnostic targets a missing child property and the
|
|
60
|
+
* resolver has to fall back to the parent. */
|
|
49
61
|
export function buildPositionIndex(doc, lineOffsets) {
|
|
50
62
|
const index = new Map();
|
|
51
63
|
function recordNode(node, path) {
|
|
@@ -66,6 +78,9 @@ export function buildPositionIndex(doc, lineOffsets) {
|
|
|
66
78
|
if (key == null)
|
|
67
79
|
continue;
|
|
68
80
|
const childPath = path ? `${path}.${key}` : key;
|
|
81
|
+
if (pair.key && pair.key.range) {
|
|
82
|
+
recordNode(pair.key, `@key:${childPath}`);
|
|
83
|
+
}
|
|
69
84
|
if (pair.value != null) {
|
|
70
85
|
recordNode(pair.value, childPath);
|
|
71
86
|
walk(pair.value, childPath);
|
package/dist/precompile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"precompile.d.ts","sourceRoot":"","sources":["../src/precompile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIxD;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"precompile.d.ts","sourceRoot":"","sources":["../src/precompile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIxD;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CA0CrE"}
|
package/dist/precompile.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isCompiledValue } from "@telorun/sdk";
|
|
2
|
-
import { compileString, defaultRegistry, isTaggedSentinel } from "@telorun/templating";
|
|
2
|
+
import { compileString, defaultRegistry, isRefSentinel, isTaggedSentinel } from "@telorun/templating";
|
|
3
3
|
/**
|
|
4
4
|
* Walks a raw YAML document and replaces all `${{ expr }}` strings (and
|
|
5
5
|
* `!cel`-tagged sentinels) with CompiledValue wrappers. Throws on CEL syntax
|
|
@@ -19,6 +19,14 @@ export function precompileDoc(doc, env) {
|
|
|
19
19
|
// analyzer's diagnostic walk can identify it on compiled trees too;
|
|
20
20
|
// engines returning plain values (e.g. `literal` → a string) pass through
|
|
21
21
|
// verbatim — the runtime contract is "any scalar value is fine."
|
|
22
|
+
// `!ref` sentinels are identity markers, not templating values. They must
|
|
23
|
+
// survive precompile intact so the analyzer's `resolveRefSentinels` pass
|
|
24
|
+
// can substitute them with `{kind, name}` objects against the resolved
|
|
25
|
+
// resource manifest. Running the engine's `compile` here would prematurely
|
|
26
|
+
// collapse the sentinel into its source string and the ref slot would
|
|
27
|
+
// arrive at the controller as a bare name with no kind.
|
|
28
|
+
if (isRefSentinel(doc))
|
|
29
|
+
return doc;
|
|
22
30
|
if (isTaggedSentinel(doc)) {
|
|
23
31
|
const engine = defaultRegistry().get(doc.engine);
|
|
24
32
|
if (!engine) {
|
|
@@ -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"}
|