@telorun/analyzer 0.11.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +3 -3
  2. package/dist/analysis-registry.d.ts +7 -0
  3. package/dist/analysis-registry.d.ts.map +1 -1
  4. package/dist/analysis-registry.js +38 -0
  5. package/dist/analyzer.d.ts.map +1 -1
  6. package/dist/analyzer.js +44 -9
  7. package/dist/builtins.d.ts.map +1 -1
  8. package/dist/builtins.js +44 -1
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -0
  12. package/dist/kernel-globals.d.ts.map +1 -1
  13. package/dist/kernel-globals.js +9 -11
  14. package/dist/normalize-inline-resources.d.ts.map +1 -1
  15. package/dist/normalize-inline-resources.js +26 -14
  16. package/dist/position-metadata.d.ts +5 -1
  17. package/dist/position-metadata.d.ts.map +1 -1
  18. package/dist/position-metadata.js +8 -1
  19. package/dist/reference-field-map.d.ts +21 -4
  20. package/dist/reference-field-map.d.ts.map +1 -1
  21. package/dist/reference-field-map.js +35 -19
  22. package/dist/residual-schema.d.ts +23 -0
  23. package/dist/residual-schema.d.ts.map +1 -0
  24. package/dist/residual-schema.js +45 -0
  25. package/dist/rewrite-synthetic-origins.d.ts +10 -0
  26. package/dist/rewrite-synthetic-origins.d.ts.map +1 -0
  27. package/dist/rewrite-synthetic-origins.js +55 -0
  28. package/dist/validate-cel-context.d.ts +5 -0
  29. package/dist/validate-cel-context.d.ts.map +1 -1
  30. package/dist/validate-cel-context.js +27 -15
  31. package/dist/validate-provider-coherence.d.ts +23 -0
  32. package/dist/validate-provider-coherence.d.ts.map +1 -0
  33. package/dist/validate-provider-coherence.js +148 -0
  34. package/dist/validate-references.js +24 -24
  35. package/package.json +5 -3
  36. package/src/analysis-registry.ts +37 -0
  37. package/src/analyzer.ts +45 -11
  38. package/src/builtins.ts +44 -1
  39. package/src/index.ts +1 -0
  40. package/src/kernel-globals.ts +9 -11
  41. package/src/normalize-inline-resources.ts +48 -13
  42. package/src/position-metadata.ts +8 -1
  43. package/src/reference-field-map.ts +46 -18
  44. package/src/residual-schema.ts +49 -0
  45. package/src/rewrite-synthetic-origins.ts +75 -0
  46. package/src/validate-cel-context.ts +28 -15
  47. package/src/validate-provider-coherence.ts +166 -0
  48. package/src/validate-references.ts +24 -24
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="./assets/telo.png" alt="Telo" width="200" />
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: ../modules/http-server
64
+ source: std/http-server@0.4.0
65
65
  ---
66
66
  kind: Telo.Import
67
67
  metadata:
68
68
  name: Sql
69
- source: ../modules/sql
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 };
@@ -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;AAa9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA8c/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;IA6avB,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"}
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAc9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA+c/F,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,OAAO,GAAE,qBAA0B;IAI/C,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IA6cvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAMvB,SAAS,CAAC,SAAS,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,EAAE;IAUxF,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,GACzB;QAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAsB5F"}
package/dist/analyzer.js CHANGED
@@ -7,10 +7,12 @@ import { buildKernelGlobalsSchema, mergeKernelGlobalsIntoContext } from "./kerne
7
7
  import { computeSuggestKind } from "./kind-suggest.js";
8
8
  import { isModuleKind } from "./module-kinds.js";
9
9
  import { normalizeInlineResources } from "./normalize-inline-resources.js";
10
+ import { rewriteSyntheticOrigins } from "./rewrite-synthetic-origins.js";
10
11
  import { celTypeSatisfiesJsonSchema, substituteCelFields, validateAgainstSchema, } from "./schema-compat.js";
11
12
  import { DiagnosticSeverity } from "./types.js";
