@telorun/analyzer 0.64.0 → 0.66.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 (114) hide show
  1. package/dist/analyzer.d.ts +1 -1
  2. package/dist/analyzer.d.ts.map +1 -1
  3. package/dist/analyzer.js +175 -65
  4. package/dist/call-graph.d.ts.map +1 -1
  5. package/dist/call-graph.js +7 -1
  6. package/dist/cel-environment.d.ts +12 -0
  7. package/dist/cel-environment.d.ts.map +1 -1
  8. package/dist/cel-environment.js +35 -19
  9. package/dist/cel-scope.d.ts.map +1 -1
  10. package/dist/cel-scope.js +24 -4
  11. package/dist/definition-registry.d.ts +47 -6
  12. package/dist/definition-registry.d.ts.map +1 -1
  13. package/dist/definition-registry.js +82 -13
  14. package/dist/eval-paths.d.ts +68 -0
  15. package/dist/eval-paths.d.ts.map +1 -1
  16. package/dist/eval-paths.js +138 -0
  17. package/dist/flatten-for-analyzer.d.ts +1 -1
  18. package/dist/flatten-for-analyzer.js +1 -1
  19. package/dist/import-resolution-diagnostics.d.ts +1 -1
  20. package/dist/import-resolution-diagnostics.js +1 -1
  21. package/dist/index.d.ts +13 -9
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +8 -6
  24. package/dist/manifest-visitor.d.ts.map +1 -1
  25. package/dist/manifest-visitor.js +8 -1
  26. package/dist/migrations/entries/index.d.ts.map +1 -1
  27. package/dist/migrations/entries/index.js +2 -0
  28. package/dist/migrations/entries/schema-prepare-bucket.json +27 -0
  29. package/dist/peer-binding.d.ts +232 -0
  30. package/dist/peer-binding.d.ts.map +1 -0
  31. package/dist/peer-binding.js +418 -0
  32. package/dist/ref-slot.d.ts +13 -0
  33. package/dist/ref-slot.d.ts.map +1 -1
  34. package/dist/ref-slot.js +26 -0
  35. package/dist/reference-field-map.d.ts +28 -0
  36. package/dist/reference-field-map.d.ts.map +1 -1
  37. package/dist/reference-field-map.js +25 -0
  38. package/dist/referrer-rule.d.ts +17 -0
  39. package/dist/referrer-rule.d.ts.map +1 -1
  40. package/dist/referrer-rule.js +4 -1
  41. package/dist/release/index.d.ts +1 -1
  42. package/dist/release/index.js +1 -1
  43. package/dist/resource-rule.d.ts +40 -27
  44. package/dist/resource-rule.d.ts.map +1 -1
  45. package/dist/resource-rule.js +61 -7
  46. package/dist/rule-condition.d.ts +11 -0
  47. package/dist/rule-condition.d.ts.map +1 -1
  48. package/dist/rule-condition.js +13 -0
  49. package/dist/schema-compat.d.ts +59 -22
  50. package/dist/schema-compat.d.ts.map +1 -1
  51. package/dist/schema-compat.js +60 -75
  52. package/dist/schema-error-report.d.ts +68 -0
  53. package/dist/schema-error-report.d.ts.map +1 -0
  54. package/dist/schema-error-report.js +356 -0
  55. package/dist/schema-projection.d.ts +106 -4
  56. package/dist/schema-projection.d.ts.map +1 -1
  57. package/dist/schema-projection.js +206 -18
  58. package/dist/telo-version.d.ts +1 -1
  59. package/dist/telo-version.js +1 -1
  60. package/dist/validate-cel-context.d.ts +1 -17
  61. package/dist/validate-cel-context.d.ts.map +1 -1
  62. package/dist/validate-cel-context.js +5 -70
  63. package/dist/validate-nested-inline.d.ts +22 -1
  64. package/dist/validate-nested-inline.d.ts.map +1 -1
  65. package/dist/validate-nested-inline.js +17 -9
  66. package/dist/validate-reference-forms.d.ts +10 -0
  67. package/dist/validate-reference-forms.d.ts.map +1 -1
  68. package/dist/validate-reference-forms.js +23 -0
  69. package/dist/validate-references.d.ts.map +1 -1
  70. package/dist/validate-references.js +9 -2
  71. package/dist/validate-referrer-rules.d.ts +38 -4
  72. package/dist/validate-referrer-rules.d.ts.map +1 -1
  73. package/dist/validate-referrer-rules.js +139 -31
  74. package/dist/validate-resource-rules.d.ts +11 -2
  75. package/dist/validate-resource-rules.d.ts.map +1 -1
  76. package/dist/validate-resource-rules.js +18 -12
  77. package/dist/validate-schema-projection.d.ts.map +1 -1
  78. package/dist/validate-schema-projection.js +33 -2
  79. package/dist/validate-step-inputs.js +11 -3
  80. package/dist/with-synthetic-positions.d.ts +1 -1
  81. package/dist/with-synthetic-positions.js +1 -1
  82. package/package.json +3 -3
  83. package/src/analyzer.ts +211 -68
  84. package/src/call-graph.ts +7 -1
  85. package/src/cel-environment.ts +48 -19
  86. package/src/cel-scope.ts +24 -6
  87. package/src/definition-registry.ts +90 -11
  88. package/src/eval-paths.ts +152 -0
  89. package/src/flatten-for-analyzer.ts +1 -1
  90. package/src/import-resolution-diagnostics.ts +1 -1
  91. package/src/index.ts +55 -6
  92. package/src/manifest-visitor.ts +8 -1
  93. package/src/migrations/entries/index.ts +2 -0
  94. package/src/migrations/entries/schema-prepare-bucket.json +27 -0
  95. package/src/peer-binding.ts +641 -0
  96. package/src/ref-slot.ts +36 -0
  97. package/src/reference-field-map.ts +42 -0
  98. package/src/referrer-rule.ts +20 -1
  99. package/src/release/index.ts +1 -1
  100. package/src/resource-rule.ts +69 -7
  101. package/src/rule-condition.ts +15 -0
  102. package/src/schema-compat.ts +92 -79
  103. package/src/schema-error-report.ts +417 -0
  104. package/src/schema-projection.ts +283 -20
  105. package/src/telo-version.ts +1 -1
  106. package/src/validate-cel-context.ts +6 -71
  107. package/src/validate-nested-inline.ts +35 -14
  108. package/src/validate-reference-forms.ts +22 -0
  109. package/src/validate-references.ts +15 -2
  110. package/src/validate-referrer-rules.ts +215 -28
  111. package/src/validate-resource-rules.ts +26 -12
  112. package/src/validate-schema-projection.ts +50 -1
  113. package/src/validate-step-inputs.ts +10 -4
  114. package/src/with-synthetic-positions.ts +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-error-report.d.ts","sourceRoot":"","sources":["../src/schema-error-report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH;gDACgD;AAChD,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,mFAAmF;AACnF,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;CACd;AAWD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAe3D;AAkGD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,YAAY,EAAE,CAsF5F;AAqHD;;sFAEsF;AACtF,wBAAgB,cAAc,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAaxD;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,EAAE,CAKrF;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAIjF"}
