@intentius/chant 0.60.0 → 0.61.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.
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/lifecycle.ts"],"names":[],"mappings":"AA6CA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKlD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAE9E;AAmCD;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAyH/E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA4C3E;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA8B/E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAmL3E;AAkrBD;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAwQ3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA2L/E;AAsBD;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB1E;AAgBD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAmE7E;AAiCD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9E;AAkBD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA+C/E"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/lifecycle.ts"],"names":[],"mappings":"AA6CA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKlD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAE9E;AAmCD;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAyH/E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA4C3E;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA8B/E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAmL3E;AAmrBD;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAwQ3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA2L/E;AAsBD;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB1E;AAgBD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAmE7E;AAiCD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9E;AAkBD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA+C/E"}
@@ -16,6 +16,7 @@ export interface JsonSchemaProperty {
16
16
  items?: JsonSchemaProperty;
17
17
  oneOf?: JsonSchemaProperty[];
18
18
  anyOf?: JsonSchemaProperty[];
19
+ allOf?: JsonSchemaProperty[];
19
20
  properties?: Record<string, JsonSchemaProperty>;
20
21
  required?: string[];
21
22
  enum?: string[];
@@ -56,8 +57,10 @@ export declare function primaryType(type: string | string[] | undefined): string
56
57
  * @param resolveDefName - Callback to produce a TypeScript name from a definition.
57
58
  * Receives (defName: string) and should return the TS type name for that definition.
58
59
  * When null, $ref to object definitions resolves to "any".
60
+ * @param seen - Definition names already on the current resolution path, so a
61
+ * list definition that reaches itself terminates instead of recursing forever.
59
62
  */
60
- export declare function resolvePropertyType(prop: JsonSchemaProperty | undefined, schema: JsonSchemaDocument, resolveDefName: ((defName: string) => string) | null): string;
63
+ export declare function resolvePropertyType(prop: JsonSchemaProperty | undefined, schema: JsonSchemaDocument, resolveDefName: ((defName: string) => string) | null, seen?: ReadonlySet<string>): string;
61
64
  /**
62
65
  * Resolve a $ref pointer to a TypeScript type name.
63
66
  *
@@ -66,7 +69,7 @@ export declare function resolvePropertyType(prop: JsonSchemaProperty | undefined
66
69
  * @param resolveDefName - Callback to produce a TS name for object definitions.
67
70
  * Receives (defName: string). When null, object defs resolve to "any".
68
71
  */
69
- export declare function resolveRef(ref: string, schema: JsonSchemaDocument, resolveDefName: ((defName: string) => string) | null): string;
72
+ export declare function resolveRef(ref: string, schema: JsonSchemaDocument, resolveDefName: ((defName: string) => string) | null, seen?: ReadonlySet<string>): string;
70
73
  /**
71
74
  * Extract property constraints from a schema property.
72
75
  */
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema.d.ts","sourceRoot":"","sources":["../../src/codegen/json-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACnD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAChD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC9D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAID,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAID;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,CAOvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,kBAAkB,GAAG,SAAS,EACpC,MAAM,EAAE,kBAAkB,EAC1B,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,IAAI,GACnD,MAAM,CAwCR;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,kBAAkB,EAC1B,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,IAAI,GACnD,MAAM,CA+BR;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,mBAAmB,CAYhF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAYlE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAEnE"}
1
+ {"version":3,"file":"json-schema.d.ts","sourceRoot":"","sources":["../../src/codegen/json-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACnD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAChD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC9D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAID,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAOD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,CAOvE;AA8DD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,kBAAkB,GAAG,SAAS,EACpC,MAAM,EAAE,kBAAkB,EAC1B,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,IAAI,EACpD,IAAI,GAAE,WAAW,CAAC,MAAM,CAAc,GACrC,MAAM,CAmDR;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,kBAAkB,EAC1B,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,IAAI,EACpD,IAAI,GAAE,WAAW,CAAC,MAAM,CAAc,GACrC,MAAM,CAkDR;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,mBAAmB,CAYhF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAYlE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAEnE"}
@@ -6,7 +6,8 @@ import type { UnobservedEntity } from "./observation.js";
6
6
  * resolved infrastructure graph. Painters (mermaid, graphviz, custom SVG) and
7
7
  * the agentic diagrammer consume this; it is a pure function of lint-clean
8
8
  * source. Every node traces to the file that declared it; every edge is a real
9
- * cross-resource reference (AttrRef).
9
+ * cross-resource reference, either an `AttrRef` this module resolves itself or
10
+ * an {@link EntityReference} the producing lexicon resolved (#2265).
10
11
  *
11
12
  * Emitted by `chant graph --format ir`. See issue #493 / epic #492.
12
13
  */
@@ -78,6 +79,69 @@ export interface IREdge {
78
79
  /** The producer-side attribute referenced (e.g. "id"). Added at detail T3. */
79
80
  toAttr?: string;
80
81
  }
82
+ /**
83
+ * One reference an entity declares in a vocabulary this module cannot read
84
+ * (#2265), already resolved to the entity key it points at.
85
+ *
86
+ * `collectEdges` below finds references two ways: an {@link AttrRef} object a
87
+ * typed lexicon puts in its props, and a `Ref`-shaped intrinsic. Both are
88
+ * objects with a resolvable target, which is what makes them findable by a
89
+ * walk that knows no lexicon. A lexicon that PARSES someone else's source has
90
+ * neither: the terraform lexicon's entities keep their block body verbatim
91
+ * from hcl2json, where a reference to another block is the string
92
+ * `"${aws_vpc.main.id}"` and nothing else. The walk finds nothing, correctly,
93
+ * and a 247-node estate graphs with zero edges.
94
+ *
95
+ * The reference is not missing, it is just in the producer's vocabulary, and
96
+ * resolving it needs an HCL expression parser and the lexicon's own key shape.
97
+ * So the lexicon resolves it and says so here, on the entity, in the one
98
+ * vocabulary this module does understand: an entity key, plus the two
99
+ * attribute names {@link IREdge} already carries.
100
+ *
101
+ * The hook is a plain duck-typed optional field any entity MAY carry beside
102
+ * its `props`, read through {@link entityReferences}. That is the same shape
103
+ * (and the same reasoning) as `suppressions`, ../lint/suppressions.ts: a
104
+ * reference is a fact ABOUT one entity, `Declarable` already lets each lexicon
105
+ * carry its own fields beside `props`, and nothing that constructs or merges
106
+ * an entity map had to change for it. A lexicon that wants edges adopts the
107
+ * convention on its own entities; every other lexicon keeps the AttrRef walk
108
+ * it already had, and the two sources of edges merge in one place.
109
+ *
110
+ * An entry whose `to` is not a node in this graph is dropped rather than kept
111
+ * as a dangling endpoint, the same rule {@link buildLiveGraphIr} applies to an
112
+ * observed edge.
113
+ */
114
+ export interface EntityReference {
115
+ /** The referenced entity's key, in the same space as the IR's node ids. */
116
+ to: string;
117
+ /** The consumer-side attribute the reference flows through (`IREdge.viaAttr`). */
118
+ viaAttr?: string;
119
+ /** The producer-side attribute referenced (`IREdge.toAttr`), when the reference named exactly one. */
120
+ toAttr?: string;
121
+ }
122
+ /**
123
+ * The references an entity declared for itself (#2265), or an empty list.
124
+ * Duck-typed on purpose, see {@link EntityReference}.
125
+ */
126
+ export declare function entityReferences(entity: Declarable): readonly EntityReference[];
127
+ /**
128
+ * The deployable unit an entity says it belongs to (#2266), or undefined.
129
+ *
130
+ * Same duck-typed channel as {@link entityReferences}, for the other half of
131
+ * the same problem. `groups.byStack` keys by lexicon partition when nothing
132
+ * says otherwise, on the reasoning that one lexicon serialises to one
133
+ * deployable stack. That reasoning is exactly wrong for a lexicon reading an
134
+ * estate that already has several: a terraform project declaring five
135
+ * `terraform.roots` has five things `terraform apply` runs against, and each
136
+ * one is a stack in every sense `byStack` means, so five roots landed in one
137
+ * bucket named `terraform`.
138
+ *
139
+ * The root is not inferable here and should not be: it is the producer's own
140
+ * name for its own unit. A consumer could recover it from the node id prefix
141
+ * the terraform lexicon mints, but an id convention is not a promise, and
142
+ * `byStack` is the field whose whole job is saying which box a node goes in.
143
+ */
144
+ export declare function entityStack(entity: Declarable): string | undefined;
81
145
  /** Grouping metadata for cluster/subgraph rendering. Maps group name -> node ids. */
82
146
  export interface IRGroups {
83
147
  byLexicon?: Record<string, string[]>;
@@ -87,11 +151,15 @@ export interface IRGroups {
87
151
  * boundary boxes) read this rather than inferring stacks — which is the point,
88
152
  * so it should never require one.
89
153
  *
90
- * Two sources, depending on how the project is shaped:
154
+ * Three sources, depending on how the project is shaped:
91
155
  *
92
156
  * - **side-by-side stacks**, declared in config and composed by
93
157
  * `buildDeclaredPerStack` — keys are the declared stack names. Nothing is
94
158
  * inferred; the project stated both the names and the membership.
159
+ * - **entities that name their own unit** ({@link entityStack}, #2266) —
160
+ * keys are those names, one entry per unit. A terraform root is the case
161
+ * this exists for: five `terraform.roots` are five `terraform apply`s and
162
+ * five boundary boxes, not one bucket called `terraform`.
95
163
  * - **one source tree** — keys are lexicon partitions, since each lexicon
96
164
  * serialises to one deployable stack.
97
165
  *
@@ -1 +1 @@
1
- {"version":3,"file":"graph-ir.d.ts","sourceRoot":"","sources":["../src/graph-ir.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graph-ir.d.ts","sourceRoot":"","sources":["../src/graph-ir.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"assert-live.d.ts","sourceRoot":"","sources":["../../src/lifecycle/assert-live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAA6D,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClH,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;aAE/B,MAAM,EAAE,MAAM;aACd,MAAM,EAAE,gBAAgB;aACxB,MAAM,CAAC,EAAE,MAAM;gBAFf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,gBAAgB,EACxB,MAAM,CAAC,EAAE,MAAM,YAAA;CAQlC;AAED,MAAM,WAAW,iBAAiB;IAChC,+FAA+F;IAC/F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,MAAM,EAAE,kBAAkB,CAAC;IAC3B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,MAAM,EAAE,eAAe,CAAC;CACzB;AAQD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwD/F"}
1
+ {"version":3,"file":"assert-live.d.ts","sourceRoot":"","sources":["../../src/lifecycle/assert-live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAA6D,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClH,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;aAE/B,MAAM,EAAE,MAAM;aACd,MAAM,EAAE,gBAAgB;aACxB,MAAM,CAAC,EAAE,MAAM;gBAFf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,gBAAgB,EACxB,MAAM,CAAC,EAAE,MAAM,YAAA;CAQlC;AAED,MAAM,WAAW,iBAAiB;IAChC,+FAA+F;IAC/F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,MAAM,EAAE,kBAAkB,CAAC;IAC3B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,MAAM,EAAE,eAAe,CAAC;CACzB;AAQD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyD/F"}
@@ -1 +1 @@
1
- {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/lifecycle/observe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAI5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAYnD,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAsBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,kBAAkB,EAAE,EAC7B,WAAW,EAAE,WAAW,EACxB,IAAI,CAAC,EAAE;IACL,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzE;;8CAE0C;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;0CAEsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,OAAO,CAAC,aAAa,CAAC,CA6LxB;AAyBD;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE;IACJ,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC3C,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,GACA,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CA2B3C;AAED,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC5C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE;IACJ,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAC9E,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC3C,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChE,GACA,OAAO,CAAC,qBAAqB,CAAC,CA0ChC"}
1
+ {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/lifecycle/observe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAI5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAYnD,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAwBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,kBAAkB,EAAE,EAC7B,WAAW,EAAE,WAAW,EACxB,IAAI,CAAC,EAAE;IACL,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzE;;8CAE0C;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;0CAEsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,OAAO,CAAC,aAAa,CAAC,CA8LxB;AAyBD;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE;IACJ,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC3C,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,GACA,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CA2B3C;AAED,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC5C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE;IACJ,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAC9E,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC3C,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChE,GACA,OAAO,CAAC,qBAAqB,CAAC,CA0ChC"}
@@ -93,6 +93,24 @@ export interface ObservationResult {
93
93
  * namespace, endpoint or region was read, not the one the resource lives in.
94
94
  */
95
95
  queried?: Record<string, string>;
96
+ /**
97
+ * Which read answered for each declared entity (#2267), keyed by chant
98
+ * entity name. The value is a short token the lexicon defines and documents;
99
+ * core carries it and never interprets it, so nothing here switches on one.
100
+ *
101
+ * Additive metadata over the tri-state, exactly like {@link queried}, and
102
+ * present for every verdict including OBSERVED-ABSENT and NOT-OBSERVED,
103
+ * since which read was attempted is a fact whatever the read came back with.
104
+ *
105
+ * A lexicon with one read path has nothing to say here and omits it. This is
106
+ * for a lexicon with two, where the two answer different questions: a stock
107
+ * Terraform root is read with `terraform show -json` over its state, which
108
+ * says what the last apply recorded, and a live root is read with
109
+ * `choudoufu live-plan -json`, which says what the account holds. A renderer
110
+ * that paints an overlay as drift must not paint the first one green, and
111
+ * before this field an observation gave it no way to tell them apart.
112
+ */
113
+ sources?: Record<string, string>;
96
114
  /**
97
115
  * Notices about the read as a whole, not about any one entity (#1265) —
98
116
  * "the ownership filter could not be applied on this surface" is the
@@ -123,6 +141,8 @@ export interface NormalizedObservation {
123
141
  unobserved: Record<string, UnobservedEntity>;
124
142
  /** Resolved query address per entity name (#1620). Empty when the lexicon reported none. */
125
143
  queried: Record<string, string>;
144
+ /** Which read answered, per entity name (#2267). Empty when the lexicon reported none. */
145
+ sources: Record<string, string>;
126
146
  /** Run-level notices (#1265), distinct. Empty when the lexicon reported none. */
127
147
  notes: string[];
128
148
  /** Per-stack exports (#1279), keyed by stack name. Absent when the lexicon reported none. */
@@ -134,7 +154,7 @@ export declare function isObservationResult(value: unknown): value is Observatio
134
154
  * Build an {@link ObservationResult}. Lexicons use this rather than writing the
135
155
  * discriminant by hand.
136
156
  */
137
- export declare function observation(resources: Record<string, ResourceMetadata>, unobserved?: Record<string, UnobservedEntity>, queried?: Record<string, string>, notes?: string[], stackExports?: Record<string, Record<string, unknown>>): ObservationResult;
157
+ export declare function observation(resources: Record<string, ResourceMetadata>, unobserved?: Record<string, UnobservedEntity>, queried?: Record<string, string>, notes?: string[], stackExports?: Record<string, Record<string, unknown>>, sources?: Record<string, string>): ObservationResult;
138
158
  /**
139
159
  * Normalize either accepted return shape. `undefined` (a lexicon that returned
140
160
  * nothing at all) normalizes to two empty maps — which reads as "everything was
@@ -1 +1 @@
1
- {"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../src/observation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../src/observation.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.60.0",
3
+ "version": "0.61.0",
4
4
  "description": "Declarative infrastructure-as-code toolkit — TypeScript on Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -591,7 +591,7 @@ export async function runComponentsStatus(ctx: CommandContext): Promise<number>
591
591
  // rather than "stale" (recorded, and nothing live).
592
592
  const message = err instanceof Error ? err.message : String(err);
593
593
  console.error(formatWarning({ message: `${plugin.name}: describeResources failed — ${message} (components in this lexicon report unknown, not stale)` }));
594
- observed = { resources: {}, unobserved: unobservedAll(declared, "read-failed", message, entities), queried: {}, notes: [] };
594
+ observed = { resources: {}, unobserved: unobservedAll(declared, "read-failed", message, entities), queried: {}, sources: {}, notes: [] };
595
595
  }
596
596
  const cs = buildChangeSet(environment, {
597
597
  declared,
@@ -782,6 +782,7 @@ async function observeLexicon(
782
782
  resources: {},
783
783
  unobserved: unobservedAll(entityNames, "read-failed", message, opts.entities),
784
784
  queried: {},
785
+ sources: {},
785
786
  notes: [],
786
787
  };
787
788
  }
@@ -110,6 +110,91 @@ describe("resolvePropertyType", () => {
110
110
  };
111
111
  expect(resolvePropertyType({ $ref: "#/definitions/Foo" }, schema, null)).toBe("any");
112
112
  });
113
+
114
+ // chant #2205 — a branch list beside a real `type` relaxes that type.
115
+ test("reads the sibling type through a oneOf", () => {
116
+ const prop: JsonSchemaProperty = {
117
+ type: "string",
118
+ oneOf: [{ pattern: "^a" }, { pattern: "^b" }],
119
+ };
120
+ expect(resolvePropertyType(prop, emptySchema, defName)).toBe("string");
121
+ });
122
+
123
+ test("reads the sibling $ref through an anyOf", () => {
124
+ const schema: JsonSchemaDocument = {
125
+ definitions: { Foo: { properties: { bar: { type: "string" } } } },
126
+ };
127
+ const prop: JsonSchemaProperty = {
128
+ $ref: "#/definitions/Foo",
129
+ anyOf: [{ required: ["bar"] }],
130
+ };
131
+ expect(resolvePropertyType(prop, schema, defName)).toBe("Test_Foo");
132
+ });
133
+
134
+ test("an anyOf branch carrying an enum narrows a string property to that union", () => {
135
+ // AWS::AmazonMQ::Broker.EngineType: the enum branch beside case-insensitive
136
+ // patterns for the same two values.
137
+ const prop: JsonSchemaProperty = {
138
+ type: "string",
139
+ anyOf: [
140
+ { type: "string", enum: ["ACTIVEMQ", "RABBITMQ"] },
141
+ { pattern: "^[Aa][Cc][Tt][Ii][Vv][Ee][Mm][Qq]$" },
142
+ { pattern: "^[Rr][Aa][Bb][Bb][Ii][Tt][Mm][Qq]$" },
143
+ ],
144
+ };
145
+ expect(resolvePropertyType(prop, emptySchema, defName)).toBe('"ACTIVEMQ" | "RABBITMQ"');
146
+ });
147
+
148
+ test("a non-string enum branch is left alone", () => {
149
+ const prop: JsonSchemaProperty = {
150
+ type: "object",
151
+ anyOf: [{ enum: ["a", "b"] }],
152
+ };
153
+ expect(resolvePropertyType(prop, emptySchema, defName)).toBe("Record<string, any>");
154
+ });
155
+
156
+ test("a branch list with nothing beside it stays 'any'", () => {
157
+ const prop: JsonSchemaProperty = {
158
+ oneOf: [
159
+ { type: "object", properties: { Fixed: { type: "string" } } },
160
+ { type: "object", properties: { Below: { type: "string" } } },
161
+ ],
162
+ };
163
+ expect(resolvePropertyType(prop, emptySchema, defName)).toBe("any");
164
+ });
165
+
166
+ // chant #2205 — allOf of one $ref and an annotation types as that $ref.
167
+ test("resolves an allOf of a single $ref plus an annotation", () => {
168
+ const schema: JsonSchemaDocument = {
169
+ definitions: { ProtocolType: { type: "string", enum: ["MCP"] } },
170
+ };
171
+ const prop: JsonSchemaProperty = {
172
+ allOf: [{ $ref: "#/definitions/ProtocolType" }, { default: "MCP" }],
173
+ };
174
+ expect(resolvePropertyType(prop, schema, defName)).toBe("Test_ProtocolType");
175
+ });
176
+
177
+ test("leaves an allOf that intersects two real shapes as 'any'", () => {
178
+ const schema: JsonSchemaDocument = {
179
+ definitions: {
180
+ A: { properties: { a: { type: "string" } } },
181
+ B: { properties: { b: { type: "string" } } },
182
+ },
183
+ };
184
+ const prop: JsonSchemaProperty = {
185
+ allOf: [{ $ref: "#/definitions/A" }, { $ref: "#/definitions/B" }],
186
+ };
187
+ expect(resolvePropertyType(prop, schema, defName)).toBe("any");
188
+ });
189
+
190
+ // chant #2205 — `[]` binds tighter than `|`.
191
+ test("parenthesizes a union inside an array", () => {
192
+ const prop: JsonSchemaProperty = {
193
+ type: "array",
194
+ items: { type: "string", enum: ["arm64", "x86_64"] },
195
+ };
196
+ expect(resolvePropertyType(prop, emptySchema, defName)).toBe('("arm64" | "x86_64")[]');
197
+ });
113
198
  });
114
199
 
115
200
  describe("resolveRef", () => {
@@ -129,6 +214,80 @@ describe("resolveRef", () => {
129
214
  };
130
215
  expect(resolveRef("#/definitions/Count", schema, defName)).toBe("number");
131
216
  });
217
+
218
+ // chant #2205 — CloudFormation names its list shapes, and a `$ref` to one
219
+ // used to fall through to "any".
220
+ test("resolves an array definition through its items", () => {
221
+ const schema: JsonSchemaDocument = {
222
+ definitions: {
223
+ TagList: { type: "array", items: { $ref: "#/definitions/Tag" } },
224
+ Tag: { properties: { Key: { type: "string" }, Value: { type: "string" } } },
225
+ },
226
+ };
227
+ expect(resolveRef("#/definitions/TagList", schema, defName)).toBe("Test_Tag[]");
228
+ });
229
+
230
+ test("resolves an array definition of scalars", () => {
231
+ const schema: JsonSchemaDocument = {
232
+ definitions: { Names: { type: "array", items: { type: "string" } } },
233
+ };
234
+ expect(resolveRef("#/definitions/Names", schema, defName)).toBe("string[]");
235
+ });
236
+
237
+ test("resolves an array definition with no items to 'any[]'", () => {
238
+ const schema: JsonSchemaDocument = {
239
+ definitions: { Loose: { type: "array" } },
240
+ };
241
+ expect(resolveRef("#/definitions/Loose", schema, defName)).toBe("any[]");
242
+ });
243
+
244
+ test("parenthesizes a union inside an array definition", () => {
245
+ const schema: JsonSchemaDocument = {
246
+ definitions: { Modes: { type: "array", items: { type: "string", enum: ["b", "a"] } } },
247
+ };
248
+ expect(resolveRef("#/definitions/Modes", schema, defName)).toBe('("a" | "b")[]');
249
+ });
250
+
251
+ test("a list definition that reaches itself terminates", () => {
252
+ const schema: JsonSchemaDocument = {
253
+ definitions: { Tree: { type: "array", items: { $ref: "#/definitions/Tree" } } },
254
+ };
255
+ expect(resolveRef("#/definitions/Tree", schema, defName)).toBe("any[]");
256
+ });
257
+
258
+ test("a definition that is an allOf of one $ref resolves as that $ref", () => {
259
+ const schema: JsonSchemaDocument = {
260
+ definitions: {
261
+ Wrapped: { allOf: [{ $ref: "#/definitions/Inner" }, { default: "x" }] },
262
+ Inner: { properties: { a: { type: "string" } } },
263
+ },
264
+ };
265
+ expect(resolveRef("#/definitions/Wrapped", schema, defName)).toBe("Test_Inner");
266
+ });
267
+
268
+ test("a definition that is a sum of object branches stays 'any'", () => {
269
+ const schema: JsonSchemaDocument = {
270
+ definitions: {
271
+ FieldPosition: {
272
+ oneOf: [
273
+ { type: "object", properties: { Fixed: { type: "string" } } },
274
+ { type: "object", properties: { Below: { type: "string" } } },
275
+ ],
276
+ },
277
+ },
278
+ };
279
+ expect(resolveRef("#/definitions/FieldPosition", schema, defName)).toBe("any");
280
+ });
281
+
282
+ test("a nested list definition resolves through both hops", () => {
283
+ const schema: JsonSchemaDocument = {
284
+ definitions: {
285
+ Matrix: { type: "array", items: { $ref: "#/definitions/Row" } },
286
+ Row: { type: "array", items: { type: "number" } },
287
+ },
288
+ };
289
+ expect(resolveRef("#/definitions/Matrix", schema, defName)).toBe("number[][]");
290
+ });
132
291
  });
133
292
 
134
293
  describe("extractConstraints", () => {
@@ -20,6 +20,7 @@ export interface JsonSchemaProperty {
20
20
  items?: JsonSchemaProperty;
21
21
  oneOf?: JsonSchemaProperty[];
22
22
  anyOf?: JsonSchemaProperty[];
23
+ allOf?: JsonSchemaProperty[];
23
24
  properties?: Record<string, JsonSchemaProperty>;
24
25
  required?: string[];
25
26
  enum?: string[];
@@ -54,6 +55,9 @@ export interface PropertyConstraints {
54
55
 
55
56
  // --- Functions ---
56
57
 
58
+ /** Shared empty cycle guard, so the common call allocates nothing. */
59
+ const EMPTY_SEEN: ReadonlySet<string> = new Set<string>();
60
+
57
61
  /**
58
62
  * Get the primary type from a type field that can be string or string[].
59
63
  * Returns first non-"null" type, or "any" if empty.
@@ -67,6 +71,66 @@ export function primaryType(type: string | string[] | undefined): string {
67
71
  return type.length > 0 ? type[0] : "any";
68
72
  }
69
73
 
74
+ /** The branch list a property carries under `oneOf` or `anyOf`, or undefined. */
75
+ function branchesOf(prop: JsonSchemaProperty): JsonSchemaProperty[] | undefined {
76
+ if (prop.oneOf && prop.oneOf.length > 0) return prop.oneOf;
77
+ if (prop.anyOf && prop.anyOf.length > 0) return prop.anyOf;
78
+ return undefined;
79
+ }
80
+
81
+ /** A union of string literals, sorted, from a list of enum values. */
82
+ function enumUnion(values: string[]): string {
83
+ return [...values].sort().map((v) => JSON.stringify(v)).join(" | ");
84
+ }
85
+
86
+ /**
87
+ * `T[]`, parenthesized when `T` is a union.
88
+ *
89
+ * `[]` binds tighter than `|`, so an unparenthesized `"a" | "b"[]` reads as
90
+ * `"a" | ("b"[])`: it accepts the bare string `"a"` and rejects `["a"]`.
91
+ */
92
+ function arrayOf(itemType: string): string {
93
+ return itemType.includes(" | ") ? `(${itemType})[]` : `${itemType}[]`;
94
+ }
95
+
96
+ /**
97
+ * The branch of a `oneOf`/`anyOf` that narrows a string property to an enum.
98
+ *
99
+ * CloudFormation writes several string properties as a `type: "string"` beside
100
+ * a branch list whose first branch is the real enum and whose other branches
101
+ * are case-insensitive `pattern`s for the same values (`AWS::AmazonMQ::Broker`'s
102
+ * `EngineType`). The branch list relaxes the enum rather than summing shapes, so
103
+ * the enum is the useful type. Only strings qualify: anything else is a real sum.
104
+ */
105
+ function relaxedEnumBranch(
106
+ prop: JsonSchemaProperty,
107
+ branches: JsonSchemaProperty[],
108
+ ): string[] | undefined {
109
+ if (prop.type !== undefined && primaryType(prop.type) !== "string") return undefined;
110
+ for (const b of branches) {
111
+ if (!b.enum || b.enum.length === 0) continue;
112
+ if (b.type !== undefined && primaryType(b.type) !== "string") return undefined;
113
+ if (!b.enum.every((v) => typeof v === "string")) return undefined;
114
+ return b.enum;
115
+ }
116
+ return undefined;
117
+ }
118
+
119
+ /**
120
+ * The single content-bearing branch of an `allOf`, when that is the whole shape.
121
+ *
122
+ * Every `allOf` in the CloudFormation Registry is one `$ref` beside an
123
+ * annotation object (`{ "default": "MCP" }`), which types exactly as the `$ref`
124
+ * alone. An `allOf` that intersects two real shapes has no single answer and is
125
+ * left to the caller.
126
+ */
127
+ function soleTypedBranch(branches: JsonSchemaProperty[]): JsonSchemaProperty | undefined {
128
+ const typed = branches.filter(
129
+ (b) => b.$ref || b.type || b.properties || b.items || (b.enum && b.enum.length > 0) || branchesOf(b),
130
+ );
131
+ return typed.length === 1 ? typed[0] : undefined;
132
+ }
133
+
70
134
  /**
71
135
  * Resolve a schema property to its TypeScript type string.
72
136
  *
@@ -75,28 +139,43 @@ export function primaryType(type: string | string[] | undefined): string {
75
139
  * @param resolveDefName - Callback to produce a TypeScript name from a definition.
76
140
  * Receives (defName: string) and should return the TS type name for that definition.
77
141
  * When null, $ref to object definitions resolves to "any".
142
+ * @param seen - Definition names already on the current resolution path, so a
143
+ * list definition that reaches itself terminates instead of recursing forever.
78
144
  */
79
145
  export function resolvePropertyType(
80
146
  prop: JsonSchemaProperty | undefined,
81
147
  schema: JsonSchemaDocument,
82
148
  resolveDefName: ((defName: string) => string) | null,
149
+ seen: ReadonlySet<string> = EMPTY_SEEN,
83
150
  ): string {
84
151
  if (!prop) return "any";
85
152
 
86
- // Handle oneOf/anyOf → any
87
- if ((prop.oneOf && prop.oneOf.length > 0) || (prop.anyOf && prop.anyOf.length > 0)) {
88
- return "any";
153
+ // `allOf` of one real branch and some annotations types as that branch.
154
+ if (prop.allOf && prop.allOf.length > 0 && !prop.$ref && !prop.type && !prop.enum) {
155
+ const sole = soleTypedBranch(prop.allOf);
156
+ if (sole) return resolvePropertyType(sole, schema, resolveDefName, seen);
157
+ }
158
+
159
+ const branches = branchesOf(prop);
160
+ if (branches) {
161
+ // A branch list beside a `type` relaxes that type; the enum branch, when
162
+ // there is one, is the narrowest reading of it.
163
+ const relaxed = relaxedEnumBranch(prop, branches);
164
+ if (relaxed) return enumUnion(relaxed);
165
+ // With nothing beside it the branch list is a sum of shapes, which this
166
+ // emitter does not express yet (chant #2278).
167
+ if (!prop.$ref && !prop.type && !(prop.enum && prop.enum.length > 0)) return "any";
168
+ // Otherwise fall through and read the sibling keywords.
89
169
  }
90
170
 
91
171
  // Handle $ref
92
172
  if (prop.$ref) {
93
- return resolveRef(prop.$ref, schema, resolveDefName);
173
+ return resolveRef(prop.$ref, schema, resolveDefName, seen);
94
174
  }
95
175
 
96
176
  // Inline enum → union of string literals
97
177
  if (prop.enum && prop.enum.length > 0) {
98
- const sorted = [...prop.enum].sort();
99
- return sorted.map((v) => JSON.stringify(v)).join(" | ");
178
+ return enumUnion(prop.enum);
100
179
  }
101
180
 
102
181
  const pt = primaryType(prop.type);
@@ -111,8 +190,7 @@ export function resolvePropertyType(
111
190
  return "boolean";
112
191
  case "array":
113
192
  if (prop.items) {
114
- const itemType = resolvePropertyType(prop.items, schema, resolveDefName);
115
- return `${itemType}[]`;
193
+ return arrayOf(resolvePropertyType(prop.items, schema, resolveDefName, seen));
116
194
  }
117
195
  return "any[]";
118
196
  case "object":
@@ -134,6 +212,7 @@ export function resolveRef(
134
212
  ref: string,
135
213
  schema: JsonSchemaDocument,
136
214
  resolveDefName: ((defName: string) => string) | null,
215
+ seen: ReadonlySet<string> = EMPTY_SEEN,
137
216
  ): string {
138
217
  const prefix = "#/definitions/";
139
218
  if (!ref.startsWith(prefix)) return "any";
@@ -141,6 +220,8 @@ export function resolveRef(
141
220
  const defName = ref.slice(prefix.length);
142
221
  const def = schema.definitions?.[defName];
143
222
  if (!def) return "any";
223
+ // A list definition whose items reach it again would recurse without end.
224
+ if (seen.has(defName)) return "any";
144
225
 
145
226
  // String enum → named type (via resolveDefName) or string
146
227
  if (isEnumDefinition(def)) {
@@ -161,9 +242,26 @@ export function resolveRef(
161
242
  case "number": return "number";
162
243
  case "boolean": return "boolean";
163
244
  case "object": return "Record<string, any>";
245
+ // CloudFormation names its list shapes: `Tags` is a `$ref` to a `TagList`
246
+ // definition whose items are the `Tag` definition one hop away. Resolve
247
+ // through `items` the way the inline `array` case does. (chant #2205)
248
+ case "array": {
249
+ if (!def.items) return "any[]";
250
+ const nested = new Set(seen);
251
+ nested.add(defName);
252
+ return arrayOf(resolvePropertyType(def.items, schema, resolveDefName, nested));
253
+ }
164
254
  }
165
255
  }
166
256
 
257
+ // An `allOf`/`oneOf`/`anyOf` definition types as its property form would.
258
+ if (def.allOf || def.oneOf || def.anyOf) {
259
+ const nested = new Set(seen);
260
+ nested.add(defName);
261
+ const viaBranches = resolvePropertyType(def, schema, resolveDefName, nested);
262
+ if (viaBranches !== "any") return viaBranches;
263
+ }
264
+
167
265
  return "any";
168
266
  }
169
267
 
@@ -224,3 +224,66 @@ describe("buildGraphIr", () => {
224
224
  expect(JSON.stringify(buildGraphIr(reordered))).toEqual(JSON.stringify(ir));
225
225
  });
226
226
  });
227
+
228
+ /**
229
+ * The two duck-typed channels an entity may carry beside its `props` (#2265,
230
+ * #2266), for a lexicon whose references are strings in someone else's syntax
231
+ * rather than `AttrRef` objects. Asserted here with a made-up lexicon rather
232
+ * than terraform's, because what is being checked is that this module reads
233
+ * the channels without knowing anything about who filled them.
234
+ */
235
+ describe("entity-declared references and stacks", () => {
236
+ test("turns a declared reference into an edge, with both attribute names", () => {
237
+ const vpc = decl({ lexicon: "hcl", entityType: "Vpc" });
238
+ const subnet = decl({
239
+ lexicon: "hcl",
240
+ entityType: "Subnet",
241
+ props: { vpc_id: "${aws_vpc.main.id}" },
242
+ references: [{ to: "vpc", viaAttr: "vpc_id", toAttr: "id" }],
243
+ });
244
+ const entities = new Map<string, Declarable>([
245
+ ["vpc", vpc],
246
+ ["subnet", subnet],
247
+ ]);
248
+ resolveAttrRefs(entities);
249
+
250
+ const ir = buildGraphIr(entities);
251
+ expect(ir.edges).toEqual([{ from: "subnet", to: "vpc", kind: "ref", viaAttr: "vpc_id", toAttr: "id" }]);
252
+ // The reference channel is metadata about the graph, not declared config,
253
+ // so it never lands in attrs; the string it was resolved from still does.
254
+ expect(ir.nodes.find((n) => n.id === "subnet")!.attrs).toEqual({ vpc_id: "${aws_vpc.main.id}" });
255
+ });
256
+
257
+ test("drops a declared reference to a self or to something that is not a node", () => {
258
+ const a = decl({
259
+ lexicon: "hcl",
260
+ entityType: "Vpc",
261
+ references: [
262
+ { to: "a", viaAttr: "self" },
263
+ { to: "nowhere", viaAttr: "gone" },
264
+ ],
265
+ });
266
+ const entities = new Map<string, Declarable>([["a", a]]);
267
+ resolveAttrRefs(entities);
268
+ expect(buildGraphIr(entities).edges).toEqual([]);
269
+ });
270
+
271
+ test("keys byStack by the unit an entity names, and leaves byLexicon alone", () => {
272
+ const app = decl({ lexicon: "hcl", entityType: "Vpc", stack: "app" });
273
+ const net = decl({ lexicon: "hcl", entityType: "Vpc", stack: "network" });
274
+ // An entity that names no unit still falls back to the lexicon partition,
275
+ // which is every other lexicon's behaviour and stays unchanged.
276
+ const plain = decl({ lexicon: "gcp", entityType: "Vpc" });
277
+ const entities = new Map<string, Declarable>([
278
+ ["app/vpc", app],
279
+ ["network/vpc", net],
280
+ ["plain", plain],
281
+ ]);
282
+ resolveAttrRefs(entities);
283
+
284
+ const ir = buildGraphIr(entities);
285
+ expect(ir.groups.byStack).toEqual({ app: ["app/vpc"], gcp: ["plain"], network: ["network/vpc"] });
286
+ expect(ir.groups.byLexicon).toEqual({ gcp: ["plain"], hcl: ["app/vpc", "network/vpc"] });
287
+ expect(ir.nodes.find((n) => n.id === "app/vpc")!.attrs).not.toHaveProperty("stack");
288
+ });
289
+ });
package/src/graph-ir.ts CHANGED
@@ -13,7 +13,8 @@ import type { UnobservedEntity } from "./observation";
13
13
  * resolved infrastructure graph. Painters (mermaid, graphviz, custom SVG) and
14
14
  * the agentic diagrammer consume this; it is a pure function of lint-clean
15
15
  * source. Every node traces to the file that declared it; every edge is a real
16
- * cross-resource reference (AttrRef).
16
+ * cross-resource reference, either an `AttrRef` this module resolves itself or
17
+ * an {@link EntityReference} the producing lexicon resolved (#2265).
17
18
  *
18
19
  * Emitted by `chant graph --format ir`. See issue #493 / epic #492.
19
20
  */
@@ -90,6 +91,88 @@ export interface IREdge {
90
91
  toAttr?: string;
91
92
  }
92
93
 
94
+ /**
95
+ * One reference an entity declares in a vocabulary this module cannot read
96
+ * (#2265), already resolved to the entity key it points at.
97
+ *
98
+ * `collectEdges` below finds references two ways: an {@link AttrRef} object a
99
+ * typed lexicon puts in its props, and a `Ref`-shaped intrinsic. Both are
100
+ * objects with a resolvable target, which is what makes them findable by a
101
+ * walk that knows no lexicon. A lexicon that PARSES someone else's source has
102
+ * neither: the terraform lexicon's entities keep their block body verbatim
103
+ * from hcl2json, where a reference to another block is the string
104
+ * `"${aws_vpc.main.id}"` and nothing else. The walk finds nothing, correctly,
105
+ * and a 247-node estate graphs with zero edges.
106
+ *
107
+ * The reference is not missing, it is just in the producer's vocabulary, and
108
+ * resolving it needs an HCL expression parser and the lexicon's own key shape.
109
+ * So the lexicon resolves it and says so here, on the entity, in the one
110
+ * vocabulary this module does understand: an entity key, plus the two
111
+ * attribute names {@link IREdge} already carries.
112
+ *
113
+ * The hook is a plain duck-typed optional field any entity MAY carry beside
114
+ * its `props`, read through {@link entityReferences}. That is the same shape
115
+ * (and the same reasoning) as `suppressions`, ../lint/suppressions.ts: a
116
+ * reference is a fact ABOUT one entity, `Declarable` already lets each lexicon
117
+ * carry its own fields beside `props`, and nothing that constructs or merges
118
+ * an entity map had to change for it. A lexicon that wants edges adopts the
119
+ * convention on its own entities; every other lexicon keeps the AttrRef walk
120
+ * it already had, and the two sources of edges merge in one place.
121
+ *
122
+ * An entry whose `to` is not a node in this graph is dropped rather than kept
123
+ * as a dangling endpoint, the same rule {@link buildLiveGraphIr} applies to an
124
+ * observed edge.
125
+ */
126
+ export interface EntityReference {
127
+ /** The referenced entity's key, in the same space as the IR's node ids. */
128
+ to: string;
129
+ /** The consumer-side attribute the reference flows through (`IREdge.viaAttr`). */
130
+ viaAttr?: string;
131
+ /** The producer-side attribute referenced (`IREdge.toAttr`), when the reference named exactly one. */
132
+ toAttr?: string;
133
+ }
134
+
135
+ /** True when `value` has the {@link EntityReference} shape. */
136
+ function isEntityReference(value: unknown): value is EntityReference {
137
+ return (
138
+ typeof value === "object" &&
139
+ value !== null &&
140
+ typeof (value as { to?: unknown }).to === "string"
141
+ );
142
+ }
143
+
144
+ /**
145
+ * The references an entity declared for itself (#2265), or an empty list.
146
+ * Duck-typed on purpose, see {@link EntityReference}.
147
+ */
148
+ export function entityReferences(entity: Declarable): readonly EntityReference[] {
149
+ const declared = (entity as unknown as { references?: unknown }).references;
150
+ if (!Array.isArray(declared)) return [];
151
+ return declared.filter(isEntityReference);
152
+ }
153
+
154
+ /**
155
+ * The deployable unit an entity says it belongs to (#2266), or undefined.
156
+ *
157
+ * Same duck-typed channel as {@link entityReferences}, for the other half of
158
+ * the same problem. `groups.byStack` keys by lexicon partition when nothing
159
+ * says otherwise, on the reasoning that one lexicon serialises to one
160
+ * deployable stack. That reasoning is exactly wrong for a lexicon reading an
161
+ * estate that already has several: a terraform project declaring five
162
+ * `terraform.roots` has five things `terraform apply` runs against, and each
163
+ * one is a stack in every sense `byStack` means, so five roots landed in one
164
+ * bucket named `terraform`.
165
+ *
166
+ * The root is not inferable here and should not be: it is the producer's own
167
+ * name for its own unit. A consumer could recover it from the node id prefix
168
+ * the terraform lexicon mints, but an id convention is not a promise, and
169
+ * `byStack` is the field whose whole job is saying which box a node goes in.
170
+ */
171
+ export function entityStack(entity: Declarable): string | undefined {
172
+ const stack = (entity as unknown as { stack?: unknown }).stack;
173
+ return typeof stack === "string" && stack.length > 0 ? stack : undefined;
174
+ }
175
+
93
176
  /** Grouping metadata for cluster/subgraph rendering. Maps group name -> node ids. */
94
177
  export interface IRGroups {
95
178
  byLexicon?: Record<string, string[]>;
@@ -99,11 +182,15 @@ export interface IRGroups {
99
182
  * boundary boxes) read this rather than inferring stacks — which is the point,
100
183
  * so it should never require one.
101
184
  *
102
- * Two sources, depending on how the project is shaped:
185
+ * Three sources, depending on how the project is shaped:
103
186
  *
104
187
  * - **side-by-side stacks**, declared in config and composed by
105
188
  * `buildDeclaredPerStack` — keys are the declared stack names. Nothing is
106
189
  * inferred; the project stated both the names and the membership.
190
+ * - **entities that name their own unit** ({@link entityStack}, #2266) —
191
+ * keys are those names, one entry per unit. A terraform root is the case
192
+ * this exists for: five `terraform.roots` are five `terraform apply`s and
193
+ * five boundary boxes, not one bucket called `terraform`.
107
194
  * - **one source tree** — keys are lexicon partitions, since each lexicon
108
195
  * serialises to one deployable stack.
109
196
  *
@@ -315,7 +402,12 @@ function project(value: unknown, seen: Set<unknown>, reverse: Map<object, string
315
402
  return out;
316
403
  }
317
404
 
318
- const SKIP_KEYS = new Set(["lexicon", "entityType", "kind", "attributes", "Ref"]);
405
+ // `references` and `stack` are the two duck-typed channels above
406
+ // ({@link entityReferences}, {@link entityStack}). They are metadata about the
407
+ // entity's place in the graph, not declared configuration, and both are read
408
+ // directly, so projecting them into `attrs` would only restate an edge and a
409
+ // group key the IR already carries.
410
+ const SKIP_KEYS = new Set(["lexicon", "entityType", "kind", "attributes", "Ref", "references", "stack"]);
319
411
 
320
412
  /** The config bag of a node, paired with each key. Lexicon entities keep their
321
413
  * declared props in a (usually non-enumerable) `props` object; simpler entities
@@ -349,7 +441,17 @@ function projectConfig(
349
441
  return out;
350
442
  }
351
443
 
352
- /** Collect ref edges from one node, labelling each with its consumer property. */
444
+ /**
445
+ * Collect ref edges from one node, labelling each with its consumer property.
446
+ *
447
+ * Two sources, merged here. First the references the entity resolved for
448
+ * itself ({@link EntityReference}), which is how a lexicon that parses HCL,
449
+ * YAML or any other foreign source says what its strings point at. Then the
450
+ * walk over the config bag for {@link AttrRef} objects and `Ref` intrinsics,
451
+ * which is how a typed lexicon's props carry the same fact. A lexicon uses one
452
+ * or the other; nothing stops it using both, and the dedup in `buildGraphIr`
453
+ * collapses an edge two sources agree on.
454
+ */
353
455
  function collectEdges(
354
456
  entity: Declarable,
355
457
  from: string,
@@ -357,6 +459,16 @@ function collectEdges(
357
459
  reverse: Map<object, string>,
358
460
  ): IREdge[] {
359
461
  const edges: IREdge[] = [];
462
+ for (const ref of entityReferences(entity)) {
463
+ if (ref.to === from || !nodeIds.has(ref.to)) continue;
464
+ edges.push({
465
+ from,
466
+ to: ref.to,
467
+ kind: "ref",
468
+ ...(ref.viaAttr ? { viaAttr: ref.viaAttr } : {}),
469
+ ...(ref.toAttr ? { toAttr: ref.toAttr } : {}),
470
+ });
471
+ }
360
472
  const seen = new Set<unknown>();
361
473
  const visit = (value: unknown, viaAttr: string): void => {
362
474
  if (value === null || typeof value !== "object") return;
@@ -440,9 +552,14 @@ export function buildGraphIr(
440
552
  nodes.push(node);
441
553
 
442
554
  (byLexicon[entity.lexicon] ??= []).push(name);
443
- // Within ONE source tree a stack is a lexicon partition — each lexicon
444
- // serialises to one deployable stack (a CloudFormation template, a CI
445
- // config) — so that is what `byStack` reports here. It stays a distinct axis
555
+ // An entity that names its own deployable unit is taken at its word
556
+ // (#2266): a terraform root is one `terraform apply`, and a project
557
+ // declaring five of them has five stacks whatever the lexicon count says.
558
+ // See {@link entityStack}.
559
+ //
560
+ // Otherwise, within ONE source tree a stack is a lexicon partition — each
561
+ // lexicon serialises to one deployable stack (a CloudFormation template, a
562
+ // CI config) — so that is what `byStack` reports. It stays a distinct axis
446
563
  // from `byLexicon` (which is for provenance/colouring), emitted separately
447
564
  // even where the two coincide.
448
565
  //
@@ -455,7 +572,7 @@ export function buildGraphIr(
455
572
  // child-project". #513 is closed, that phase was never filed, and directory
456
573
  // partitioning is the exception rather than the rule — so the promise is
457
574
  // withdrawn rather than left pointing at a closed issue.
458
- (byStack[entity.lexicon] ??= []).push(name);
575
+ (byStack[entityStack(entity) ?? entity.lexicon] ??= []).push(name);
459
576
  if (prov?.composite) (byComposite[prov.composite] ??= []).push(name);
460
577
  }
461
578
 
@@ -121,6 +121,7 @@ export async function assertLiveEntity(opts: AssertLiveEntityOptions): Promise<R
121
121
  resources: {},
122
122
  unobserved: unobservedAll([name], "read-failed", detail, { [name]: entityType }),
123
123
  queried: {},
124
+ sources: {},
124
125
  notes: [],
125
126
  };
126
127
  }
@@ -55,6 +55,8 @@ function qualifyObservation(obs: NormalizedObservation, stackName: string): Norm
55
55
  resources: q(obs.resources),
56
56
  unobserved: q(obs.unobserved),
57
57
  queried: q(obs.queried),
58
+ // Which read answered (#2267) is per entity, so it re-keys with them.
59
+ sources: q(obs.sources),
58
60
  notes: obs.notes,
59
61
  // Exports are already keyed by stack (#1279); nothing to qualify.
60
62
  ...(obs.stackExports ? { stackExports: obs.stackExports } : {}),
@@ -283,6 +285,7 @@ export async function observeResources(
283
285
  resources: {},
284
286
  unobserved: unobservedAll(entityNames, "read-failed", message, entities),
285
287
  queried: {},
288
+ sources: {},
286
289
  notes: [],
287
290
  },
288
291
  environment,
@@ -29,7 +29,7 @@ const meta = (over: Partial<ResourceMetadata> = {}): ResourceMetadata => ({
29
29
 
30
30
  describe("normalizeObservation", () => {
31
31
  test("a bare map means 'I looked at everything'", () => {
32
- expect(normalizeObservation({ a: meta() })).toEqual({ resources: { a: meta() }, unobserved: {}, queried: {}, notes: [] });
32
+ expect(normalizeObservation({ a: meta() })).toEqual({ resources: { a: meta() }, unobserved: {}, queried: {}, sources: {}, notes: [] });
33
33
  });
34
34
 
35
35
  test("the envelope carries both halves", () => {
@@ -38,6 +38,7 @@ describe("normalizeObservation", () => {
38
38
  resources: { a: meta() },
39
39
  unobserved: { b: { reason: "read-failed" } },
40
40
  queried: {},
41
+ sources: {},
41
42
  notes: [],
42
43
  });
43
44
  });
@@ -50,7 +51,7 @@ describe("normalizeObservation", () => {
50
51
  });
51
52
 
52
53
  test("undefined normalizes to empty maps", () => {
53
- expect(normalizeObservation(undefined)).toEqual({ resources: {}, unobserved: {}, queried: {}, notes: [] });
54
+ expect(normalizeObservation(undefined)).toEqual({ resources: {}, unobserved: {}, queried: {}, sources: {}, notes: [] });
54
55
  });
55
56
 
56
57
  test("the envelope carries the queried addresses through normalization (#1620)", () => {
@@ -92,8 +93,8 @@ describe("unobservedAll", () => {
92
93
  describe("mergeObservations (multi-stack)", () => {
93
94
  test("present beats not-observed beats absent", () => {
94
95
  const merged = mergeObservations([
95
- { resources: {}, unobserved: { a: { reason: "read-failed" }, b: { reason: "no-binding" } }, queried: {}, notes: [] },
96
- { resources: { a: meta() }, unobserved: {}, queried: {}, notes: [] },
96
+ { resources: {}, unobserved: { a: { reason: "read-failed" }, b: { reason: "no-binding" } }, queried: {}, sources: {}, notes: [] },
97
+ { resources: { a: meta() }, unobserved: {}, queried: {}, sources: {}, notes: [] },
97
98
  ]);
98
99
  expect(Object.keys(merged.resources)).toEqual(["a"]);
99
100
  expect(Object.keys(merged.unobserved)).toEqual(["b"]);
@@ -102,26 +103,37 @@ describe("mergeObservations (multi-stack)", () => {
102
103
  test("the same note from four stacks is one note (#1265)", () => {
103
104
  const note = "ownership filter unavailable";
104
105
  const merged = mergeObservations(
105
- ["a", "b", "c", "d"].map((k) => ({ resources: { [k]: meta() }, unobserved: {}, queried: {}, notes: [note] })),
106
+ ["a", "b", "c", "d"].map((k) => ({ resources: { [k]: meta() }, unobserved: {}, queried: {}, sources: {}, notes: [note] })),
106
107
  );
107
108
  expect(merged.notes).toEqual([note]);
108
109
  });
109
110
 
110
111
  test("an entity nobody looked for in any stack stays absent", () => {
111
112
  const merged = mergeObservations([
112
- { resources: { a: meta() }, unobserved: {}, queried: {}, notes: [] },
113
- { resources: { b: meta() }, unobserved: {}, queried: {}, notes: [] },
113
+ { resources: { a: meta() }, unobserved: {}, queried: {}, sources: {}, notes: [] },
114
+ { resources: { b: meta() }, unobserved: {}, queried: {}, sources: {}, notes: [] },
114
115
  ]);
115
116
  expect(merged.unobserved).toEqual({});
116
117
  });
117
118
 
118
119
  test("queried addresses union across stacks (#1620)", () => {
119
120
  const merged = mergeObservations([
120
- { resources: {}, unobserved: {}, queried: { a: "stack-1/a" }, notes: [] },
121
- { resources: { b: meta() }, unobserved: {}, queried: { b: "stack-2/b" }, notes: [] },
121
+ { resources: {}, unobserved: {}, queried: { a: "stack-1/a" }, sources: {}, notes: [] },
122
+ { resources: { b: meta() }, unobserved: {}, queried: { b: "stack-2/b" }, sources: {}, notes: [] },
122
123
  ]);
123
124
  expect(merged.queried).toEqual({ a: "stack-1/a", b: "stack-2/b" });
124
125
  });
126
+
127
+ test("which read answered unions across parts (#2267)", () => {
128
+ // Parts read disjoint entity sets (one per stack, or per terraform root),
129
+ // so the union is the whole answer and there is nothing to reconcile. A
130
+ // project mixing a stock root and a live one gets both values here.
131
+ const merged = mergeObservations([
132
+ { resources: { a: meta() }, unobserved: {}, queried: {}, sources: { a: "state" }, notes: [] },
133
+ { resources: { b: meta() }, unobserved: {}, queried: {}, sources: { b: "live" }, notes: [] },
134
+ ]);
135
+ expect(merged.sources).toEqual({ a: "state", b: "live" });
136
+ });
125
137
  });
126
138
 
127
139
  describe("reason totality", () => {
@@ -112,6 +112,24 @@ export interface ObservationResult {
112
112
  * namespace, endpoint or region was read, not the one the resource lives in.
113
113
  */
114
114
  queried?: Record<string, string>;
115
+ /**
116
+ * Which read answered for each declared entity (#2267), keyed by chant
117
+ * entity name. The value is a short token the lexicon defines and documents;
118
+ * core carries it and never interprets it, so nothing here switches on one.
119
+ *
120
+ * Additive metadata over the tri-state, exactly like {@link queried}, and
121
+ * present for every verdict including OBSERVED-ABSENT and NOT-OBSERVED,
122
+ * since which read was attempted is a fact whatever the read came back with.
123
+ *
124
+ * A lexicon with one read path has nothing to say here and omits it. This is
125
+ * for a lexicon with two, where the two answer different questions: a stock
126
+ * Terraform root is read with `terraform show -json` over its state, which
127
+ * says what the last apply recorded, and a live root is read with
128
+ * `choudoufu live-plan -json`, which says what the account holds. A renderer
129
+ * that paints an overlay as drift must not paint the first one green, and
130
+ * before this field an observation gave it no way to tell them apart.
131
+ */
132
+ sources?: Record<string, string>;
115
133
  /**
116
134
  * Notices about the read as a whole, not about any one entity (#1265) —
117
135
  * "the ownership filter could not be applied on this surface" is the
@@ -144,6 +162,8 @@ export interface NormalizedObservation {
144
162
  unobserved: Record<string, UnobservedEntity>;
145
163
  /** Resolved query address per entity name (#1620). Empty when the lexicon reported none. */
146
164
  queried: Record<string, string>;
165
+ /** Which read answered, per entity name (#2267). Empty when the lexicon reported none. */
166
+ sources: Record<string, string>;
147
167
  /** Run-level notices (#1265), distinct. Empty when the lexicon reported none. */
148
168
  notes: string[];
149
169
  /** Per-stack exports (#1279), keyed by stack name. Absent when the lexicon reported none. */
@@ -169,12 +189,14 @@ export function observation(
169
189
  queried?: Record<string, string>,
170
190
  notes?: string[],
171
191
  stackExports?: Record<string, Record<string, unknown>>,
192
+ sources?: Record<string, string>,
172
193
  ): ObservationResult {
173
194
  return {
174
195
  observation: "v1",
175
196
  resources,
176
197
  ...(unobserved && Object.keys(unobserved).length > 0 ? { unobserved } : {}),
177
198
  ...(queried && Object.keys(queried).length > 0 ? { queried } : {}),
199
+ ...(sources && Object.keys(sources).length > 0 ? { sources } : {}),
178
200
  ...(notes && notes.length > 0 ? { notes } : {}),
179
201
  ...(stackExports && Object.keys(stackExports).length > 0 ? { stackExports } : {}),
180
202
  };
@@ -187,17 +209,18 @@ export function observation(
187
209
  * {@link unobservedAll} rather than returning nothing.
188
210
  */
189
211
  export function normalizeObservation(value: DescribeResourcesResult | undefined): NormalizedObservation {
190
- if (!value) return { resources: {}, unobserved: {}, queried: {}, notes: [] };
212
+ if (!value) return { resources: {}, unobserved: {}, queried: {}, sources: {}, notes: [] };
191
213
  if (isObservationResult(value)) {
192
214
  return {
193
215
  resources: value.resources ?? {},
194
216
  unobserved: value.unobserved ?? {},
195
217
  queried: value.queried ?? {},
218
+ sources: value.sources ?? {},
196
219
  notes: [...new Set(value.notes ?? [])],
197
220
  ...(value.stackExports && Object.keys(value.stackExports).length > 0 ? { stackExports: value.stackExports } : {}),
198
221
  };
199
222
  }
200
- return { resources: value, unobserved: {}, queried: {}, notes: [] };
223
+ return { resources: value, unobserved: {}, queried: {}, sources: {}, notes: [] };
201
224
  }
202
225
 
203
226
  /**
@@ -237,6 +260,10 @@ export function mergeObservations(parts: Iterable<NormalizedObservation>): Norma
237
260
  const resources: Record<string, ResourceMetadata> = {};
238
261
  const unobserved: Record<string, UnobservedEntity> = {};
239
262
  const queried: Record<string, string> = {};
263
+ // Which read answered (#2267) merges the same way `queried` does: it is a
264
+ // fact about one entity's read, and the parts being merged are reads of
265
+ // disjoint entity sets (one per stack, or per terraform root).
266
+ const sources: Record<string, string> = {};
240
267
  // A note is about the read, not a stack; four stacks saying the same thing
241
268
  // is one note (#1265).
242
269
  const notes = new Set<string>();
@@ -245,6 +272,7 @@ export function mergeObservations(parts: Iterable<NormalizedObservation>): Norma
245
272
  Object.assign(resources, part.resources);
246
273
  Object.assign(unobserved, part.unobserved);
247
274
  Object.assign(queried, part.queried);
275
+ Object.assign(sources, part.sources);
248
276
  for (const n of part.notes) notes.add(n);
249
277
  Object.assign(stackExports, part.stackExports ?? {});
250
278
  }
@@ -255,6 +283,7 @@ export function mergeObservations(parts: Iterable<NormalizedObservation>): Norma
255
283
  resources,
256
284
  unobserved,
257
285
  queried,
286
+ sources,
258
287
  notes: [...notes],
259
288
  ...(Object.keys(stackExports).length > 0 ? { stackExports } : {}),
260
289
  };