@telorun/analyzer 0.62.1 → 0.64.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 (171) hide show
  1. package/dist/analysis-registry.d.ts +24 -0
  2. package/dist/analysis-registry.d.ts.map +1 -1
  3. package/dist/analysis-registry.js +41 -0
  4. package/dist/analyzer.d.ts +3 -37
  5. package/dist/analyzer.d.ts.map +1 -1
  6. package/dist/analyzer.js +396 -476
  7. package/dist/call-graph.d.ts.map +1 -1
  8. package/dist/call-graph.js +2 -7
  9. package/dist/cel-scope-query.d.ts +109 -0
  10. package/dist/cel-scope-query.d.ts.map +1 -0
  11. package/dist/cel-scope-query.js +270 -0
  12. package/dist/cel-scope.d.ts +166 -0
  13. package/dist/cel-scope.d.ts.map +1 -0
  14. package/dist/cel-scope.js +377 -0
  15. package/dist/definition-registry.d.ts +15 -0
  16. package/dist/definition-registry.d.ts.map +1 -1
  17. package/dist/definition-registry.js +25 -9
  18. package/dist/find-manifest.d.ts +10 -0
  19. package/dist/find-manifest.d.ts.map +1 -0
  20. package/dist/find-manifest.js +12 -0
  21. package/dist/flatten-for-analyzer.js +1 -1
  22. package/dist/import-resolution-diagnostics.d.ts +9 -2
  23. package/dist/import-resolution-diagnostics.d.ts.map +1 -1
  24. package/dist/import-resolution-diagnostics.js +26 -7
  25. package/dist/index.d.ts +22 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +22 -1
  28. package/dist/invocation-contract.d.ts +17 -1
  29. package/dist/invocation-contract.d.ts.map +1 -1
  30. package/dist/invocation-contract.js +42 -4
  31. package/dist/loaded-types.d.ts +12 -3
  32. package/dist/loaded-types.d.ts.map +1 -1
  33. package/dist/manifest-analysis.d.ts +73 -0
  34. package/dist/manifest-analysis.d.ts.map +1 -0
  35. package/dist/manifest-analysis.js +78 -0
  36. package/dist/manifest-loader.d.ts +0 -1
  37. package/dist/manifest-loader.d.ts.map +1 -1
  38. package/dist/manifest-loader.js +62 -41
  39. package/dist/manifest-path.d.ts +18 -0
  40. package/dist/manifest-path.d.ts.map +1 -0
  41. package/dist/manifest-path.js +37 -0
  42. package/dist/manifest-schemas.d.ts +699 -0
  43. package/dist/manifest-schemas.d.ts.map +1 -1
  44. package/dist/manifest-schemas.js +306 -14
  45. package/dist/module-compatibility.d.ts +40 -0
  46. package/dist/module-compatibility.d.ts.map +1 -0
  47. package/dist/module-compatibility.js +52 -0
  48. package/dist/ref-slot-reading.d.ts +30 -0
  49. package/dist/ref-slot-reading.d.ts.map +1 -0
  50. package/dist/ref-slot-reading.js +139 -0
  51. package/dist/referrer-rule.d.ts +67 -0
  52. package/dist/referrer-rule.d.ts.map +1 -0
  53. package/dist/referrer-rule.js +96 -0
  54. package/dist/release/index.d.ts +1 -1
  55. package/dist/release/index.d.ts.map +1 -1
  56. package/dist/release/index.js +1 -1
  57. package/dist/release/version-stamp.d.ts +16 -0
  58. package/dist/release/version-stamp.d.ts.map +1 -1
  59. package/dist/release/version-stamp.js +52 -0
  60. package/dist/release/workspace-config.d.ts +10 -6
  61. package/dist/release/workspace-config.d.ts.map +1 -1
  62. package/dist/release/workspace-config.js +10 -6
  63. package/dist/resolve-schema-ref-kinds.d.ts +4 -3
  64. package/dist/resolve-schema-ref-kinds.d.ts.map +1 -1
  65. package/dist/resolve-schema-ref-kinds.js +16 -0
  66. package/dist/resolve-throws-union.d.ts.map +1 -1
  67. package/dist/resolve-throws-union.js +4 -3
  68. package/dist/resolve-zone-containment.d.ts +99 -0
  69. package/dist/resolve-zone-containment.d.ts.map +1 -0
  70. package/dist/resolve-zone-containment.js +212 -0
  71. package/dist/resolve-zone-requirements.d.ts +5 -0
  72. package/dist/resolve-zone-requirements.d.ts.map +1 -1
  73. package/dist/resolve-zone-requirements.js +48 -7
  74. package/dist/resource-rule.d.ts +106 -0
  75. package/dist/resource-rule.d.ts.map +1 -0
  76. package/dist/resource-rule.js +236 -0
  77. package/dist/rule-condition.d.ts +50 -0
  78. package/dist/rule-condition.d.ts.map +1 -0
  79. package/dist/rule-condition.js +108 -0
  80. package/dist/schema-keywords.d.ts.map +1 -1
  81. package/dist/schema-keywords.js +20 -0
  82. package/dist/schema-projection.d.ts +171 -0
  83. package/dist/schema-projection.d.ts.map +1 -0
  84. package/dist/schema-projection.js +317 -0
  85. package/dist/schema-walk.d.ts +25 -0
  86. package/dist/schema-walk.d.ts.map +1 -0
  87. package/dist/schema-walk.js +126 -0
  88. package/dist/step-slot.d.ts +39 -0
  89. package/dist/step-slot.d.ts.map +1 -0
  90. package/dist/step-slot.js +49 -0
  91. package/dist/telo-version.d.ts +1 -1
  92. package/dist/telo-version.js +1 -1
  93. package/dist/validate-cel-context.d.ts.map +1 -1
  94. package/dist/validate-cel-context.js +17 -3
  95. package/dist/validate-durable-regions.d.ts +15 -0
  96. package/dist/validate-durable-regions.d.ts.map +1 -0
  97. package/dist/validate-durable-regions.js +336 -0
  98. package/dist/validate-references.d.ts.map +1 -1
  99. package/dist/validate-references.js +44 -2
  100. package/dist/validate-referrer-rules.d.ts +120 -0
  101. package/dist/validate-referrer-rules.d.ts.map +1 -0
  102. package/dist/validate-referrer-rules.js +255 -0
  103. package/dist/validate-resource-rules.d.ts +114 -0
  104. package/dist/validate-resource-rules.d.ts.map +1 -0
  105. package/dist/validate-resource-rules.js +283 -0
  106. package/dist/validate-schema-projection.d.ts +32 -0
  107. package/dist/validate-schema-projection.d.ts.map +1 -0
  108. package/dist/validate-schema-projection.js +110 -0
  109. package/dist/validate-step-inputs.d.ts +19 -1
  110. package/dist/validate-step-inputs.d.ts.map +1 -1
  111. package/dist/validate-step-inputs.js +102 -9
  112. package/dist/validate-throws-coverage.d.ts.map +1 -1
  113. package/dist/validate-throws-coverage.js +11 -5
  114. package/dist/validate-zone-slots.d.ts +2 -2
  115. package/dist/validate-zone-slots.d.ts.map +1 -1
  116. package/dist/validate-zone-slots.js +202 -6
  117. package/dist/validate-zone-violations.d.ts +12 -0
  118. package/dist/validate-zone-violations.d.ts.map +1 -0
  119. package/dist/validate-zone-violations.js +169 -0
  120. package/dist/value-type-keyword.d.ts +1 -1
  121. package/dist/value-type-keyword.d.ts.map +1 -1
  122. package/dist/value-type-keyword.js +4 -0
  123. package/dist/zone-slot.d.ts +61 -0
  124. package/dist/zone-slot.d.ts.map +1 -1
  125. package/dist/zone-slot.js +86 -5
  126. package/package.json +2 -2
  127. package/src/analysis-registry.ts +43 -0
  128. package/src/analyzer.ts +452 -595
  129. package/src/call-graph.ts +2 -8
  130. package/src/cel-scope-query.ts +337 -0
  131. package/src/cel-scope.ts +570 -0
  132. package/src/definition-registry.ts +31 -13
  133. package/src/find-manifest.ts +19 -0
  134. package/src/flatten-for-analyzer.ts +1 -1
  135. package/src/import-resolution-diagnostics.ts +25 -7
  136. package/src/index.ts +86 -0
  137. package/src/invocation-contract.ts +67 -2
  138. package/src/loaded-types.ts +12 -3
  139. package/src/manifest-analysis.ts +132 -0
  140. package/src/manifest-loader.ts +87 -55
  141. package/src/manifest-path.ts +34 -0
  142. package/src/manifest-schemas.ts +328 -14
  143. package/src/module-compatibility.ts +72 -0
  144. package/src/ref-slot-reading.ts +156 -0
  145. package/src/referrer-rule.ts +116 -0
  146. package/src/release/index.ts +1 -0
  147. package/src/release/version-stamp.ts +58 -0
  148. package/src/release/workspace-config.ts +10 -6
  149. package/src/resolve-schema-ref-kinds.ts +28 -4
  150. package/src/resolve-throws-union.ts +4 -3
  151. package/src/resolve-zone-containment.ts +329 -0
  152. package/src/resolve-zone-requirements.ts +67 -7
  153. package/src/resource-rule.ts +252 -0
  154. package/src/rule-condition.ts +126 -0
  155. package/src/schema-keywords.ts +24 -0
  156. package/src/schema-projection.ts +403 -0
  157. package/src/schema-walk.ts +144 -0
  158. package/src/step-slot.ts +64 -0
  159. package/src/telo-version.ts +1 -1
  160. package/src/validate-cel-context.ts +17 -3
  161. package/src/validate-durable-regions.ts +401 -0
  162. package/src/validate-references.ts +57 -2
  163. package/src/validate-referrer-rules.ts +373 -0
  164. package/src/validate-resource-rules.ts +401 -0
  165. package/src/validate-schema-projection.ts +184 -0
  166. package/src/validate-step-inputs.ts +148 -10
  167. package/src/validate-throws-coverage.ts +12 -5
  168. package/src/validate-zone-slots.ts +192 -8
  169. package/src/validate-zone-violations.ts +190 -0
  170. package/src/value-type-keyword.ts +4 -0
  171. package/src/zone-slot.ts +114 -5