@@ -0,0 +1,356 @@
1
+ /**
2
+ * The one renderer for AJV validation failures.
3
+ *
4
+ * Browser-safe and re-imported by the kernel — the split `buildEvalPaths` and
5
+ * the redaction path parser already use — so a failure is phrased identically
6
+ * under `telo check` and at runtime. Three implementations used to answer this
7
+ * (the analyzer's keyword prose, the kernel's raw `instancePath + message`
8
+ * join, and observed state's own inline variant), so a developer who fixed what
9
+ * the analyzer told them met a different sentence describing the same thing.
10
+ *
11
+ * UNION REDUCTION is the second half. A union must attempt every branch, and
12
+ * AJV cannot know which one was intended — `discriminator: true` works only
13
+ * against an explicit OpenAPI-style discriminator property, which would mean
14
+ * changing what every module's authors write. So branch selection is a
15
+ * reporting concern and lives here.
16
+ *
17
+ * It narrows the error SET, never just the sentence: every consumer maps the
18
+ * surviving errors to manifest paths to anchor a diagnostic, so reducing at the
19
+ * prose layer alone would move the soup out of the message and into the
20
+ * problems list, one entry per branch on a different line.
21
+ *
22
+ * Selection is made from the ERRORS ALONE, never from the schema. A branch
23
+ * whose discriminating key is present emits no complaint at the union's own
24
+ * instancePath; one whose key is absent says `required`, and one that forbids a
25
+ * key the value carries says `additionalProperties`. That is the whole signal,
26
+ * and reading it off the errors is what lets reduction work across a `$ref`
27
+ * into another registered schema, where navigating to the branch subschema
28
+ * would mean re-implementing AJV's resolution.
29
+ */
30
+ const UNION_KEYWORDS = new Set(["anyOf", "oneOf"]);
31
+ /** Keywords a branch raises at the union's OWN instancePath when the value is
32
+ * not of that branch's shape at all — as opposed to being that shape and wrong
33
+ * further in. These are what make a branch implausible. */
34
+ const SHAPE_KEYWORDS = new Set(["required", "type", "additionalProperties", "enum", "const"]);
35
+ /* ------------------------------------------------------------------ prose */
36
+ export function formatSingleError(err) {
37
+ const p = err.instancePath || "/";
38
+ const params = err.params ?? {};
39
+ switch (err.keyword) {
40
+ case "additionalProperties":
41
+ return `${p} must NOT have additional properties ('${params.additionalProperty}' is not allowed)`;
42
+ case "required":
43
+ return `${p} is missing required property '${params.missingProperty}'`;
44
+ case "enum":
45
+ return `${p} ${err.message ?? "is invalid"} (${params.allowedValues?.join(" | ")})`;
46
+ case "type":
47
+ return `${p} must be ${params.type}${describeActual(err)}`;
48
+ default:
49
+ return `${p} ${err.message ?? "is invalid"}`;
50
+ }
51
+ }
52
+ /** ` (got string)`, or nothing when the value is not in hand. AJV carries
53
+ * `data` only under `verbose`, and a reducer that navigated the root value
54
+ * would have to be given it at every call site; an absent actual type is worth
55
+ * less than a wrong one. */
56
+ function describeActual(err) {
57
+ if (!("data" in err))
58
+ return "";
59
+ const d = err.data;
60
+ if (d === null)
61
+ return " (got null)";
62
+ if (Array.isArray(d))
63
+ return " (got array)";
64
+ return ` (got ${typeof d})`;
65
+ }
66
+ /* -------------------------------------------------------------- reduction */
67
+ /** The branch index a `schemaPath` sits under, for a union whose own schemaPath
68
+ * is `unionPath` (`…/anyOf`): a child is `…/anyOf/<i>/…` and nothing else can
69
+ * collide with it. */
70
+ function branchIndexUnder(unionPath, schemaPath) {
71
+ if (!schemaPath || !schemaPath.startsWith(unionPath + "/"))
72
+ return undefined;
73
+ const rest = schemaPath.slice(unionPath.length + 1);
74
+ const slash = rest.indexOf("/");
75
+ const head = slash === -1 ? rest : rest.slice(0, slash);
76
+ const index = Number(head);
77
+ return Number.isInteger(index) ? index : undefined;
78
+ }
79
+ function instanceDepth(path) {
80
+ if (!path)
81
+ return 0;
82
+ return path.split("/").filter((s) => s !== "").length;
83
+ }
84
+ /**
85
+ * How the alternatives at a union node are described, one phrase each.
86
+ *
87
+ * Read off the complaints made at the union's own node, and deliberately ONE
88
+ * PHRASE PER MISSING KEY rather than one per error group. AJV inlines most
89
+ * `$ref` branches and reports them all under the same bare `schemaPath`, so
90
+ * several branches are genuinely indistinguishable in the error set — joining
91
+ * their keys into a single phrase would read as one alternative demanding all
92
+ * of them, which is a claim about the schema that is simply false. Listing them
93
+ * separately under-specifies a branch that requires two keys at once, and each
94
+ * clause is still a true necessary condition; asserting a conjunction that does
95
+ * not exist is not.
96
+ */
97
+ function describeAlternatives(errors, unionInstancePath) {
98
+ const own = errors.filter((e) => (e.instancePath || "") === unionInstancePath);
99
+ const phrases = [];
100
+ for (const e of own) {
101
+ if (e.keyword === "required")
102
+ phrases.push(`one with '${e.params?.missingProperty}'`);
103
+ else if (e.keyword === "type")
104
+ phrases.push(`a ${e.params?.type}`);
105
+ else if (e.keyword === "enum") {
106
+ phrases.push(`one of ${e.params?.allowedValues?.join(" | ")}`);
107
+ }
108
+ }
109
+ return phrases.length > 0 ? phrases : ["another shape"];
110
+ }
111
+ /** Is this branch a plausible reading of the value — does it accept the value's
112
+ * shape at the union node itself, and only disagree further in? */
113
+ function isPlausible(errors, unionInstancePath) {
114
+ return !errors.some((e) => (e.instancePath || "") === unionInstancePath && SHAPE_KEYWORDS.has(e.keyword ?? ""));
115
+ }
116
+ function isUnder(child, parent) {
117
+ return parent === "" ? child !== "" : child.startsWith(parent + "/");
118
+ }
119
+ /** The value path one level up, or undefined at the root. `""` is the root, so
120
+ * a non-empty path with no separator has the root as its parent. */
121
+ function parentPath(path) {
122
+ if (path === "")
123
+ return undefined;
124
+ const cut = path.lastIndexOf("/");
125
+ return cut <= 0 ? "" : path.slice(0, cut);
126
+ }
127
+ /**
128
+ * Replace each failing union with the errors of the branch the author plainly
129
+ * meant, recursively, outside in.
130
+ *
131
+ * Attribution runs to the DEEPEST occurrence that could own an error, which is
132
+ * what keeps a container's own complaint apart from its child's when both carry
133
+ * the same `schemaPath`. An occurrence reached through a branch becomes a
134
+ * candidate branch of its own — it raised nothing at the parent's node, so it is
135
+ * plausible exactly when the value really did take that shape and fail further
136
+ * in, and reducing it recursively is what stops an inner union's alternatives
137
+ * from surviving inside the outer one's selection.
138
+ */
139
+ export function reduceSchemaErrors(errors) {
140
+ if (!errors || errors.length === 0)
141
+ return [];
142
+ const occurrences = errors
143
+ .filter((e) => UNION_KEYWORDS.has(e.keyword ?? "") && typeof e.schemaPath === "string")
144
+ .map((e) => ({
145
+ error: e,
146
+ schemaPath: e.schemaPath,
147
+ instancePath: e.instancePath || "",
148
+ owned: [],
149
+ children: [],
150
+ }));
151
+ if (occurrences.length === 0)
152
+ return errors;
153
+ // The VALUE NODE is the claim, not the schemaPath. A branch written as a
154
+ // `$ref` is reported by AJV under the TARGET's schemaPath — and AJV inlines
155
+ // some of them, reporting several branches under one identical path — so
156
+ // nothing in such an error points back at the union that dispatched to it. A
157
+ // large union is written exactly that way, a branch per `$defs` entry, so
158
+ // claiming by schemaPath alone would leave the biggest unions unreduced.
159
+ //
160
+ // Indexed by instancePath rather than scanned: an error is claimed by the
161
+ // DEEPEST occurrence enclosing it, which is found by walking that error's own
162
+ // path upwards — bounded by the path's depth instead of by the number of
163
+ // unions. The scan this replaced was O(errors × occurrences), and both grow
164
+ // with nesting depth on a recursive shape, on a path the editor runs per
165
+ // keystroke.
166
+ const byPath = new Map();
167
+ const isOccurrence = new Set();
168
+ for (const o of occurrences) {
169
+ isOccurrence.add(o.error);
170
+ // Several unions can occur at ONE value node (a union inside a union
171
+ // branch); the first is kept, and the rest nest under it below.
172
+ if (!byPath.has(o.instancePath))
173
+ byPath.set(o.instancePath, o);
174
+ }
175
+ /** The nearest occurrence at or above `path`, excluding `path` itself when
176
+ * `strict` — which is how an occurrence finds its parent rather than itself. */
177
+ const enclosing = (path, strict) => {
178
+ let current = strict ? parentPath(path) : path;
179
+ while (current !== undefined) {
180
+ const hit = byPath.get(current);
181
+ if (hit)
182
+ return hit;
183
+ current = parentPath(current);
184
+ }
185
+ return undefined;
186
+ };
187
+ const owner = new Map();
188
+ for (const err of errors) {
189
+ if (isOccurrence.has(err))
190
+ continue;
191
+ const best = enclosing(err.instancePath || "", false);
192
+ if (best) {
193
+ best.owned.push(err);
194
+ owner.set(err, best);
195
+ }
196
+ }
197
+ // Nest occurrences the same way: an occurrence deeper in the value was reached
198
+ // through some branch of the nearest one enclosing it.
199
+ const roots = [];
200
+ for (const o of occurrences) {
201
+ const parent = o === byPath.get(o.instancePath)
202
+ ? enclosing(o.instancePath, true)
203
+ : byPath.get(o.instancePath);
204
+ if (parent && parent !== o)
205
+ parent.children.push(o);
206
+ else
207
+ roots.push(o);
208
+ }
209
+ const replaced = new Map();
210
+ for (const root of roots)
211
+ replaced.set(root.error, resolveOccurrence(root));
212
+ const out = [];
213
+ for (const err of errors) {
214
+ const replacement = replaced.get(err);
215
+ if (replacement) {
216
+ out.push(...replacement);
217
+ continue;
218
+ }
219
+ // Everything an occurrence owns is spoken for by whichever branch survived,
220
+ // and a nested occurrence is carried inside its parent's selection.
221
+ if (owner.has(err))
222
+ continue;
223
+ if (occurrences.some((o) => o.error === err))
224
+ continue;
225
+ out.push(err);
226
+ }
227
+ return out;
228
+ }
229
+ /** Groups one union's complaints into candidate readings of the value.
230
+ *
231
+ * The branch INDEX is used wherever the error carries it. It does not when the
232
+ * branch is a `$ref` — AJV reports under the target's schemaPath — so the
233
+ * fallback groups by the VALUE NODE each complaint is about: everything said
234
+ * about the union node itself is one candidate (those are the branches that
235
+ * rejected the value's shape outright), and each child node complained about is
236
+ * its own. That is the same question asked of the data instead of the schema,
237
+ * and it is what the ordering below actually reads. */
238
+ function groupCandidates(occurrence) {
239
+ const byIndex = new Map();
240
+ const byNode = new Map();
241
+ for (const err of occurrence.owned) {
242
+ const index = branchIndexUnder(occurrence.schemaPath, err.schemaPath);
243
+ const bucket = index === undefined
244
+ ? mapBucket(byNode, childSegment(err.instancePath || "", occurrence.instancePath))
245
+ : mapBucket(byIndex, index);
246
+ bucket.push(err);
247
+ }
248
+ return [...byIndex]
249
+ .map(([index, errs]) => ({ index, errors: errs, nested: false }))
250
+ .concat([...byNode].map(([, errs]) => ({ index: Number.MAX_SAFE_INTEGER, errors: errs, nested: false })))
251
+ .concat(occurrence.children.map((child) => ({
252
+ index: Number.POSITIVE_INFINITY,
253
+ errors: resolveOccurrence(child),
254
+ nested: true,
255
+ })));
256
+ }
257
+ function mapBucket(map, key) {
258
+ const existing = map.get(key);
259
+ if (existing)
260
+ return existing;
261
+ const created = [];
262
+ map.set(key, created);
263
+ return created;
264
+ }
265
+ /** The first value-path segment below `parent`, or "" for the node itself. */
266
+ function childSegment(instancePath, parent) {
267
+ if (!isUnder(instancePath, parent))
268
+ return "";
269
+ const rest = instancePath.slice(parent.length + 1);
270
+ const slash = rest.indexOf("/");
271
+ return slash === -1 ? rest : rest.slice(0, slash);
272
+ }
273
+ function resolveOccurrence(occurrence) {
274
+ const candidates = groupCandidates(occurrence);
275
+ // `oneOf` matching SEVERAL branches emits the union error with no branch
276
+ // errors at all — nothing was rejected, so there is no branch to select.
277
+ if (candidates.length === 0)
278
+ return [occurrence.error];
279
+ const plausible = candidates.filter((c) => c.nested || isPlausible(c.errors, occurrence.instancePath));
280
+ if (plausible.length === 0) {
281
+ return [alternativesError(candidates, occurrence)];
282
+ }
283
+ // Deepest agreement first — a branch that matched further into the value is
284
+ // the one the author was writing — then the fewest complaints, then the
285
+ // declaration order, so the choice is stable.
286
+ plausible.sort((a, b) => {
287
+ const depth = maxDepth(b.errors) - maxDepth(a.errors);
288
+ if (depth !== 0)
289
+ return depth;
290
+ if (a.errors.length !== b.errors.length)
291
+ return a.errors.length - b.errors.length;
292
+ return a.index - b.index;
293
+ });
294
+ const winner = plausible[0];
295
+ return winner.nested ? winner.errors : reduceSchemaErrors(winner.errors);
296
+ }
297
+ function maxDepth(errors) {
298
+ let max = 0;
299
+ for (const e of errors)
300
+ max = Math.max(max, instanceDepth(e.instancePath));
301
+ return max;
302
+ }
303
+ /** One error anchored at the union node, listing what could have gone there.
304
+ * The honest fallback: a confident wrong message is worse than the
305
+ * concatenation this replaces, so when no branch is a plausible reading the
306
+ * reader is told what the alternatives are rather than shown one branch's
307
+ * complaints as if it were the intended one. */
308
+ function alternativesError(candidates, occurrence) {
309
+ const seen = new Set();
310
+ const described = [];
311
+ for (const text of describeAlternatives(candidates.flatMap((c) => c.errors), occurrence.instancePath)) {
312
+ if (seen.has(text))
313
+ continue;
314
+ seen.add(text);
315
+ described.push(text);
316
+ }
317
+ return {
318
+ ...occurrence.error,
319
+ instancePath: occurrence.instancePath,
320
+ message: `matches no alternative — expected ${described.join(", or ")}`,
321
+ };
322
+ }
323
+ /* --------------------------------------------------------------- rendering */
324
+ /** Converts an AJV error to a dotted path compatible with PositionIndex keys.
325
+ * e.g. instancePath "/config/routes/0/handler" → "config.routes[0].handler"
326
+ * For "required" keyword errors, appends the missing property to the parent path. */
327
+ export function ajvErrorToPath(err) {
328
+ const instancePath = err.instancePath ?? "";
329
+ const parts = instancePath.split("/").filter((p) => p !== "");
330
+ let result = "";
331
+ for (const part of parts) {
332
+ if (/^\d+$/.test(part))
333
+ result += `[${part}]`;
334
+ else
335
+ result += result ? `.${part}` : part;
336
+ }
337
+ if (err.keyword === "required" && err.params?.missingProperty) {
338
+ const missing = err.params.missingProperty;
339
+ result += result ? `.${missing}` : missing;
340
+ }
341
+ return result;
342
+ }
343
+ /** Reduced, path-anchored issues — what a diagnostic list is built from. */
344
+ export function schemaIssues(errors) {
345
+ return reduceSchemaErrors(errors).map((err) => ({
346
+ message: formatSingleError(err),
347
+ path: ajvErrorToPath(err),
348
+ }));
349
+ }
350
+ /** Reduced, rendered as one sentence — what a thrown runtime error carries. */
351
+ export function formatAjvErrors(errors) {
352
+ const reduced = reduceSchemaErrors(errors);
353
+ if (reduced.length === 0)
354
+ return "Unknown schema error";
355
+ return reduced.map(formatSingleError).join("; ");
356
+ }
@@ -47,6 +47,32 @@ export interface SchemaProjection {
47
47
  readonly nullable?: string;
48
48
  /** Entry field that wraps the mapped node in an array. */
49
49
  readonly array?: string;
50
+ /**
51
+ * How an entry whose keyed field holds a REFERENCE projects.
52
+ *
53
+ * The map is keyed on the field's VALUE, and a reference is not a key, so a
54
+ * `type:` holding one falls through to this path. It is declared as data by the
55
+ * backend, which is what keeps the analyzer from learning that an enum exists:
56
+ * `from` names the field of the target declaration to read, `keyword` the
57
+ * schema keyword its values become, and `base` / `baseFrom` where the node's
58
+ * own type comes from — a literal for an engine whose named type IS its own
59
+ * base, a field of the target for one that declares a storage class.
60
+ *
61
+ * A backend that declares none projects exactly as it did before.
62
+ */
63
+ readonly reference?: ProjectionReference;
64
+ }
65
+ /** The reference path of a projection — see {@link SchemaProjection.reference}. */
66
+ export interface ProjectionReference {
67
+ /** Field of the TARGET declaration whose value the keyword takes. */
68
+ readonly from: string;
69
+ /** The JSON Schema keyword those values become (`enum`). */
70
+ readonly keyword: string;
71
+ /** The node the keyword is added to, written literally. */
72
+ readonly base?: Record<string, unknown>;
73
+ /** Field of the target declaration naming a value in the kind's own
74
+ * `x-telo-schema-map`, whose mapped node is the base. */
75
+ readonly baseFrom?: string;
50
76
  }
