@telorun/analyzer 0.65.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 (100) hide show
  1. package/dist/analyzer.d.ts +1 -1
  2. package/dist/analyzer.d.ts.map +1 -1
  3. package/dist/analyzer.js +157 -63
  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 +24 -0
  12. package/dist/definition-registry.d.ts.map +1 -1
  13. package/dist/definition-registry.js +41 -0
  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 +9 -7
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +6 -5
  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-projection.d.ts +106 -4
  50. package/dist/schema-projection.d.ts.map +1 -1
  51. package/dist/schema-projection.js +206 -18
  52. package/dist/telo-version.d.ts +1 -1
  53. package/dist/telo-version.js +1 -1
  54. package/dist/validate-cel-context.d.ts +1 -17
  55. package/dist/validate-cel-context.d.ts.map +1 -1
  56. package/dist/validate-cel-context.js +5 -70
  57. package/dist/validate-reference-forms.d.ts +10 -0
  58. package/dist/validate-reference-forms.d.ts.map +1 -1
  59. package/dist/validate-reference-forms.js +23 -0
  60. package/dist/validate-references.d.ts.map +1 -1
  61. package/dist/validate-references.js +9 -2
  62. package/dist/validate-referrer-rules.d.ts +38 -4
  63. package/dist/validate-referrer-rules.d.ts.map +1 -1
  64. package/dist/validate-referrer-rules.js +139 -31
  65. package/dist/validate-resource-rules.d.ts +11 -2
  66. package/dist/validate-resource-rules.d.ts.map +1 -1
  67. package/dist/validate-resource-rules.js +18 -12
  68. package/dist/validate-schema-projection.d.ts.map +1 -1
  69. package/dist/validate-schema-projection.js +33 -2
  70. package/dist/with-synthetic-positions.d.ts +1 -1
  71. package/dist/with-synthetic-positions.js +1 -1
  72. package/package.json +3 -3
  73. package/src/analyzer.ts +191 -68
  74. package/src/call-graph.ts +7 -1
  75. package/src/cel-environment.ts +48 -19
  76. package/src/cel-scope.ts +24 -6
  77. package/src/definition-registry.ts +42 -0
  78. package/src/eval-paths.ts +152 -0
  79. package/src/flatten-for-analyzer.ts +1 -1
  80. package/src/import-resolution-diagnostics.ts +1 -1
  81. package/src/index.ts +45 -4
  82. package/src/manifest-visitor.ts +8 -1
  83. package/src/migrations/entries/index.ts +2 -0
  84. package/src/migrations/entries/schema-prepare-bucket.json +27 -0
  85. package/src/peer-binding.ts +641 -0
  86. package/src/ref-slot.ts +36 -0
  87. package/src/reference-field-map.ts +42 -0
  88. package/src/referrer-rule.ts +20 -1
  89. package/src/release/index.ts +1 -1
  90. package/src/resource-rule.ts +69 -7
  91. package/src/rule-condition.ts +15 -0
  92. package/src/schema-projection.ts +283 -20
  93. package/src/telo-version.ts +1 -1
  94. package/src/validate-cel-context.ts +6 -71
  95. package/src/validate-reference-forms.ts +22 -0
  96. package/src/validate-references.ts +15 -2
  97. package/src/validate-referrer-rules.ts +215 -28
  98. package/src/validate-resource-rules.ts +26 -12
  99. package/src/validate-schema-projection.ts +50 -1
  100. package/src/with-synthetic-positions.ts +1 -1
@@ -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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"}
@@ -34,6 +34,7 @@
34
34
  * row shape lives in one instance's own `columns:`, which no definition-level
35
35
  * derivation can reach.
36
36
  */
37
+ import { isRefSentinel } from "@telorun/templating";
37
38
  function isObject(value) {
38
39
  return !!value && typeof value === "object" && !Array.isArray(value);
39
40
  }
@@ -55,6 +56,20 @@ export function readSchemaProjection(definition) {
55
56
  nameField: typeof raw.name === "string" ? raw.name : undefined,
56
57
  nullable: typeof raw.nullable === "string" ? raw.nullable : undefined,
57
58
  array: typeof raw.array === "string" ? raw.array : undefined,
59
+ reference: readProjectionReference(raw.reference),
60
+ };
61
+ }
62
+ function readProjectionReference(raw) {
63
+ if (!isObject(raw))
64
+ return undefined;
65
+ const { from, keyword, base, baseFrom } = raw;
66
+ if (typeof from !== "string" || typeof keyword !== "string")
67
+ return undefined;
68
+ return {
69
+ from,
70
+ keyword,
71
+ base: isObject(base) ? base : undefined,
72
+ baseFrom: typeof baseFrom === "string" ? baseFrom : undefined,
58
73
  };
59
74
  }