12
13
  import { getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
13
14
  import { validateExtends } from "./validate-extends.js";
15
+ import { validateProviderCoherence } from "./validate-provider-coherence.js";
14
16
  import { validateReferences } from "./validate-references.js";
15
17
  import { validateThrowsCoverage } from "./validate-throws-coverage.js";
16
18
  const SELF_PREFIX = "Self.";
@@ -497,6 +499,36 @@ export class StaticAnalyzer {
497
499
  byName.set(m.metadata.name, m);
498
500
  }
499
501
  }
502
+ // Library env: rejection — `env:` on a Library `variables` / `secrets`
503
+ // entry is forbidden. The Library entry schema is otherwise open so that
504
+ // any JSON Schema property schema is valid; this targeted check produces
505
+ // a clear diagnostic instead of a generic "additional property" error.
506
+ for (const m of allManifests) {
507
+ if (m.kind !== "Telo.Library")
508
+ continue;
509
+ const filePath = m.metadata?.source;
510
+ const moduleName = m.metadata?.name;
511
+ const resource = moduleName ? { kind: m.kind, name: moduleName } : undefined;
512
+ for (const block of ["variables", "secrets"]) {
513
+ const entries = m[block];
514
+ if (!entries || typeof entries !== "object" || Array.isArray(entries))
515
+ continue;
516
+ for (const [entryName, entry] of Object.entries(entries)) {
517
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
518
+ continue;
519
+ if ("env" in entry) {
520
+ diagnostics.push({
521
+ severity: DiagnosticSeverity.Error,
522
+ code: "LIBRARY_ENV_KEY_REJECTED",
523
+ source: SOURCE,
524
+ message: `Telo.Library ${block}/${entryName}: 'env:' is only permitted on Telo.Application entries. ` +
525
+ `Libraries must receive values from importers via the parent manifest's variables / secrets block.`,
526
+ data: { resource, filePath, path: `${block}.${entryName}.env` },
527
+ });
528
+ }
529
+ }
530
+ }
531
+ }
500
532
  // Build typed kernel globals schema so x-telo-context chain validation
501
533
  // recognises variables, secrets, resources, env automatically
502
534
  const kernelGlobals = buildKernelGlobalsSchema(allManifests);
@@ -622,15 +654,14 @@ export class StaticAnalyzer {
622
654
  emitTargetMismatch(dispatchKind, targetSchema, md.inputs, "inputs");
623
655
  }
624
656
  }
625
- // Top-level `result:` (a sibling, only meaningful with `provide:`) is a
626
- // post-call mapping that must satisfy the abstract this definition
627
- // `extends` (`outputType`). The target's outputType lives on `provide.kind`
657
+ // Top-level `result:` is a post-call mapping that must satisfy the abstract
658
+ // this definition `extends` (`outputType`). It's a sibling of whichever
659
+ // dispatch entry-point declared a kind-typed target (`provide:` or
660
+ // `invoke:`). The target's outputType lives on the dispatcher's `kind`
628
661
  // and is what `result` is typed against *inside* CEL — separate role.