51
77
  export type SchemaMap = Readonly<Record<string, Record<string, unknown>>>;
52
78
  /** The projection a kind declares, or undefined. Invalid shapes read as absent;
@@ -59,6 +85,18 @@ export declare function rawSchemaProjection(definition: unknown): unknown;
59
85
  /** True when the annotation was found inside `schema:` rather than on the
60
86
  * document — the misplacement the strict half reports. */
61
87
  export declare function schemaProjectionIsMisplaced(definition: unknown): boolean;
88
+ /**
89
+ * The schema node that CARRIES the value vocabulary — the node itself, or the
90
+ * branch of a union that declares the map.
91
+ *
92
+ * A slot unioning a closed value vocabulary with a reference keeps its map on the
93
+ * value branch, exactly as the ref-slot reader peels the same union for its
94
+ * constraint. Exported because the strict half checks the map against the same
95
+ * branch's `enum`, and two implementations of "which branch is the value one"
96
+ * would eventually disagree — silently, since the failure of missing one is a
97
+ * completeness check that quietly stops running.
98
+ */
99
+ export declare function schemaMapBranch(node: unknown): Record<string, unknown> | undefined;
62
100
  export declare function readSchemaMap(node: unknown): SchemaMap | undefined;
63
101
  /** The consumer-side annotation: a JSON Pointer to this resource's ref slot
64
102
  * whose target declares the projection. */
