@telorun/analyzer 0.11.0 → 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.
Files changed (92) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +3 -3
  3. package/dist/adapters/http-adapter.d.ts +10 -0
  4. package/dist/adapters/http-adapter.d.ts.map +1 -0
  5. package/dist/adapters/http-adapter.js +18 -0
  6. package/dist/adapters/node-adapter.d.ts +17 -0
  7. package/dist/adapters/node-adapter.d.ts.map +1 -0
  8. package/dist/adapters/node-adapter.js +71 -0
  9. package/dist/adapters/registry-adapter.d.ts +15 -0
  10. package/dist/adapters/registry-adapter.d.ts.map +1 -0
  11. package/dist/adapters/registry-adapter.js +53 -0
  12. package/dist/analysis-registry.d.ts +7 -0
  13. package/dist/analysis-registry.d.ts.map +1 -1
  14. package/dist/analysis-registry.js +38 -0
  15. package/dist/analyzer.d.ts +15 -0
  16. package/dist/analyzer.d.ts.map +1 -1
  17. package/dist/analyzer.js +114 -10
  18. package/dist/builtins.d.ts.map +1 -1
  19. package/dist/builtins.js +58 -1
  20. package/dist/definition-registry.d.ts.map +1 -1
  21. package/dist/definition-registry.js +16 -0
  22. package/dist/dependency-graph.d.ts.map +1 -1
  23. package/dist/dependency-graph.js +27 -13
  24. package/dist/index.d.ts +2 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +2 -0
  27. package/dist/kernel-globals.d.ts.map +1 -1
  28. package/dist/kernel-globals.js +9 -11
  29. package/dist/manifest-loader.d.ts +23 -1
  30. package/dist/manifest-loader.d.ts.map +1 -1
  31. package/dist/manifest-loader.js +66 -3
  32. package/dist/normalize-inline-resources.d.ts.map +1 -1
  33. package/dist/normalize-inline-resources.js +26 -14
  34. package/dist/position-metadata.d.ts +11 -2
  35. package/dist/position-metadata.d.ts.map +1 -1
  36. package/dist/position-metadata.js +18 -3
  37. package/dist/precompile.d.ts.map +1 -1
  38. package/dist/precompile.js +9 -1
  39. package/dist/reference-field-map.d.ts +21 -4
  40. package/dist/reference-field-map.d.ts.map +1 -1
  41. package/dist/reference-field-map.js +93 -25
  42. package/dist/residual-schema.d.ts +23 -0
  43. package/dist/residual-schema.d.ts.map +1 -0
  44. package/dist/residual-schema.js +45 -0
  45. package/dist/resolve-ref-sentinels.d.ts +27 -0
  46. package/dist/resolve-ref-sentinels.d.ts.map +1 -0
  47. package/dist/resolve-ref-sentinels.js +114 -0
  48. package/dist/rewrite-synthetic-origins.d.ts +10 -0
  49. package/dist/rewrite-synthetic-origins.d.ts.map +1 -0
  50. package/dist/rewrite-synthetic-origins.js +55 -0
  51. package/dist/schema-compat.d.ts +7 -1
  52. package/dist/schema-compat.d.ts.map +1 -1
  53. package/dist/schema-compat.js +19 -2
  54. package/dist/system-kinds.d.ts +25 -0
  55. package/dist/system-kinds.d.ts.map +1 -0
  56. package/dist/system-kinds.js +34 -0
  57. package/dist/types.d.ts +12 -0
  58. package/dist/types.d.ts.map +1 -1
  59. package/dist/validate-cel-context.d.ts +5 -0
  60. package/dist/validate-cel-context.d.ts.map +1 -1
  61. package/dist/validate-cel-context.js +27 -15
  62. package/dist/validate-provider-coherence.d.ts +23 -0
  63. package/dist/validate-provider-coherence.d.ts.map +1 -0
  64. package/dist/validate-provider-coherence.js +148 -0
  65. package/dist/validate-references.d.ts.map +1 -1
  66. package/dist/validate-references.js +141 -36
  67. package/dist/with-synthetic-positions.d.ts +28 -0
  68. package/dist/with-synthetic-positions.d.ts.map +1 -0
  69. package/dist/with-synthetic-positions.js +45 -0
  70. package/package.json +7 -4
  71. package/src/analysis-registry.ts +37 -0
  72. package/src/analyzer.ts +118 -12
  73. package/src/builtins.ts +58 -1
  74. package/src/definition-registry.ts +15 -0
  75. package/src/dependency-graph.ts +27 -14
  76. package/src/index.ts +2 -0
  77. package/src/kernel-globals.ts +9 -11
  78. package/src/manifest-loader.ts +69 -4
  79. package/src/normalize-inline-resources.ts +48 -13
  80. package/src/position-metadata.ts +18 -3
  81. package/src/precompile.ts +8 -1
  82. package/src/reference-field-map.ts +129 -24
  83. package/src/residual-schema.ts +49 -0
  84. package/src/resolve-ref-sentinels.ts +127 -0
  85. package/src/rewrite-synthetic-origins.ts +75 -0
  86. package/src/schema-compat.ts +19 -2
  87. package/src/system-kinds.ts +37 -0
  88. package/src/types.ts +12 -0
  89. package/src/validate-cel-context.ts +28 -15
  90. package/src/validate-provider-coherence.ts +166 -0
  91. package/src/validate-references.ts +138 -35
  92. package/src/with-synthetic-positions.ts +48 -0