629
- if (provide &&
630
- typeof provide === "object" &&
631
- !Array.isArray(provide) &&
632
- md.result &&
633
- typeof md.result === "object") {
662
+ const hasDispatchObject = (provide && typeof provide === "object" && !Array.isArray(provide)) ||
663
+ (invoke && typeof invoke === "object" && !Array.isArray(invoke));
664
+ if (hasDispatchObject && md.result && typeof md.result === "object") {
634
665
  const extendsValue = md.extends;
635
666
  if (typeof extendsValue === "string" && extendsValue.length > 0) {
636
667
  const abstractSchema = lookupDefinitionTypeField(extendsValue, "outputType", defs, aliases, allManifests);
@@ -735,9 +766,13 @@ export class StaticAnalyzer {
735
766
  diagnostics.push(...validateReferences(allManifests, { aliases, definitions: defs, aliasesByModule }));
736
767
  // Validate `extends` fields and flag legacy `capability: <UserAbstract>` overload.
737
768
  diagnostics.push(...validateExtends(allManifests, defs, aliases));
769
+ // Validate provider coherence rules for `provide:` template-target definitions.
770
+ diagnostics.push(...validateProviderCoherence(allManifests, defs, aliases));
738
771
  // Validate throws: declarations and catches: coverage (rules 1, 2, 4, 7)
739
772
  diagnostics.push(...validateThrowsCoverage(allManifests, defs, aliases, this.celEnv));
740
- return diagnostics;
773
+ // Reroute diagnostics on synthetic (inline-extracted) resources back to
774
+ // the chain root so position-index lookups land on the parent doc.
775
+ return rewriteSyntheticOrigins(diagnostics, allManifests);
741
776
  }
742
777
  analyzeErrors(manifests, options, registry) {
743
778
  return this.analyze(manifests, options, registry).filter((d) => d.severity === DiagnosticSeverity.Error);
@@ -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,EAwQ/C,CAAC"}
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: { "x-telo-context-from-ref-kind": "provide/kind#outputType" },
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";
@@ -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;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
  }
@@ -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
  }
@@ -22,6 +22,10 @@ export declare function documentLineOffsets(text: string): number[];
22
22
  * yaml-AST node range into Range coordinates. */
23
23
  export declare function buildLineOffsets(text: string): number[];
24
24
  /** Walks the YAML AST and records source ranges for every field value, keyed
25
- * by dotted path (e.g. "kind", "config.handler", "config.routes[0].path"). */
25
+ * by dotted path (e.g. "kind", "config.handler", "config.routes[0].path").
26
+ * Map keys are also recorded under the `@key:<path>` namespace so diagnostic
27
+ * resolvers can squiggle just the key identifier instead of the full value
28
+ * block — used when a diagnostic targets a missing child property and the
29
+ * resolver has to fall back to the parent. */
26
30
  export declare function buildPositionIndex(doc: Document, lineOffsets: number[]): PositionIndex;
27
31
  //# sourceMappingURL=position-metadata.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"position-metadata.d.ts","sourceRoot":"","sources":["../src/position-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,MAAM,CAAC;AACrE,OAAO,KAAK,EAAY,aAAa,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;;oBAKoB;AAEpB,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,kEAAkE;AAClE,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,QAAQ,EAAE,GACrB,gBAAgB,EAAE,CAOpB;AAED;;8CAE8C;AAC9C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ1D;AAED;;kDAEkD;AAClD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAMvD;AAaD;+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;;8CAE8C;AAC9C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ1D;AAED;;kDAEkD;AAClD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAMvD;AAaD;;;;;+CAK+C;AAC/C,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,aAAa,CA0CtF"}
@@ -45,7 +45,11 @@ function offsetToPosition(offset, lineOffsets) {
45
45
  return { line: lo, character: offset - lineOffsets[lo] };
46
46
  }
47
47
  /** Walks the YAML AST and records source ranges for every field value, keyed
48
- * by dotted path (e.g. "kind", "config.handler", "config.routes[0].path"). */
48
+ * by dotted path (e.g. "kind", "config.handler", "config.routes[0].path").
49
+ * Map keys are also recorded under the `@key:<path>` namespace so diagnostic
50
+ * resolvers can squiggle just the key identifier instead of the full value
51
+ * block — used when a diagnostic targets a missing child property and the
52
+ * resolver has to fall back to the parent. */
49
53
  export function buildPositionIndex(doc, lineOffsets) {
50
54
  const index = new Map();
51
55
  function recordNode(node, path) {
@@ -66,6 +70,9 @@ export function buildPositionIndex(doc, lineOffsets) {
66
70
  if (key == null)
67
71
  continue;
68
72
  const childPath = path ? `${path}.${key}` : key;
73
+ if (pair.key && pair.key.range) {
74
+ recordNode(pair.key, `@key:${childPath}`);
75
+ }
69
76
  if (pair.value != null) {
70
77
  recordNode(pair.value, childPath);
71
78
  walk(pair.value, childPath);
@@ -43,12 +43,29 @@ export declare const REFERENCE_KEYS: Set<string>;
43
43
  * A named reference (has string `name`) may carry extra keys (e.g. `inputs`)
44
44
  * that are runtime call parameters — those are never inline resources. */
45
45
  export declare function isInlineResource(val: Record<string, unknown>): boolean;
46
- /** 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"}
@@ -26,25 +26,30 @@ export function isInlineResource(val) {
26
26
  return false;
27
27
  return true;
28
28
  }
29
- /** Resolves all values at a field map path in a resource config.
30
- * Path-segment markers:
31
- * - `[]` iterate array values at this key
29
+ /** Resolves all `{value, path}` entries at a field map path in a resource
30
+ * config. The returned `path` is the concrete dotted path produced by the
31
+ * substitutions below, matching the format `buildPositionIndex` keys on.
32
+ * Path-segment markers accepted in the input `path`:
33
+ * - `[]` iterate array values at this key, substituting `[N]` per item
34
+ * (e.g. `routes[]` → `routes[0]`, `routes[1]`, …).
32
35
  * - `{}` iterate map values (every value in an `additionalProperties`-typed
33
36
  * object — used for fields like `content[mime]` whose schema declares
34
- * a key-as-MIME map). The path is `<key>.{}.<rest>`. */
35
- export function resolveFieldValues(obj, path) {
37
+ * a key-as-MIME map). Substituted with the literal map key joined by
38
+ * a dot, so the input `content.{}.encoder` yields concrete paths
39
+ * like `content.application/json.encoder`. */
40
+ export function resolveFieldEntries(obj, path) {
36
41
  const parts = path.split(".");
37
- let current = [obj];
42
+ let current = [{ value: obj, path: "" }];
38
43
  for (const part of parts) {
39
44
  if (part === "{}") {
40
- // Iterate the values of every map currently in `current`.
41
45
  const next = [];
42
- for (const item of current) {
43
- if (!item || typeof item !== "object")
46
+ for (const entry of current) {
47
+ if (!entry.value || typeof entry.value !== "object")
44
48
  continue;
45
- for (const v of Object.values(item)) {
46
- if (v != null)
47
- next.push(v);
49
+ for (const [k, v] of Object.entries(entry.value)) {
50
+ if (v != null) {
51
+ next.push({ value: v, path: entry.path ? `${entry.path}.${k}` : k });
52
+ }
48
53
  }
49
54
  }
50
55
  current = next;
@@ -53,21 +58,32 @@ export function resolveFieldValues(obj, path) {
53
58
  const isArray = part.endsWith("[]");
54
59
  const key = isArray ? part.slice(0, -2) : part;
55
60
  const next = [];
56
- for (const item of current) {
57
- if (!item || typeof item !== "object")
61
+ for (const entry of current) {
62
+ if (!entry.value || typeof entry.value !== "object")
58
63
  continue;
59
- const val = item[key];
64
+ const val = entry.value[key];
60
65
  if (val == null)
61
66
  continue;
62
- if (isArray && Array.isArray(val))
63
- next.push(...val);
64
- else if (!isArray)
65
- next.push(val);
67
+ const basePath = entry.path ? `${entry.path}.${key}` : key;
68
+ if (isArray && Array.isArray(val)) {
69
+ for (let i = 0; i < val.length; i++) {
70
+ if (val[i] != null)
71
+ next.push({ value: val[i], path: `${basePath}[${i}]` });
72
+ }
73
+ }
74
+ else if (!isArray) {
75
+ next.push({ value: val, path: basePath });
76
+ }
66
77
  }
67
78
  current = next;
68
79
  }
69
80
  return current;
70
81
  }
82
+ /** Backwards-compat wrapper that drops the concrete path. Prefer
83
+ * `resolveFieldEntries` for new code that wants positions. */
84
+ export function resolveFieldValues(obj, path) {
85
+ return resolveFieldEntries(obj, path).map((e) => e.value);
86
+ }
71
87
  /**
72
88
  * Traverses a definition's JSON Schema once and returns a field map recording every
73
89
  * x-telo-ref slot and every x-telo-scope slot.