60
75
  /** The annotation as written, from either position — the document (canonical)
@@ -79,7 +94,37 @@ export function schemaProjectionIsMisplaced(definition) {
79
94
  const schema = definition.schema;
80
95
  return isObject(schema) && schema["x-telo-schema-projection"] !== undefined;
81
96
  }
97
+ /**
98
+ * The schema node that CARRIES the value vocabulary — the node itself, or the
99
+ * branch of a union that declares the map.
100
+ *
101
+ * A slot unioning a closed value vocabulary with a reference keeps its map on the
102
+ * value branch, exactly as the ref-slot reader peels the same union for its
103
+ * constraint. Exported because the strict half checks the map against the same
104
+ * branch's `enum`, and two implementations of "which branch is the value one"
105
+ * would eventually disagree — silently, since the failure of missing one is a
106
+ * completeness check that quietly stops running.
107
+ */
108
+ export function schemaMapBranch(node) {
109
+ if (!isObject(node))
110
+ return undefined;
111
+ if (node["x-telo-schema-map"] !== undefined)
112
+ return node;
113
+ for (const key of ["oneOf", "anyOf"]) {
114
+ const branches = node[key];
115
+ if (!Array.isArray(branches))
116
+ continue;
117
+ for (const branch of branches) {
118
+ if (isObject(branch) && branch["x-telo-schema-map"] !== undefined)
119
+ return branch;
120
+ }
121
+ }
122
+ return undefined;
123
+ }
82
124
  export function readSchemaMap(node) {
125
+ return ownSchemaMap(schemaMapBranch(node));
126
+ }
127
+ function ownSchemaMap(node) {
83
128
  if (!isObject(node))
84
129
  return undefined;
85
130
  const raw = node["x-telo-schema-map"];
@@ -134,6 +179,66 @@ export function projectionKeyMap(kindSchema, projection) {
134
179
  return undefined;
135
180
  return readSchemaMap(entry.properties[projection.key]);
136
181
  }
182
+ /**
183
+ * The node an entry whose keyed field holds a REFERENCE projects to.
184
+ *
185
+ * This is the one place a projection crosses to another declaration, and it is
186
+ * a deliberate exception to the projection's lossiness: length, precision and
187
+ * collation stop at the boundary because the database enforces them, while a
188
+ * domain crosses because it IS the type at the granularity a consumer acts on —
189
+ * the enum in a CRUD model's OpenAPI operation, a completion list in the editor,
190
+ * a filter a repository can reject before the query.
191
+ *
192
+ * **A reference that cannot be read projects OPEN, never to nothing**, and that
193
+ * is the opposite of the rule an unmapped VALUE follows. The two failures are
194
+ * not the same failure: an unmapped value is a gap in the kind's own vocabulary,
195
+ * so there is no entry to speak of, while an unreadable reference names an entry
196
+ * the declaration plainly HAS and only leaves its type unknown. Dropping it made
197
+ * the projection deny the entry exists — a table whose enum reference had a typo
198
+ * reported `'status' is not allowed` against a column declared three lines up,
199
+ * blaming the seed row for the reference's mistake. Open is the honest
200
+ * under-approximation, and the reason is reported alongside.
201
+ */
202
+ function referencedNode(value, entryName, projection, map, options) {
203
+ const reference = projection.reference;
204
+ if (!reference || !isObject(value))
205
+ return undefined;
206
+ // Through the single reader, so the name in the diagnostic is the one the
207
+ // author wrote whichever shape the slot holds — reading `value.name` here
208
+ // reported `<unnamed>` for an unresolved `!ref`, which is precisely the case
209
+ // that produces the diagnostic.
210
+ const name = readProjectionRef(value)?.name ?? "<unnamed>";
211
+ const report = () => {
212
+ options?.failures?.push({
213
+ reason: "entry-reference",
214
+ // The EMPTY pointer means the projected declaration is the one carrying
215
+ // the diagnostic, so the entry's own path is a real anchor in that file —
216
+ // the column, not the document root. Any other pointer names a slot
217
+ // holding a reference to a DIFFERENT manifest, whose entry paths mean
218
+ // nothing here, so the slot stays the anchor.
219
+ pointer: options?.pointer === ""
220
+ ? `${projection.entries}/${entryName}`
221
+ : (options?.pointer ?? projection.entries),
222
+ entry: entryName,
223
+ name,
224
+ });
225
+ return {};
226
+ };
227
+ const found = options?.scope?.resolveManifest(value);
228
+ if (!found || "ambiguous" in found)
229
+ return report();
230
+ const values = found.manifest[reference.from];
231
+ if (!Array.isArray(values) || values.length === 0)
232
+ return report();
233
+ let base = reference.base;
234
+ if (reference.baseFrom !== undefined) {
235
+ const declared = found.manifest[reference.baseFrom];
236
+ base = typeof declared === "string" ? map[declared] : undefined;
237
+ }
238
+ if (!base)
239
+ return report();
240
+ return { ...base, [reference.keyword]: values };
241
+ }
137
242
  /**
138
243
  * Project one declaration to an object schema.
139
244
  *
@@ -148,7 +253,12 @@ export function projectionKeyMap(kindSchema, projection) {
148
253
  * enough to double as a validator would move the domain's semantics into the
149
254
  * type layer.
150
255
  */
151
- export function projectEntries(manifest, projection, map) {
256
+ export function projectEntries(manifest, projection, map,
257
+ /** What a REFERENCE at the keyed field is resolved through, and where a
258
+ * failure to resolve one is reported. A caller with no scope cannot resolve
259
+ * one, so such an entry projects OPEN — present, untyped — rather than
260
+ * vanishing from the row. */
261
+ options) {
152
262
  const entries = navigate(manifest, projection.entries);
153
263
  if (entries === undefined)
154
264
  return undefined;
@@ -157,7 +267,9 @@ export function projectEntries(manifest, projection, map) {
157
267
  if (!isObject(entry) || name === undefined)
158
268
  return;
159
269
  const key = entry[projection.key];
160
- const mapped = typeof key === "string" ? map[key] : undefined;
270
+ const mapped = typeof key === "string"
271
+ ? map[key]
272
+ : referencedNode(key, name, projection, map, options);
161
273
  // A value with no map entry projects to nothing rather than to `any`: the
162
274
  // vocabulary is the kind's own enum, so an unmapped value is a gap in the
163
275
  // kind's declaration, not a shape to guess at.
@@ -193,11 +305,26 @@ export function projectEntries(manifest, projection, map) {
193
305
  additionalProperties: false,
194
306
  };
195
307
  }
196
- /** The `{kind, name, alias?}` reference a value holds, or undefined. Exported so
197
- * a host whose slot may hold EITHER shape can fall back to this reading. */
308
+ /**
309
+ * The `{kind, name, alias?}` reference a value holds, or undefined. Exported so
310
+ * a host whose slot may hold EITHER shape can fall back to this reading.
311
+ *
312
+ * The unresolved `!ref` SENTINEL is read too. `resolveRefSentinels` normally
313
+ * rewrites one before this pass, but not when the reference names nothing — and
314
+ * that is exactly when a projection failure is reported, so reading only the
315
+ * resolved shape made the diagnostic name the target `<unnamed>`, which is the
316
+ * one fact the author needed from it. A round-trip host (`compile` off) carries
317
+ * the sentinel for every reference, resolved or not.
318
+ */
198
319
  export function readProjectionRef(value) {
199
320
  if (!isObject(value))
200
321
  return undefined;
322
+ if (isRefSentinel(value)) {
323
+ const dot = value.source.indexOf(".");
324
+ return dot > 0
325
+ ? { name: value.source.slice(dot + 1), alias: value.source.slice(0, dot) }
326
+ : { name: value.source };
327
+ }
201
328
  const name = value.name;
202
329
  if (typeof name !== "string")
203
330
  return undefined;
@@ -258,10 +385,22 @@ function refTarget(value, scope, pointer) {
258
385
  return { reason: "no-projection", pointer, kind: manifest.kind };
259
386
  return { manifest, definition };
260
387
  }
388
+ /** The declaration the annotation is written on, as a projection target. */
389
+ function ownTarget(manifest, scope) {
390
+ if (typeof manifest.kind !== "string") {
391
+ return { reason: "no-ref", pointer: "" };
392
+ }
393
+ const definition = scope.resolveDefinition(manifest.kind);
394
+ if (!definition)
395
+ return { reason: "no-projection", pointer: "", kind: manifest.kind };
396
+ return { manifest, definition };
397
+ }
261
398
  export function describeProjectionFailure(failure) {
262
399
  switch (failure.reason) {
263
400
  case "no-ref":
264
- return `'${failure.pointer}' does not hold a reference, so there is no declaration to project.`;
401
+ return failure.pointer === ""
402
+ ? "this resource declares no 'kind:', so there is no definition to project it through."
403
+ : `'${failure.pointer}' does not hold a reference, so there is no declaration to project.`;
265
404
  case "unresolved":
266
405
  return `'${failure.pointer}' references '${failure.name}', which resolves to no resource.`;
267
406
  case "ambiguous":
@@ -270,6 +409,17 @@ export function describeProjectionFailure(failure) {
270
409
  case "no-projection":
271
410
  return (`'${failure.pointer}' references a resource of kind '${failure.kind}', which declares no ` +
272
411
  `'x-telo-schema-projection' — so there is nothing for this slot to be typed from.`);
412
+ case "no-projection-map":
413
+ return (`kind '${failure.kind}' declares an 'x-telo-schema-projection' whose key field carries ` +
414
+ `no 'x-telo-schema-map', so there is no vocabulary to project its entries through and ` +
415
+ `'${failure.pointer || "this declaration"}' cannot be typed from it.`);
416
+ case "no-entries":
417
+ return (`'${failure.entries}' holds no entry collection on this ${failure.kind}, so the ` +
418
+ `projection has nothing to type '${failure.pointer || "this declaration"}' from.`);
419
+ case "entry-reference":
420
+ return (`entry '${failure.entry}' at '${failure.pointer}' references '${failure.name}', which ` +
421
+ `resolves to no declaration this analysis can read — so that entry is projected as an ` +
422
+ `open value and nothing typed from it is checked against the shape it was meant to have.`);
273
423
  }
274
424
  }
275
425
  /**
@@ -282,36 +432,74 @@ export function describeProjectionFailure(failure) {
282
432
  * `failures`, because degrading SILENTLY is the failure this whole mechanism
283
433
  * exists to move earlier: the consumer's contract quietly reopens and a
284
434
  * misspelled field passes `telo check` exactly as it did before.
435
+ *
436
+ * **A node that projected NOTHING is returned by IDENTITY**, and that is a
437
+ * correctness property of the caller rather than a micro-optimization:
438
+ * `DefinitionRegistry` memoizes a compiled AJV validator per schema OBJECT,
439
+ * because every resource of a kind is checked against the same one at keystroke
440
+ * time. Rebuilding each node unconditionally — which this did — misses that memo
441
+ * for every resource in the analysis, so AJV recompiled the whole kind schema
442
+ * once per resource: on `apps/hub` that was 197 compiles instead of 54, and 723
443
+ * ms instead of 97. Returning the input where nothing changed restores it for
444
+ * every kind that declares no projection at all, which is nearly all of them.
285
445
  */
286
446
  export function resolveSchemaProjections(schema, manifest, scope, failures) {
287
447
  if (Array.isArray(schema)) {
288
- return schema.map((item) => resolveSchemaProjections(item, manifest, scope, failures));
448
+ let moved = false;
449
+ const items = schema.map((item) => {
450
+ const next = resolveSchemaProjections(item, manifest, scope, failures);
451
+ if (next !== item)
452
+ moved = true;
453
+ return next;
454
+ });
455
+ return moved ? items : schema;
289
456
  }
290
457
  if (!isObject(schema))
291
458
  return schema;
292
459
  const pointer = readProjectionFrom(schema);
293
- if (pointer && manifest) {
294
- const target = refTarget(navigate(manifest, pointer), scope, pointer);
460
+ if (pointer !== undefined && manifest) {
461
+ // The EMPTY pointer names the declaration this annotation is written on,
462
+ // rather than a slot holding a reference to another. Resolution is skipped
463
+ // because the declaration is already in hand — which is what lets a kind
464
+ // type its own data against its own entries (a table's seed rows against its
465
+ // columns), something no reference could reach.
466
+ const target = pointer === ""
467
+ ? ownTarget(manifest, scope)
468
+ : refTarget(navigate(manifest, pointer), scope, pointer);
295
469
  if ("reason" in target) {
296
470
  failures?.push(target);
297
471
  }
298
472
  else {
473
+ const kind = String(target.manifest.kind ?? "<unknown>");
299
474
  const projection = readSchemaProjection(target.definition);
300
475
  const map = projection && projectionKeyMap(target.definition.schema, projection);
301
- const projected = projection && map ? projectEntries(target.manifest, projection, map) : undefined;
476
+ const projected = projection && map
477
+ ? projectEntries(target.manifest, projection, map, { scope, pointer, failures })
478
+ : undefined;
302
479
  if (projected) {
303
480
  const { ["x-telo-schema-projection-from"]: _dropped, ...rest } = schema;
304
481
  return { ...rest, ...projected };
305
482
  }
306
- failures?.push({
307
- reason: "no-projection",
308
- pointer,
309
- kind: String(target.manifest.kind ?? "<unknown>"),
310
- });
483
+ // Three distinct omissions, three repairs by three different authors: the
484
+ // kind declares no projection, the kind declares one the key field has no
485
+ // vocabulary for, or this DECLARATION simply lists no entries.
486
+ if (!projection)
487
+ failures?.push({ reason: "no-projection", pointer, kind });
488
+ else if (!map)
489
+ failures?.push({ reason: "no-projection-map", pointer, kind });
490
+ else {
491
+ failures?.push({ reason: "no-entries", pointer, kind, entries: projection.entries });
492
+ }
311
493
  }
312
494
  }
313
- return Object.fromEntries(Object.entries(schema).map(([key, value]) => [
314
- key,
315
- key.startsWith("x-telo-") ? value : resolveSchemaProjections(value, manifest, scope, failures),
316
- ]));
495
+ let moved = false;
496
+ const entries = Object.entries(schema).map(([key, value]) => {
497
+ const next = key.startsWith("x-telo-")
498
+ ? value
499
+ : resolveSchemaProjections(value, manifest, scope, failures);
500
+ if (next !== value)
501
+ moved = true;
502
+ return [key, next];
503
+ });
504
+ return moved ? Object.fromEntries(entries) : schema;
317
505
  }
@@ -1,3 +1,3 @@
1
1
  /** The surface generation this analyzer implements. */
2
- export declare const TELO_SURFACE_VERSION = "0.82.0";
2
+ export declare const TELO_SURFACE_VERSION = "0.82.1";
3
3
  //# sourceMappingURL=telo-version.d.ts.map
@@ -5,4 +5,4 @@
5
5
  // its release identity, this is the scale a module's `requires.telo` range is
6
6
  // written against, and every kernel in every language reports the same scale.
7
7
  /** The surface generation this analyzer implements. */
8
- export const TELO_SURFACE_VERSION = "0.82.0";
8
+ export const TELO_SURFACE_VERSION = "0.82.1";
@@ -1,4 +1,5 @@
1
1
  export { extractAccessChains, validateChainAgainstSchema } from "@telorun/templating";
2
+ export { extractCelRegionScopes, pathMatchesScope } from "./eval-paths.js";
2
3
  export interface ContextResolveOpts {
3
4
  /** When provided, used to resolve `x-telo-context-from-root` annotations against the
4
5
  * root manifest. When omitted, defaults to `manifestItem`. */
@@ -15,14 +16,6 @@ export interface ContextResolveOpts {
15
16
  allManifests?: Record<string, any>[];
16
17
  }
17
18
  export declare function resolveTypeFieldToSchema(value: unknown, allManifests: Record<string, any>[], ancestry?: ReadonlySet<string>): Record<string, any> | undefined;
18
- /**
19
- * Returns true when a CEL expression path (from walkCelExpressions, e.g. "routes[0].inputs.q")
20
- * falls within the scope of a context (e.g. "$.routes[*].inputs").
21
- *
22
- * The scope is matched directly (no sibling sharing): a context at "$.routes[*].inputs" only
23
- * applies to expressions whose path starts with "routes[N].inputs", not to other sibling fields.
24
- */
25
- export declare function pathMatchesScope(exprPath: string, scope: string): boolean;
26
19
  /**
27
20
  * Resolves `x-telo-context-*` annotations in a context schema using the concrete
28
21
  * manifest item (per-scope) and the manifest root.
@@ -91,13 +84,4 @@ export declare function extractContextsFromSchema(schema: Record<string, any>, p
91
84
  scope: string;
92
85
  schema: Record<string, any>;
93
86
  }>;
94
- /**
95
- * Walk a JSON Schema tree and collect the JSONPath scopes of every field that
96
- * declares a CEL-bearing region (`x-telo-context` / `x-telo-step-context` /
97
- * `x-telo-error-context`). Used — alongside `x-telo-eval` paths — to decide
98
- * whether a `!cel` expression sits in a slot the runtime actually evaluates.
99
- * Scopes use the same `$.a.b[*]` form as `extractContextsFromSchema`, matched
100
- * against expression paths with `pathMatchesScope`.
101
- */
102
- export declare function extractCelRegionScopes(schema: Record<string, any>, path?: string): string[];
103
87
  //# sourceMappingURL=validate-cel-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-cel-context.d.ts","sourceRoot":"","sources":["../src/validate-cel-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAWtF,MAAM,WAAW,kBAAkB;IACjC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC;;kDAE8C;IAC9C,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;KACxD,CAAC;IACF,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KAC/C,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CACtC;AA4CD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACnC,QAAQ,GAAE,WAAW,CAAC,MAAM,CAAa,GACxC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA0EjC;AAyID;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,IAAI,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAChD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAyKrB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAQrB;AAWD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,SAAM,GACT,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAAC,CAGvD;AAYD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,SAAM,GAAG,MAAM,EAAE,CAqBxF"}
1
+ {"version":3,"file":"validate-cel-context.d.ts","sourceRoot":"","sources":["../src/validate-cel-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAatF,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAG3E,MAAM,WAAW,kBAAkB;IACjC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC;;kDAE8C;IAC9C,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;KACxD,CAAC;IACF,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KAC/C,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CACtC;AA4CD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACnC,QAAQ,GAAE,WAAW,CAAC,MAAM,CAAa,GACxC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA0EjC;AAyID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,IAAI,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAChD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAyKrB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAQrB;AAWD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,SAAM,GACT,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAAC,CAGvD"}
@@ -1,8 +1,12 @@
1
1
  export { extractAccessChains, validateChainAgainstSchema } from "@telorun/templating";
2
2
  import { elementSchemaOf, isLiveSlot, mergeTypeSchemas, parseCanonicalTypeSchemaId, } from "@telorun/sdk";
3
3
  import { KERNEL_BUILTINS } from "./builtins.js";
4
- import { isStepSlot } from "./step-slot.js";
5
4
  import { withRefSlotsAsReadings } from "./ref-slot-reading.js";
5
+ // Where CEL is evaluated is one reader (`eval-paths.ts`), and the region half of
6
+ // it moved there so the scope walk and the `x-telo-eval` walk answer the same
7
+ // question in one place. Re-exported: this module is where every existing
8
+ // consumer imports them from.
9
+ export { extractCelRegionScopes, pathMatchesScope } from "./eval-paths.js";
6
10
  /**
7
11
  * Resolve a type field value (string name, inline type, or raw schema) to a JSON Schema.
8
12
  * - String: look up the named type in allManifests (Type.JsonSchema resources)
@@ -229,36 +233,6 @@ function collectionBindingWithheld(schema, manifestRoot, allManifests) {
229
233
  return false;
230
234
  return isLiveSlot(resolveCollectionSchema(manifestRoot, from, allManifests));
231
235
  }
232
- /**
233
- * Returns true when a CEL expression path (from walkCelExpressions, e.g. "routes[0].inputs.q")
234
- * falls within the scope of a context (e.g. "$.routes[*].inputs").
235
- *
236
- * The scope is matched directly (no sibling sharing): a context at "$.routes[*].inputs" only
237
- * applies to expressions whose path starts with "routes[N].inputs", not to other sibling fields.
238
- */
239
- export function pathMatchesScope(exprPath, scope) {
240
- const stripped = scope.startsWith("$.") ? scope.slice(2) : scope;
241
- if (!stripped)
242
- return false;
243
- // Split on wildcard array segments; each [*] must match a concrete [N] in exprPath
244
- const parts = stripped.split("[*]");
245
- let remaining = exprPath;
246
- for (let i = 0; i < parts.length; i++) {
247
- const part = parts[i];
248
- if (!remaining.startsWith(part))
249
- return false;
250
- remaining = remaining.slice(part.length);
251
- if (i < parts.length - 1) {
252
- // Expect a concrete array index like [0], [12], ...
253
- const m = remaining.match(/^\[\d+\]/);
254
- if (!m)
255
- return false;
256
- remaining = remaining.slice(m[0].length);
257
- }
258
- }
259
- // Expression must end here or continue into a child path
260
- return remaining === "" || remaining[0] === "." || remaining[0] === "[";
261
- }
262
236
  /**
263
237
  * Resolves `x-telo-context-*` annotations in a context schema using the concrete
264
238
  * manifest item (per-scope) and the manifest root.
@@ -496,45 +470,6 @@ export function extractContextsFromSchema(schema, path = "$") {
496
470
  const all = collectContexts(schema, path);
497
471
  return all.sort((a, b) => b.scope.length - a.scope.length);
498
472
  }
499
- /** Schema keys that declare a CEL-bearing region: a field carrying any of these
500
- * is evaluated at runtime, so a `!cel` inside it (or a descendant) is live. A
501
- * STEP BODY is one too, and says so through the grammar its items point at
502
- * rather than through a key — {@link isStepSlot} reads either spelling. */
503
- const CEL_REGION_KEYS = [
504
- "x-telo-context",
505
- "x-telo-step-context",
506
- "x-telo-error-context",
507
- ];
508
- /**
509
- * Walk a JSON Schema tree and collect the JSONPath scopes of every field that
510
- * declares a CEL-bearing region (`x-telo-context` / `x-telo-step-context` /
511
- * `x-telo-error-context`). Used — alongside `x-telo-eval` paths — to decide
512
- * whether a `!cel` expression sits in a slot the runtime actually evaluates.
513
- * Scopes use the same `$.a.b[*]` form as `extractContextsFromSchema`, matched
514
- * against expression paths with `pathMatchesScope`.
515
- */
516
- export function extractCelRegionScopes(schema, path = "$") {
517
- if (!schema || typeof schema !== "object")
518
- return [];
519
- const out = [];
520
- if (CEL_REGION_KEYS.some((k) => schema[k]) || isStepSlot(schema))
521
- out.push(path);
522
- if (schema.properties) {
523
- for (const [key, value] of Object.entries(schema.properties)) {
524
- out.push(...extractCelRegionScopes(value, `${path}.${key}`));
525
- }
526
- }
527
- if (schema.items && typeof schema.items === "object") {
528
- out.push(...extractCelRegionScopes(schema.items, `${path}[*]`));
529
- }
530
- for (const key of ["oneOf", "anyOf", "allOf"]) {
531
- if (Array.isArray(schema[key])) {
532
- for (const subschema of schema[key])
533
- out.push(...extractCelRegionScopes(subschema, path));
534
- }
535
- }
536
- return out;
537
- }
538
473
  function collectContexts(schema, path) {
539
474
  if (!schema || typeof schema !== "object")
540
475
  return [];
@@ -23,6 +23,16 @@ import { type AnalysisDiagnostic } from "./types.js";
23
23
  * Rejected, each with an actionable diagnostic pointing at `!ref`:
24
24
  * - the object form `{ kind, name }` (the old reference object), and
25
25
  * - a bare string (the old name / dotted-FQN reference).
26
+ *
27
+ * **A value-or-reference union slot is the one exception**, and it is decided by
28
+ * the value rather than by a new spelling: where the reference constraint is a
29
+ * *branch* of a union (`type:` on a column, holding either a storage class or a
30
+ * `!ref` to a declared enum), a SCALAR is a value and is left to that branch,
31
+ * while an object is still checked for the removed `{kind, name}` form unless a
32
+ * branch describes it. The rule guards a REMOVED spelling — a bare string is
33
+ * simply not a reference in Telo — so admitting one takes nothing away: what it
34
+ * costs is the answer when an author meant a reference and wrote a bare name,
35
+ * which against a closed branch AJV reports as an unknown value.
26
36
  */
27
37
  export declare function validateReferenceForms(resources: ResourceManifest[], registry: DefinitionRegistry, aliases?: AliasResolver, aliasesByModule?: Map<string, AliasResolver>): AnalysisDiagnostic[];
28
38
  //# sourceMappingURL=validate-reference-forms.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-reference-forms.d.ts","sourceRoot":"","sources":["../src/validate-reference-forms.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;AAGnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,kBAAkB,EAAE,CA+DtB"}
1
+ {"version":3,"file":"validate-reference-forms.d.ts","sourceRoot":"","sources":["../src/validate-reference-forms.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,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,kBAAkB,EAAE,CA0EtB"}
@@ -1,5 +1,6 @@
1
1
  import { isTaggedSentinel } from "@telorun/templating";
2
2
  import { visitManifest } from "./manifest-visitor.js";
3
+ import { satisfiesValueBranch } from "./reference-field-map.js";
3
4
  import { REF_VALIDATION_SKIP_KINDS as SYSTEM_KINDS } from "./system-kinds.js";
4
5
  import { DiagnosticSeverity } from "./types.js";
5
6
  const SOURCE = "telo-analyzer";
@@ -24,6 +25,16 @@ const SOURCE = "telo-analyzer";
24
25
  * Rejected, each with an actionable diagnostic pointing at `!ref`:
25
26
  * - the object form `{ kind, name }` (the old reference object), and
26
27
  * - a bare string (the old name / dotted-FQN reference).
28
+ *
29
+ * **A value-or-reference union slot is the one exception**, and it is decided by
30
+ * the value rather than by a new spelling: where the reference constraint is a
31
+ * *branch* of a union (`type:` on a column, holding either a storage class or a
32
+ * `!ref` to a declared enum), a SCALAR is a value and is left to that branch,
33
+ * while an object is still checked for the removed `{kind, name}` form unless a
34
+ * branch describes it. The rule guards a REMOVED spelling — a bare string is
35
+ * simply not a reference in Telo — so admitting one takes nothing away: what it
36
+ * costs is the answer when an author meant a reference and wrote a bare name,
37
+ * which against a closed branch AJV reports as an unknown value.
27
38
  */
28
39
  export function validateReferenceForms(resources, registry, aliases, aliasesByModule) {
29
40
  if (!aliases)
@@ -37,6 +48,18 @@ export function validateReferenceForms(resources, registry, aliases, aliasesByMo
37
48
  // `!ref` and `!cel`/`${{ }}` sentinels are the supported shapes.
38
49
  if (isTaggedSentinel(value))
39
50
  return;
51
+ // A SCALAR at a slot whose union has a value branch is a value, and the
52
+ // value branch is what judges it. Reporting a mistyped storage class as
53
+ // a malformed reference — "write it as '!ref txt'" — instructs the author
54
+ // to convert a typo into a reference, and AJV has already said what the
55
+ // value must be one of. The object form below is not ambiguous the same
56
+ // way: `{kind, name}` is the removed reference object, so it is still
57
+ // reported unless a branch genuinely describes that shape.
58
+ const hasValueBranch = (e.entry.valueBranches?.length ?? 0) > 0;
59
+ if (hasValueBranch && typeof value !== "object")
60
+ return;
61
+ if (satisfiesValueBranch(value, e.entry.valueBranches, registry))
62
+ return;
40
63
  const r = e.source;
41
64
  const resourceLabel = `${r.kind}/${r.metadata.name}`;
42
65
  const resourceData = { kind: r.kind, name: r.metadata.name };
@@ -1 +1 @@
1
- {"version":3,"file":"validate-references.d.ts","sourceRoot":"","sources":["../src/validate-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAOrD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAuD/F;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,GACvB,kBAAkB,EAAE,CAogBtB"}
1
+ {"version":3,"file":"validate-references.d.ts","sourceRoot":"","sources":["../src/validate-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAarD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAuD/F;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,GACvB,kBAAkB,EAAE,CA2gBtB"}
@@ -1,6 +1,6 @@
1
1
  import { isRefSentinel } from "@telorun/templating";
2
2
  import { visitManifest } from "./manifest-visitor.js";
3
- import { isInlineResource, resolveFieldEntries, resolveFieldValues } from "./reference-field-map.js";
3
+ import { isInlineResource, resolveFieldEntries, resolveFieldValues, satisfiesValueBranch, } from "./reference-field-map.js";
4
4
  import { navigateJsonPointer, substituteCelFields } from "./schema-compat.js";
5
5
  import { REF_VALIDATION_SKIP_KINDS as SYSTEM_KINDS } from "./system-kinds.js";
6
6
  import { resolveTypeFieldToSchema } from "./validate-cel-context.js";
@@ -91,7 +91,7 @@ export function validateReferences(resources, context) {
91
91
  // Group manifests by name to detect collisions. Two subtleties:
92
92
  //
93
93
  // 1. Some analyzer hosts emit the SAME physical document twice through
94
- // their pipeline — e.g. the telo-editor's `toAnalysisManifests` walks
94
+ // their pipeline — e.g. telo studio's `toAnalysisManifests` walks
95
95
  // each workspace module's documents independently, and a file
96
96
  // reachable from two angles (entry module + `include:` partial)
97
97
  // shows up twice. The fingerprint includes `sourceLine` so identical
@@ -278,6 +278,13 @@ export function validateReferences(resources, context) {
278
278
  if (typeof val !== "object")
279
279
  return;
280
280
  const refVal = val;
281
+ // A value the slot's own union describes is a value, not a reference —
282
+ // the same narrowing `validateReferenceForms` applies, through the same
283
+ // function, because it has to be applied here too: an OBJECT-shaped value
284
+ // branch would otherwise reach the structural check below and be reported
285
+ // as a reference missing 'kind' and 'name'.
286
+ if (satisfiesValueBranch(val, entry.valueBranches, registry))
287
+ return;
281
288
  // Skip inline resources — Phase 2 normalization hasn't run yet.
282
289
  if (isInlineResource(refVal))
283
290
  return;