@@ -83,7 +121,16 @@ export declare function projectionKeyMap(kindSchema: unknown, projection: Schema
83
121
  * enough to double as a validator would move the domain's semantics into the
84
122
  * type layer.
85
123
  */
86
- export declare function projectEntries(manifest: unknown, projection: SchemaProjection, map: SchemaMap): Record<string, unknown> | undefined;
124
+ export declare function projectEntries(manifest: unknown, projection: SchemaProjection, map: SchemaMap,
125
+ /** What a REFERENCE at the keyed field is resolved through, and where a
126
+ * failure to resolve one is reported. A caller with no scope cannot resolve
127
+ * one, so such an entry projects OPEN — present, untyped — rather than
128
+ * vanishing from the row. */
129
+ options?: {
130
+ readonly scope?: ProjectionScope;
131
+ readonly pointer?: string;
132
+ readonly failures?: ProjectionFailure[];
133
+ }): Record<string, unknown> | undefined;
87
134
  /** A reference as the analyzer sees it: the internal `{kind, name, alias?}`
88
135
  * shape `resolveRefSentinels` rewrites `!ref` to. */
89
136
  export interface ProjectionRef {
@@ -91,8 +138,17 @@ export interface ProjectionRef {
91
138
  readonly kind?: string;
92
139
  readonly alias?: string;
93
140
  }
94
- /** The `{kind, name, alias?}` reference a value holds, or undefined. Exported so
95
- * a host whose slot may hold EITHER shape can fall back to this reading. */
141
+ /**
142
+ * The `{kind, name, alias?}` reference a value holds, or undefined. Exported so
143
+ * a host whose slot may hold EITHER shape can fall back to this reading.
144
+ *
145
+ * The unresolved `!ref` SENTINEL is read too. `resolveRefSentinels` normally
146
+ * rewrites one before this pass, but not when the reference names nothing — and
147
+ * that is exactly when a projection failure is reported, so reading only the
148
+ * resolved shape made the diagnostic name the target `<unnamed>`, which is the
149
+ * one fact the author needed from it. A round-trip host (`compile` off) carries
150
+ * the sentinel for every reference, resolved or not.
151
+ */
96
152
  export declare function readProjectionRef(value: unknown): ProjectionRef | undefined;
97
153
  /** What a reference resolved to. `"ambiguous"` is distinct from `undefined`
98
154
  * because the two need different advice: one says disambiguate, the other says
@@ -139,6 +195,16 @@ export interface ProjectionScope {
139
195
  * analysis has.
140
196
  */
141
197
  export declare function manifestListScope(manifests: readonly Record<string, any>[], resolveDefinition: (kind: string) => Record<string, any> | undefined): ProjectionScope;
198
+ /**
199
+ * Why a slot could not be typed from a projection.
200
+ *
201
+ * Each reason is a DIFFERENT repair, which is why the three ways a target can
202
+ * carry no usable projection are kept apart rather than collapsed into
203
+ * `no-projection`: that one message ("declares no 'x-telo-schema-projection'")
204
+ * was printed for a kind that declares one whose key field carries no map, and
205
+ * for a declaration whose entry collection is simply absent — accusing the wrong
206
+ * author of the wrong omission in both.
207
+ */
142
208
  export type ProjectionFailure = {
143
209
  readonly reason: "no-ref";
144
210
  readonly pointer: string;
@@ -150,10 +216,36 @@ export type ProjectionFailure = {
150
216
  readonly reason: "ambiguous";
151
217
  readonly pointer: string;
152
218
  readonly name: string;
153
- } | {
219
+ }
220
+ /** The target's KIND declares no `x-telo-schema-projection` at all. */
221
+ | {
154
222
  readonly reason: "no-projection";
155
223
  readonly pointer: string;
156
224
  readonly kind: string;
225
+ }
226
+ /** It declares one, but the field it keys on carries no `x-telo-schema-map`. */
227
+ | {
228
+ readonly reason: "no-projection-map";
229
+ readonly pointer: string;
230
+ readonly kind: string;
231
+ }
232
+ /** Both are declared and the DECLARATION holds no entry collection to project
233
+ * — an absent `columns:`, or a value that is not a collection. */
234
+ | {
235
+ readonly reason: "no-entries";
236
+ readonly pointer: string;
237
+ readonly kind: string;
238
+ readonly entries: string;
239
+ }
240
+ /** An ENTRY of the projected declaration references a shape that could not be
241
+ * read. Reported rather than dropped: the entry would silently vanish from
242
+ * the projected row, so a consumer naming it would be told the property does
243
+ * not exist. */
244
+ | {
245
+ readonly reason: "entry-reference";
246
+ readonly pointer: string;
247
+ readonly entry: string;
248
+ readonly name: string;
157
249
  };
158
250
  export declare function describeProjectionFailure(failure: ProjectionFailure): string;
159
251
  /**
@@ -166,6 +258,16 @@ export declare function describeProjectionFailure(failure: ProjectionFailure): s
166
258
  * `failures`, because degrading SILENTLY is the failure this whole mechanism
167
259
  * exists to move earlier: the consumer's contract quietly reopens and a
168
260
  * misspelled field passes `telo check` exactly as it did before.
261
+ *
262
+ * **A node that projected NOTHING is returned by IDENTITY**, and that is a
263
+ * correctness property of the caller rather than a micro-optimization:
264
+ * `DefinitionRegistry` memoizes a compiled AJV validator per schema OBJECT,
265
+ * because every resource of a kind is checked against the same one at keystroke
266
+ * time. Rebuilding each node unconditionally — which this did — misses that memo
267
+ * for every resource in the analysis, so AJV recompiled the whole kind schema
268
+ * once per resource: on `apps/hub` that was 197 compiles instead of 54, and 723
269
+ * ms instead of 97. Returning the input where nothing changed restores it for
270
+ * every kind that declares no projection at all, which is nearly all of them.
169
271
  */
170
272
  export declare function resolveSchemaProjections(schema: unknown, manifest: Record<string, any> | undefined, scope: ProjectionScope, failures?: ProjectionFailure[]): unknown;
171
273
  //# sourceMappingURL=schema-projection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema-projection.d.ts","sourceRoot":"","sources":["../src/schema-projection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema-projection.d.ts","sourceRoot":"","sources":["../src/schema-projection.ts"],"names":[],"mappings":"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"}