@@ -1 +1 @@
1
- {"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../src/dependency-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGnE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B;kDAC8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACpC;oFACgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACrC;AAcD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,eAAe,CA6FjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,MAAM,CAOtE"}
1
+ {"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../src/dependency-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAInE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B;kDAC8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACpC;oFACgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACrC;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,eAAe,CAkHjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,MAAM,CAOtE"}
@@ -1,13 +1,6 @@
1
+ import { isRefSentinel } from "@telorun/templating";
1
2
  import { isRefEntry, isScopeEntry, resolveFieldValues } from "./reference-field-map.js";
2
- /** System resource kinds that are not runtime nodes in the dependency graph.
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 key = nodeKey(r.kind, r.metadata.name);
31
- nodes.set(key, { kind: r.kind, name: r.metadata.name });
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 || typeof val !== "object")
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
@@ -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;AAErD;;;;;;;;;;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;AA2BD;;;;;;;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"}
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"}
@@ -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. Falls back to
63
- * an open map when the module declares no variables/secrets. */
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
- if (!schemaMap || typeof schemaMap !== "object" || Array.isArray(schemaMap)) {
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
- private resolveImportUrl;
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;IAOrD;;;+BAG2B;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAcvE;;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,OAAO,CAAC,gBAAgB;IAOxB,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"}
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"}
@@ -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
- const { source } = await this.pick(url).read(url);
49
- return source;
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
- const cacheKey = `${options?.compile ? "compiled" : "raw"}:${source}`;
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,CAwDpB"}
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 traverse(obj, partsLeft, nameParts) {
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 = sanitizeName([parentName, ...nameParts, sanitizedKey].join("_"));
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 = sanitizeName([parentName, ...nameParts, key, elemId].join("_"));
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 = sanitizeName([parentName, ...nameParts, key].join("_"));
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 `---` directive. */
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;;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;+EAC+E;AAC/E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,aAAa,CAuCtF"}
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 `---` directive. */
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);
@@ -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,CAmCrE"}
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"}
@@ -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
- /** Resolves all values at a field map path in a resource config.
47
- * Path-segment markers:
48
- * - `[]` iterate array values at this key
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). The path is `<key>.{}.<rest>`. */
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;;;;;kEAKkE;AAClE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CA6BxE;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"}
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"}