@@ -0,0 +1,212 @@
1
+ import { propertySchemas, resolveLocalRef } from "./manifest-navigation.js";
2
+ import { isStepSlot } from "./step-slot.js";
3
+ import { readProvidesZone } from "./zone-slot.js";
4
+ const moduleOf = (node) => node.manifest.metadata?.module;
5
+ /** The schema node at a field-map path, following `[]` into `items`, `{}` into
6
+ * `additionalProperties` and local `$defs` refs. Same navigation the projection
7
+ * performs — a slot's annotations live wherever this lands. */
8
+ function schemaNodeAt(rootSchema, slotPath) {
9
+ if (!rootSchema)
10
+ return undefined;
11
+ let current = rootSchema;
12
+ for (const segment of slotPath.split(".")) {
13
+ if (!current)
14
+ return undefined;
15
+ const bare = segment.replace(/(\[\]|\{\})+$/g, "");
16
+ let next = propertySchemas(current).find(([k]) => k === bare)?.[1];
17
+ for (const marker of segment.slice(bare.length).match(/\[\]|\{\}/g) ?? []) {
18
+ next = resolveLocalRef(marker === "[]"
19
+ ? next?.items
20
+ : next?.additionalProperties, rootSchema);
21
+ if (!next || typeof next !== "object")
22
+ return undefined;
23
+ }
24
+ current = resolveLocalRef(next, rootSchema);
25
+ }
26
+ return current;
27
+ }
28
+ /**
29
+ * Does a zone's lifetime extend through this edge?
30
+ *
31
+ * EVERY member of `use` must be `call`. This is the landed reduction, and its
32
+ * asymmetry with the propagation rule is deliberate: a set says several
33
+ * relations hold at once, so a slot declaring `[call, detached]` really does
34
+ * detach on some dispatch, and a detached dispatch is never inside the caller's
35
+ * zone. An edge whose `use` could not be read extends nothing either — the
36
+ * conservative direction HERE, since over-reporting containment would invent
37
+ * failures inside regions that are correct.
38
+ */
39
+ function extendsZone(edge) {
40
+ return edge.use.length > 0 && !edge.unresolved && edge.use.every((u) => u === "call");
41
+ }
42
+ /** Uses on an edge that leave the region, for the boundary record. */
43
+ function escapingUses(edge) {
44
+ return edge.use.filter((u) => u !== "call" && u !== "dependency" && u !== "schema");
45
+ }
46
+ /** Walk down from one entry edge, collecting what the zone reaches. */
47
+ function collect(graph, entries, contents, boundaries) {
48
+ const queue = [...entries];
49
+ while (queue.length > 0) {
50
+ const current = queue.shift();
51
+ if (contents.has(current.node.id))
52
+ continue;
53
+ contents.set(current.node.id, { node: current.node, via: current.via });
54
+ // A resource's own step nodes are part of whatever region reaches the
55
+ // resource: a step is where a check anchors (`the retry at
56
+ // importAll/fetch`), and its outgoing edge is the dispatch a rule judges.
57
+ const stepNodes = current.node.type === "resource" ? graph.steps(current.node.id) : [];
58
+ for (const step of stepNodes) {
59
+ if (contents.has(step.id))
60
+ continue;
61
+ queue.push({
62
+ node: step,
63
+ via: [...current.via, step.name ? `step '${step.name}'` : step.path],
64
+ });
65
+ }
66
+ const owner = current.node.type === "resource"
67
+ ? current.node
68
+ : graph.nodes.get(current.node.owner);
69
+ for (const edge of graph.edgesFrom(current.node.id)) {
70
+ if (edge.use.every((u) => u === "dependency" || u === "schema"))
71
+ continue;
72
+ const target = edge.to ? graph.nodes.get(edge.to) : undefined;
73
+ if (!extendsZone(edge)) {
74
+ if (owner && escapingUses(edge).length > 0) {
75
+ boundaries.push({
76
+ edge,
77
+ from: owner,
78
+ via: current.via,
79
+ escaping: escapingUses(edge),
80
+ });
81
+ }
82
+ continue;
83
+ }
84
+ if (!target)
85
+ continue;
86
+ queue.push({
87
+ node: target,
88
+ via: [...current.via, `${edge.slot} → ${edge.toName}`],
89
+ });
90
+ }
91
+ }
92
+ }
93
+ /**
94
+ * Every region in the graph opened by a slot declaring `attribute`.
95
+ *
96
+ * The provider itself is NOT in `contents` — a zone constrains what runs inside
97
+ * its body, not the resource that establishes it. A transaction's own
98
+ * `connection:` dependency is outside the region it opens, and a rule that
99
+ * treated the provider as contained would report the provider against its own
100
+ * constraint.
101
+ */
102
+ export function findZoneRegions(graph, resolveDef, attribute) {
103
+ const regions = [];
104
+ for (const node of graph.nodes.values()) {
105
+ if (node.type !== "resource")
106
+ continue;
107
+ const def = resolveDef(node.kind, moduleOf(node));
108
+ const rootSchema = def?.schema;
109
+ if (!rootSchema)
110
+ continue;
111
+ // The providing slots of this kind, found by walking its own declared
112
+ // properties rather than by knowing any kind's field names.
113
+ for (const [slot, slotSchema] of providingSlots(rootSchema)) {
114
+ const provides = readProvidesZone(slotSchema);
115
+ const reason = provides?.attributes[attribute];
116
+ if (!provides || typeof reason !== "string")
117
+ continue;
118
+ const contents = new Map();
119
+ const boundaries = [];
120
+ // Shape one: the slot carries a step array natively. Its steps are the
121
+ // body, and they are owned by this resource, so they are found by path
122
+ // rather than by an edge.
123
+ if (isStepSlot(slotSchema)) {
124
+ const entries = graph
125
+ .steps(node.id)
126
+ .filter((step) => step.array === slot || step.array.startsWith(`${slot}[`))
127
+ .map((step) => ({
128
+ node: step,
129
+ via: [step.name ? `step '${step.name}'` : step.path],
130
+ }));
131
+ collect(graph, entries, contents, boundaries);
132
+ }
133
+ else {
134
+ // Shape two: the slot references an executable. Its edges are the body.
135
+ // Entered regardless of the slot's OWN use — a providing slot
136
+ // establishes its zone before the enclosing lifetime terminates on that
137
+ // use, which is what lets a detached durable body both shed every
138
+ // enclosing zone and open its own.
139
+ const entries = graph
140
+ .edgesFrom(node.id)
141
+ .filter((edge) => edge.slot === slot && edge.to)
142
+ .map((edge) => ({
143
+ node: graph.nodes.get(edge.to),
144
+ via: [`${slot} → ${edge.toName}`],
145
+ }))
146
+ .filter((entry) => entry.node !== undefined);
147
+ collect(graph, entries, contents, boundaries);
148
+ }
149
+ regions.push({
150
+ attribute,
151
+ reason,
152
+ provider: node,
153
+ slot,
154
+ attributes: provides.attributes,
155
+ contents,
156
+ boundaries,
157
+ });
158
+ }
159
+ }
160
+ return regions;
161
+ }
162
+ /** Field-map paths of every slot in a kind's schema carrying a provides-zone
163
+ * annotation, with the schema node at each. Walks properties, array items and
164
+ * `additionalProperties`, resolving local `$ref`s — the paths the call graph's
165
+ * own edges are keyed by, so a slot found here matches an edge's `slot`. */
166
+ function providingSlots(rootSchema) {
167
+ const found = [];
168
+ const seen = new Set();
169
+ const walk = (schema, path) => {
170
+ const node = resolveLocalRef(schema, rootSchema);
171
+ if (!node || typeof node !== "object" || seen.has(node))
172
+ return;
173
+ seen.add(node);
174
+ if (path && readProvidesZone(node))
175
+ found.push([path, node]);
176
+ for (const [key, child] of propertySchemas(node)) {
177
+ const childPath = path ? `${path}.${key}` : key;
178
+ walk(child, childPath);
179
+ // A slot's zone annotation may sit on the ARRAY (`steps`) or on its item
180
+ // (`routes[].handler`); both are real field-map paths.
181
+ const items = resolveLocalRef(child?.items, rootSchema);
182
+ if (items)
183
+ walk(items, `${childPath}[]`);
184
+ const additional = resolveLocalRef(child?.additionalProperties, rootSchema);
185
+ if (additional)
186
+ walk(additional, `${childPath}{}`);
187
+ }
188
+ };
189
+ walk(rootSchema, "");
190
+ return found;
191
+ }
192
+ /** Convenience for a consumer that only wants membership: every node id inside
193
+ * any region opened by `attribute`, mapped to the region that contains it. */
194
+ export function containmentIndex(regions) {
195
+ const index = new Map();
196
+ for (const region of regions) {
197
+ for (const id of region.contents.keys()) {
198
+ if (!index.has(id))
199
+ index.set(id, region);
200
+ }
201
+ }
202
+ return index;
203
+ }
204
+ /** The manifests a region's contents belong to — what a diagnostic anchors on. */
205
+ export function regionManifests(region) {
206
+ const out = [];
207
+ for (const { node } of region.contents.values()) {
208
+ if (node.type === "resource")
209
+ out.push(node.manifest);
210
+ }
211
+ return out;
212
+ }
@@ -35,6 +35,11 @@ export interface ZoneRequirementSpec {
35
35
  * check compares against `exports.resources`. */
36
36
  correlationName?: string;
37
37
  reason?: string;
38
+ /** Attributes the satisfying zone must declare. Carried across the export
39
+ * boundary with the rest of the requirement: a consumer discharging a
40
+ * library's open requirement must meet the same guarantee the library's own
41
+ * internals would have. */
42
+ attributes?: string[];
38
43
  /** Label of the requiring resource (`sql.Command 'charge'`). */
39
44
  origin: string;
40
45
  /** Resource names on the propagation path so far, origin first. */
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-zone-requirements.d.ts","sourceRoot":"","sources":["../src/resolve-zone-requirements.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolve-zone-requirements.d.ts","sourceRoot":"","sources":["../src/resolve-zone-requirements.ts"],"names":[],"mappings":"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"}
@@ -232,6 +232,7 @@ export function projectZoneRequirements(args) {
232
232
  if (!bucket.some((r) => `${r.zone}\0${r.correlation ?? ""}` === req.key)) {
233
233
  bucket.push({
234
234
  zone: req.zone,
235
+ ...(req.attributes.length > 0 ? { attributes: req.attributes } : {}),
235
236
  correlation: req.correlation,
236
237
  correlationLabel: req.correlationLabel,
237
238
  correlationName: req.correlationName,
@@ -255,16 +256,44 @@ export function projectZoneRequirements(args) {
255
256
  // site. Checked BEFORE termination, so a terminating provider slot
256
257
  // (a detached durable body) still discharges its own zone.
257
258
  const callerDef = resolveDef(caller.kind, moduleOf(caller));
258
- const slotSchema = schemaNodeAt(callerDef?.schema, edge.slot);
259
+ // WHICH slot establishes the zone depends on the body's SHAPE, and both
260
+ // shapes are real. A ref body annotates the slot the edge itself names
261
+ // (`Sql.Transaction.steps`, holding a `!ref` to an executable). A NATIVE
262
+ // body annotates the step ARRAY, while the edge comes from a step inside
263
+ // it and names that step's own `invoke:` — so looking only at `edge.slot`
264
+ // finds no annotation and the enclosing zone never discharges anything.
265
+ // Handling one shape would make every requirement inside a natively-bodied
266
+ // zone unsatisfiable, which is exactly what a durable workflow's body is.
267
+ const bodySlot = from.type === "step" ? from.array.replace(/\[\d+\].*$/, "") : edge.slot;
268
+ const slotSchema = schemaNodeAt(callerDef?.schema, bodySlot);
259
269
  const provides = readProvidesZone(slotSchema);
260
270
  if (provides && callerDef && req.accepted.has(canonicalOf(callerDef))) {
271
+ // A native body's annotation sits on the array, whose enclosing object
272
+ // IS the resource root — so its correlation pointer anchors there,
273
+ // rather than at the step item the edge happens to come from.
261
274
  const providerKey = provides.key
262
- ? resolveProviderKey(caller, edge, provides.key, resolveName)
275
+ ? from.type === "step"
276
+ ? resolveStaticKey(caller, [provides.key], resolveName)
277
+ : resolveProviderKey(caller, edge, provides.key, resolveName)
263
278
  : undefined;
264
279
  const discharged = req.correlation === undefined ||
265
280
  (providerKey !== undefined && correlationIdOf(providerKey) === req.correlation);
266
- if (discharged)
281
+ if (discharged) {
282
+ // The zone is the right KIND but may not make the promise the
283
+ // requirement is built on. Reported here rather than at the providing
284
+ // module, because this is the only point where both halves are in
285
+ // hand: the requirement's demanded attributes and the slot that would
286
+ // satisfy it. The requirement still discharges — the zone IS open, so
287
+ // reporting it unsatisfied as well would name the same defect twice
288
+ // with opposite words.
289
+ const missing = req.attributes.filter((a) => !(a in provides.attributes));
290
+ if (missing.length > 0) {
291
+ emit(DiagnosticSeverity.Error, "ZONE_ATTRIBUTE_MISSING", edge, caller, req, via, `reaches a ${canonicalOf(callerDef)} zone that does not declare ` +
292
+ `${missing.join(", ")}, so the guarantee this requirement is built on ` +
293
+ `does not hold inside it`);
294
+ }
267
295
  continue;
296
+ }
268
297
  }
269
298
  // A dynamic selector is already a hard diagnostic (validate-ref-slots);
270
299
  // zones do not re-report it — and must not propagate through a use they
@@ -352,14 +381,24 @@ export function projectZoneRequirements(args) {
352
381
  continue;
353
382
  const def = resolveDef(node.kind, meta?.module);
354
383
  const schema = def?.schema;
355
- if (!def || !schema?.properties)
384
+ if (!def || !schema)
356
385
  continue;
357
- for (const [field, propSchema] of Object.entries(schema.properties)) {
358
- const requires = readRequiresZone(propSchema);
386
+ // A requirement at the SCHEMA ROOT is unconditional: every instance of the
387
+ // kind must be inside the zone, whatever it is configured with. A parking
388
+ // kind is exactly that shape — it has no option that turns the requirement
389
+ // on — and a field-level annotation would have to be attached to some
390
+ // arbitrary property and would then read "…when you set this one".
391
+ const slots = [[schema, undefined]];
392
+ for (const [field, propSchema] of Object.entries((schema.properties ?? {}))) {
393
+ slots.push([propSchema, field]);
394
+ }
395
+ for (const [slotSchema, field] of slots) {
396
+ const requires = readRequiresZone(slotSchema);
359
397
  if (!requires)
360
398
  continue;
361
- if (node.manifest[field] === undefined)
399
+ if (field !== undefined && node.manifest[field] === undefined) {
362
400
  continue;
401
+ }
363
402
  const zoneDef = resolveDef(requires.zone, def.metadata.module);
364
403
  if (!zoneDef)
365
404
  continue; // ZONE_PROVIDER_UNRESOLVED is reported at registration
@@ -368,6 +407,7 @@ export function projectZoneRequirements(args) {
368
407
  const req = {
369
408
  zone,
370
409
  accepted: acceptedFor(zone),
410
+ attributes: requires.attributes,
371
411
  correlation: target ? correlationIdOf(target) : undefined,
372
412
  correlationLabel: target ? labelOf(target) : undefined,
373
413
  correlationName: target?.name,
@@ -396,6 +436,7 @@ export function projectZoneRequirements(args) {
396
436
  const req = {
397
437
  zone: spec.zone,
398
438
  accepted: zoneDef ? acceptedFor(spec.zone) : new Set([spec.zone]),
439
+ attributes: spec.attributes ?? [],
399
440
  correlation: spec.correlation,
400
441
  correlationLabel: spec.correlationLabel,
401
442
  correlationName: spec.correlationName,
@@ -0,0 +1,106 @@
1
+ /**
2
+ * The single reader for `x-telo-resource-rules` — a kind declaring, as data,
3
+ * relationships between the fields of one resource that JSON Schema cannot
4
+ * state: an index naming a column its table does not declare, a foreign key
5
+ * whose two sides differ in length, a rename whose source is still declared.
6
+ *
7
+ * The predicate is CEL over `self` (the whole resource) and `this` (the element
8
+ * under test), so correlating two collections is a comprehension closure rather
9
+ * than a path language with wildcard bindings to design. `in:` names the
10
+ * collection to iterate and IS the diagnostic anchor: iterating what the pointer
11
+ * names is what makes a reported path exist by construction.
12
+ *
13
+ * Vocabulary borrowed from `Telo.JsonSchema.rules` — `condition` true when the
14
+ * rule HOLDS, the subject bound as `this`, plus `code` and `message` — because
15
+ * two CEL rule vocabularies with opposite polarity is a trap an author falls
16
+ * into once per rule. The two mechanisms stay separate: a `Telo.JsonSchema` rule
17
+ * runs at dispatch against a value, this one at `telo check` against a manifest.
18
+ *
19
+ * Lenient by design, the `ref-slot.ts` precedent: anything unreadable here reads
20
+ * as absent, and `validate-resource-rules.ts` is the strict half that reports it.
21
+ *
22
+ * Browser-safe: no Node built-ins.
23
+ */
24
+ export declare const RESOURCE_RULES_ANNOTATION = "x-telo-resource-rules";
25
+ export type ResourceRuleSeverity = "error" | "warning";
26
+ export interface ResourceRule {
27
+ /** JSON Pointer to the collection to iterate. Absent = the whole-resource
28
+ * form, which reports at the resource rather than at an element. */
29
+ readonly in?: string;
30
+ /** CEL source. TRUE when the rule holds. */
31
+ readonly condition: string;
32
+ /** The rule's own name, carried in `data.rule`. Never a diagnostic code —
33
+ * every violation reports under the analyzer-owned envelope. */
34
+ readonly code: string;
35
+ readonly message: string;
36
+ readonly severity: ResourceRuleSeverity;
37
+ /** Position in the annotation array — the anchor for a declaration defect. */
38
+ readonly index: number;
39
+ }
40
+ /** One element a rule is evaluated against. */
41
+ export interface RuleSubject {
42
+ /** Dotted/bracketed path from the resource root, for `data.path`. */
43
+ readonly path: string;
44
+ readonly value: unknown;
45
+ /** Present when the collection is a map: the entry's key. */
46
+ readonly key?: string;
47
+ }
48
+ /** A precompiled `!cel` node keeps its author-written text on `source`; a plain
49
+ * string is taken verbatim so a rule reads the same however the loader was
50
+ * configured (a round-trip view runs with `compile` off). */
51
+ export declare function celSourceOf(value: unknown): string | undefined;
52
+ /** The annotation exactly as written, for the strict half. `undefined` when the
53
+ * kind declares none; a non-array is returned as-is so the shape can be
54
+ * reported rather than silently skipped. */
55
+ export declare function readRawResourceRules(schema: unknown): unknown;
56
+ /** Every rule this kind declares that is well-formed enough to run. */
57
+ export declare function readResourceRules(schema: unknown): ResourceRule[];
58
+ /** Split a JSON Pointer into its unescaped segments. `/` alone is the root. */
59
+ export declare function pointerSegments(pointer: string): string[] | undefined;
60
+ /** Navigate a pointer within a value, stopping at anything that is not a plain
61
+ * container. Returns `undefined` for a pointer that does not resolve — which a
62
+ * resource legitimately produces by omitting an optional collection. */
63
+ export declare function resolvePointer(value: unknown, pointer: string): unknown;
64
+ /** Path prefix for a pointer, in the dotted/bracketed spelling diagnostics use
65
+ * (`foreignKeys[0].references`), so `data.path` is one grammar throughout. */
66
+ export declare function pointerToPath(pointer: string): string;
67
+ /**
68
+ * The elements a rule iterates. An array yields one subject per item, a map one
69
+ * per entry with its key bound; a collection that is absent yields none, which
70
+ * is a rule that had nothing to say rather than a rule that failed.
71
+ *
72
+ * A scalar at the pointer is `undefined` — not an empty list — because that is a
73
+ * declaration defect the strict half reports, and an empty list would hide it.
74
+ */
75
+ export declare function resolveRuleSubjects(config: unknown, pointer: string): RuleSubject[] | undefined;
76
+ /**
77
+ * Path of the first CEL leaf inside a value, or `undefined` when every leaf is
78
+ * literal. A rule reading an expression would be evaluating a placeholder, so
79
+ * the subject is skipped — and the skip is reported, never silent.
80
+ *
81
+ * Stops at nested inline `{ kind }` declarations for the reason every other walk
82
+ * does: that CEL belongs to the nested kind, evaluated in its own scope.
83
+ */
84
+ export declare function findDynamicLeaf(value: unknown, base?: string): string | undefined;
85
+ /**
86
+ * The nodes a condition actually READS, resolved against the resource and the
87
+ * element under test — the input to the dynamic-leaf check.
88
+ *
89
+ * Scanning the whole subject instead would make the check useless in exactly
90
+ * the shape it matters most: a resource-wide rule takes the whole resource as
91
+ * its subject, so one unrelated `!cel` anywhere (a `version:` read from
92
+ * `module.version`, which is the conventional spelling) would switch off every
93
+ * such rule. Chains are the same primitive the binding-order derivation uses:
94
+ * parsed, never lexed, so a name inside a string literal reads nothing.
95
+ *
96
+ * A chain stops at a computed index (`INDEX_SEGMENT`): what it selects is not
97
+ * known statically, so the node reached so far is what gets checked — the
98
+ * over-approximating direction, which errs toward skipping rather than toward
99
+ * evaluating a placeholder.
100
+ */
101
+ export declare function readNodes(chains: readonly (readonly string[])[],
102
+ /** The bindings in scope, by name — `self`/`this`/`key` for a resource rule,
103
+ * `self`/`referrer` for a referrer rule. A chain rooted at a name that is not
104
+ * bound reads nothing. */
105
+ roots: Record<string, unknown>): unknown[];
106
+ //# sourceMappingURL=resource-rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-rule.d.ts","sourceRoot":"","sources":["../src/resource-rule.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,eAAO,MAAM,yBAAyB,0BAA0B,CAAC;AAEjE,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B;yEACqE;IACrE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;qEACiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,+CAA+C;AAC/C,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,6DAA6D;IAC7D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAeD;;8DAE8D;AAC9D,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI9D;AAED;;6CAE6C;AAC7C,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAG7D;AAED,uEAAuE;AACvE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,YAAY,EAAE,CA0BjE;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAOrE;AAED;;yEAEyE;AACzE,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAiBvE;AAED;+EAC+E;AAC/E,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMrD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,GACd,WAAW,EAAE,GAAG,SAAS,CAe3B;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,SAAK,GAAG,MAAM,GAAG,SAAS,CAiB7E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE;AACtC;;2BAE2B;AAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,EAAE,CAqBX"}