@intentius/chant 0.44.6 → 0.44.7

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 (42) hide show
  1. package/dist/cli/commands/carve-bridge.d.ts.map +1 -1
  2. package/dist/cli/commands/carve-emit.d.ts +3 -1
  3. package/dist/cli/commands/carve-emit.d.ts.map +1 -1
  4. package/dist/cli/commands/carve.d.ts +7 -2
  5. package/dist/cli/commands/carve.d.ts.map +1 -1
  6. package/dist/terraform/adopt-state.d.ts +18 -1
  7. package/dist/terraform/adopt-state.d.ts.map +1 -1
  8. package/dist/terraform/aws-resources.d.ts +29 -0
  9. package/dist/terraform/aws-resources.d.ts.map +1 -1
  10. package/dist/terraform/bridge.d.ts +8 -0
  11. package/dist/terraform/bridge.d.ts.map +1 -1
  12. package/dist/terraform/carve.d.ts +8 -3
  13. package/dist/terraform/carve.d.ts.map +1 -1
  14. package/dist/terraform/graph.d.ts +10 -4
  15. package/dist/terraform/graph.d.ts.map +1 -1
  16. package/dist/terraform/parse.d.ts.map +1 -1
  17. package/dist/terraform/score.d.ts +11 -0
  18. package/dist/terraform/score.d.ts.map +1 -1
  19. package/dist/terraform/types.d.ts +11 -0
  20. package/dist/terraform/types.d.ts.map +1 -1
  21. package/package.json +1 -1
  22. package/src/cli/commands/carve-bridge.test.ts +32 -0
  23. package/src/cli/commands/carve-bridge.ts +3 -0
  24. package/src/cli/commands/carve-emit-state.test.ts +107 -0
  25. package/src/cli/commands/carve-emit.ts +37 -5
  26. package/src/cli/commands/carve.test.ts +39 -1
  27. package/src/cli/commands/carve.ts +8 -2
  28. package/src/terraform/__fixtures__/advise.test.ts +6 -4
  29. package/src/terraform/adopt-state.test.ts +64 -0
  30. package/src/terraform/adopt-state.ts +52 -2
  31. package/src/terraform/aws-resources.test.ts +102 -1
  32. package/src/terraform/aws-resources.ts +144 -2
  33. package/src/terraform/bridge.test.ts +30 -0
  34. package/src/terraform/bridge.ts +19 -1
  35. package/src/terraform/carve.test.ts +34 -0
  36. package/src/terraform/carve.ts +0 -0
  37. package/src/terraform/graph.test.ts +45 -0
  38. package/src/terraform/graph.ts +35 -16
  39. package/src/terraform/parse.ts +4 -1
  40. package/src/terraform/score.test.ts +25 -0
  41. package/src/terraform/score.ts +27 -4
  42. package/src/terraform/types.ts +11 -0
@@ -1 +1 @@
1
- {"version":3,"file":"carve-bridge.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/carve-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,EAAkB,KAAK,UAAU,EAAuB,MAAM,wBAAwB,CAAC;AAK9F,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,mDAAmD;IACnD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AASD,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA+GtF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAkCnE"}
1
+ {"version":3,"file":"carve-bridge.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/carve-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,EAAkB,KAAK,UAAU,EAAuB,MAAM,wBAAwB,CAAC;AAK9F,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,mDAAmD;IACnD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AASD,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA+GtF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAqCnE"}
@@ -12,7 +12,7 @@
12
12
  * resource at the observe position: emitted, reversible, nothing mutated.
13
13
  */
14
14
  import { type CarveReport } from "../../terraform/carve.js";
15
- import { type DeferredParam } from "../../terraform/adopt-state.js";
15
+ import { type DeferredParam, type FoldedContribution } from "../../terraform/adopt-state.js";
16
16
  import type { LexiconPlugin, ResourceSelector } from "../../lexicon.js";
17
17
  import type { ImportResult, LiveImportOptions } from "./import.js";
18
18
  export interface CarveEmitOptions {
@@ -56,6 +56,8 @@ export interface CarveEmitResult {
56
56
  scaffolded?: string[];
57
57
  /** Deferred outbound inputs declared as build parameters in the emitted project (#998). */
58
58
  params?: DeferredParam[];
59
+ /** Folded sub-resources and the props each joined into the emitted parent (#1637). */
60
+ folded?: FoldedContribution[];
59
61
  /** The persisted carve state manifest bridge/apply compose with. */
60
62
  manifestPath?: string;
61
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"carve-emit.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/carve-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,EAAqC,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAI5F,OAAO,EAAkE,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEjI,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAEhE,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,2DAA2D;AAC3D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,UAAU,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CAC7F;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,oCAAoC;IACpC,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC5B,4BAA4B;IAC5B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;IACzB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC,CA0FrG;AAiJD,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CA+C/D"}
1
+ {"version":3,"file":"carve-emit.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/carve-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,EAAqC,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAI5F,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACxB,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAEhE,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,2DAA2D;AAC3D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,UAAU,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CAC7F;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,oCAAoC;IACpC,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC5B,4BAA4B;IAC5B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;IACzB,sFAAsF;IACtF,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC9B,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC,CAyGrG;AAiJD,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAwD/D"}
@@ -37,8 +37,9 @@ export declare function carveAdvise(opts: CarveAdviseOptions): Promise<CarveAdvi
37
37
  * says which shape it is. The promise attached to this number:
38
38
  *
39
39
  * - **Additive within a version.** New top-level fields, new per-resource
40
- * fields, and new entries in an existing list may appear in any release. A
41
- * reader must ignore keys it does not know.
40
+ * fields, new kinds of entry in an existing list, and new values in an
41
+ * open-ended enum (a `bridge` kind, say) may appear in any release. A
42
+ * reader must ignore keys and values it does not know.
42
43
  * - **A removal, a rename, or a changed meaning bumps it.** So does narrowing
43
44
  * a field's type (an optional becoming required is additive; the reverse is
44
45
  * not).
@@ -60,6 +61,10 @@ export interface CarveJsonResource extends Peelability {
60
61
  * stand in for it. Edges internal to the carve set are not boundary work and
61
62
  * are not listed.
62
63
  *
64
+ * An inbound edge's survivor can be an `output.<name>` pseudo-address
65
+ * (#1638), carrying `bridge: "tf-output-rewrite"` and `via: ["value"]`. It
66
+ * is counted in `breakdown.outputs`, not `breakdown.inbound`.
67
+ *
63
68
  * Present (possibly with two empty lists) whenever the graph was available;
64
69
  * absent means this chant did not compute it — "none" and "not reported" are
65
70
  * different claims. `breakdown.inbound`/`outbound` keep the counts.
@@ -1 +1 @@
1
- {"version":3,"file":"carve.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/carve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAe,KAAK,WAAW,EAAwB,MAAM,uBAAuB,CAAC;AAC5F,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAID,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA2BtF;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,gGAAgG;AAChG,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,YAAY,EAAE,CAAC;QAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;CAClE;AAED,yGAAyG;AACzG,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,SAAS,EAAE,iBAAiB,EAAE,CAAC;CAChC;AAED,+CAA+C;AAC/C,wBAAgB,SAAS,CAAC,MAAM,EAAE,iBAAiB,GAAG,eAAe,CAapE;AAUD,6CAA6C;AAC7C,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAyBnE"}
1
+ {"version":3,"file":"carve.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/carve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAe,KAAK,WAAW,EAAwB,MAAM,uBAAuB,CAAC;AAC5F,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAID,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA2BtF;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,gGAAgG;AAChG,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,YAAY,EAAE,CAAC;QAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;CAClE;AAED,yGAAyG;AACzG,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,SAAS,EAAE,iBAAiB,EAAE,CAAC;CAChC;AAED,+CAA+C;AAC/C,wBAAgB,SAAS,CAAC,MAAM,EAAE,iBAAiB,GAAG,eAAe,CAapE;AAUD,6CAA6C;AAC7C,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAyBnE"}
@@ -38,6 +38,13 @@ export interface DeferredParam {
38
38
  /** The state-resolved value — the parameter's declared default. */
39
39
  default?: string | number | boolean;
40
40
  }
41
+ /** What one folded sub-resource contributed to the parent's emitted props (#1637). */
42
+ export interface FoldedContribution {
43
+ /** The sub-resource's Terraform address, e.g. `aws_s3_bucket_versioning.assets`. */
44
+ address: string;
45
+ /** CFN properties it added to the parent, e.g. `["VersioningConfiguration"]`. */
46
+ props: string[];
47
+ }
41
48
  export interface AdoptedSource {
42
49
  fileName: string;
43
50
  content: string;
@@ -46,6 +53,8 @@ export interface AdoptedSource {
46
53
  nativeType: string;
47
54
  /** Deferred params actually substituted into the emitted props (#998). */
48
55
  parameterized: string[];
56
+ /** Folded sub-resources and the props each one joined into the parent (#1637). */
57
+ folded: FoldedContribution[];
49
58
  }
50
59
  /** Is this Terraform type adoptable from state (has a native constructor)? */
51
60
  export declare function canAdoptFromState(tfType: string): boolean;
@@ -59,6 +68,14 @@ export declare function supportedStateAdoptionTypes(): string[];
59
68
  * A mapped attribute named by a `DeferredParam` renders as a `params.<name>`
60
69
  * reference (a real chant build parameter) instead of the state literal —
61
70
  * the value came from a survivor, so it stays overridable per build.
71
+ *
72
+ * `folded` carries the carve set's sub-resources (`aws_s3_bucket_versioning`
73
+ * and friends), read from the same state file. Their mappable attributes join
74
+ * the parent's props (#1637) — a fold that only announced itself and left the
75
+ * emitted resource without the versioning or public-access block the Terraform
76
+ * declared was a silent loss of configuration. A sub-resource's setting wins
77
+ * over the parent's own legacy in-state block: it is the one the config
78
+ * actually declares.
62
79
  */
63
- export declare function adoptFromState(resource: StateResource, params?: DeferredParam[]): AdoptedSource | null;
80
+ export declare function adoptFromState(resource: StateResource, params?: DeferredParam[], folded?: StateResource[]): AdoptedSource | null;
64
81
  //# sourceMappingURL=adopt-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adopt-state.d.ts","sourceRoot":"","sources":["../../src/terraform/adopt-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,uEAAuE;AACvE,eAAO,MAAM,aAAa,4BAA4B,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,uFAAuF;AACvF,wBAAgB,2BAA2B,IAAI,MAAM,EAAE,CAEtD;AAUD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,GAAE,aAAa,EAAO,GAAG,aAAa,GAAG,IAAI,CA+C1G"}
1
+ {"version":3,"file":"adopt-state.d.ts","sourceRoot":"","sources":["../../src/terraform/adopt-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAUH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,uEAAuE;AACvE,eAAO,MAAM,aAAa,4BAA4B,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CACrC;AAED,sFAAsF;AACtF,MAAM,WAAW,kBAAkB;IACjC,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,kFAAkF;IAClF,MAAM,EAAE,kBAAkB,EAAE,CAAC;CAC9B;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,uFAAuF;AACvF,wBAAgB,2BAA2B,IAAI,MAAM,EAAE,CAEtD;AAUD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,aAAa,EACvB,MAAM,GAAE,aAAa,EAAO,EAC5B,MAAM,GAAE,aAAa,EAAO,GAC3B,aAAa,GAAG,IAAI,CAoEtB"}
@@ -36,6 +36,35 @@ export interface AwsCarveType {
36
36
  export declare const AWS_LEXICON_IMPORT = "@intentius/chant-lexicon-aws";
37
37
  export declare const AWS_CARVE_TYPES: AwsCarveType[];
38
38
  export declare function awsCarveType(tfType: string): AwsCarveType | undefined;
39
+ /**
40
+ * How a folded sub-resource (see `FOLDS_INTO`) joins its parent's emitted
41
+ * properties (#1637). Terraform splits configuration the CloudFormation shape
42
+ * keeps inside the parent resource, so the fold is not just a carve-set
43
+ * membership claim: the sub-resource's attributes have to land in the parent's
44
+ * props, or the emitted resource silently loses what the Terraform declared.
45
+ */
46
+ export interface AwsFoldMapper {
47
+ /** Sub-resource attributes this mapper reads. Anything else stays unmapped. */
48
+ consumes: string[];
49
+ /** The parent CFN properties this sub-resource contributes. */
50
+ map: (attrs: Record<string, unknown>) => Record<string, unknown>;
51
+ }
52
+ export declare const AWS_FOLD_MAPPERS: Record<string, AwsFoldMapper>;
53
+ /**
54
+ * Apply a folded sub-resource's state attributes to its parent's properties.
55
+ *
56
+ * Returns the props it contributes plus the attributes that stay genuinely
57
+ * unmappable (which the emitted source preserves in its reference comment). A
58
+ * mapper that produced nothing consumes nothing — the caller reports the whole
59
+ * sub-resource rather than claiming a fold that did not happen. `null` means
60
+ * this sub-resource type has no fold mapping at all.
61
+ */
62
+ export declare function applyAwsFold(tfType: string, attrs: Record<string, unknown>): {
63
+ props: Record<string, unknown>;
64
+ unmapped: Record<string, unknown>;
65
+ } | null;
66
+ /** A folded sub-resource's attributes minus what was consumed and its parent link. */
67
+ export declare function unmappedFoldAttrs(attrs: Record<string, unknown>, consumed?: ReadonlySet<string>): Record<string, unknown>;
39
68
  /**
40
69
  * Apply an entry's field mappings to a resource's Terraform attributes, yielding
41
70
  * CloudFormation properties and the set of attribute keys that were consumed
@@ -1 +1 @@
1
- {"version":3,"file":"aws-resources.d.ts","sourceRoot":"","sources":["../../src/terraform/aws-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,+DAA+D;AAC/D,KAAK,SAAS,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAA;CAAE,CAAC;AAE/E,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,kEAAkE;IAClE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,eAAO,MAAM,kBAAkB,iCAAiC,CAAC;AAajE,eAAO,MAAM,eAAe,EAAE,YAAY,EAqNzC,CAAC;AAIF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAErE;AASD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAoB1D"}
1
+ {"version":3,"file":"aws-resources.d.ts","sourceRoot":"","sources":["../../src/terraform/aws-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,+DAA+D;AAC/D,KAAK,SAAS,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAA;CAAE,CAAC;AAE/E,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,kEAAkE;IAClE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,eAAO,MAAM,kBAAkB,iCAAiC,CAAC;AA6DjE,eAAO,MAAM,eAAe,EAAE,YAAY,EA2NzC,CAAC;AAIF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAErE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,+DAA+D;IAC/D,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClE;AAKD,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAiC1D,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,IAAI,CAM9E;AAED,sFAAsF;AACtF,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,QAAQ,GAAE,WAAW,CAAC,MAAM,CAAa,GACxC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB;AASD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAuB1D"}
@@ -8,6 +8,8 @@
8
8
  * Bridge: add a `data` source for the (now chant-managed) resource and
9
9
  * rewrite the survivor's `type.name.attr` references to `data.type.name.attr`.
10
10
  * Required immediately, or `terraform plan` errors on the dangling ref.
11
+ * An `output` block reading the carved resource is such a survivor (#1638):
12
+ * same data source, same textual rewrite, applied to the output's value.
11
13
  *
12
14
  * - outbound edge → the carved resource read a value from a survivor. That
13
15
  * value must enter chant from outside synthesis, as a deploy-time input.
@@ -55,6 +57,12 @@ export interface BridgePlan {
55
57
  deferredInputs: DeferredInput[];
56
58
  /** Every carved address whose own block the rewrites remove, across files. */
57
59
  excised: string[];
60
+ /**
61
+ * `output` blocks whose value the rewrites repoint at a data source (#1638),
62
+ * as `output.<name>`. They are inbound edges like any other — listed
63
+ * separately only because the patch is a one-line expression edit.
64
+ */
65
+ outputRewrites: string[];
58
66
  runbook: string;
59
67
  }
60
68
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/terraform/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAG9D,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,8EAA8E;IAC9E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAiBD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EAC/C,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GACtC,UAAU,CAwDZ"}
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/terraform/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAG9D,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,8EAA8E;IAC9E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;OAIG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAiBD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EAC/C,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GACtC,UAAU,CA8DZ"}
@@ -27,10 +27,15 @@ export type BoundaryDirection = "inbound" | "outbound";
27
27
  * - outbound → the carve set depends on a survivor. Bridge: the value enters
28
28
  * chant as a deploy-time input. Deferred until apply; only provenance is
29
29
  * recorded at the observe position.
30
+ *
31
+ * An inbound edge whose survivor is an `output` block (#1638) is the same
32
+ * urgency and the same patch — the output's expression is rewritten onto the
33
+ * data source — but it is called out as `tf-output-rewrite` so a reader can
34
+ * tell a one-line output edit from a resource's rewiring.
30
35
  */
31
36
  export interface BoundaryEdge {
32
37
  direction: BoundaryDirection;
33
- /** The survivor side of the edge. */
38
+ /** The survivor side of the edge. `output.<name>` for an output block. */
34
39
  survivor: string;
35
40
  /** The carve-set side of the edge. */
36
41
  carved: string;
@@ -39,10 +44,10 @@ export interface BoundaryEdge {
39
44
  * The referring block's own top-level attribute(s) the reference sits in
40
45
  * (#998). For an outbound edge this names the carved resource's attribute
41
46
  * that reads the survivor — what the deferred input's build parameter is
42
- * named after on emit.
47
+ * named after on emit. For an output edge it is `["value"]`.
43
48
  */
44
49
  via: string[];
45
- bridge: "tf-data-source" | "deferred-input";
50
+ bridge: "tf-data-source" | "tf-output-rewrite" | "deferred-input";
46
51
  required: "immediately" | "at-apply";
47
52
  }
48
53
  export interface CarveReport {
@@ -1 +1 @@
1
- {"version":3,"file":"carve.d.ts","sourceRoot":"","sources":["../../src/terraform/carve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,uFAAuF;AACvF,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;;;OAKG;IACH,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAAC;IAC5C,QAAQ,EAAE,aAAa,GAAG,UAAU,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,iFAAiF;IACjF,UAAU,EAAE,IAAI,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CAgB7E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAkDjF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAExD"}
1
+ {"version":3,"file":"carve.d.ts","sourceRoot":"","sources":["../../src/terraform/carve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,uFAAuF;AACvF,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;;;OAKG;IACH,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,MAAM,EAAE,gBAAgB,GAAG,mBAAmB,GAAG,gBAAgB,CAAC;IAClE,QAAQ,EAAE,aAAa,GAAG,UAAU,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,iFAAiF;IACjF,UAAU,EAAE,IAAI,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CAgB7E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAkDjF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAExD"}
@@ -31,9 +31,9 @@ interface RawRef {
31
31
  */
32
32
  export declare function refFromAccessor(accessor: string): RawRef | null;
33
33
  /**
34
- * Every string value carrying an interpolation across the tree's resource and
35
- * module blocks — exactly the expressions `parse.ts` must resolve through the
36
- * AST before `buildGraph` can classify them.
34
+ * Every string value carrying an interpolation across the tree's resource,
35
+ * module and output blocks — exactly the expressions `parse.ts` must resolve
36
+ * through the AST before `buildGraph` can classify them.
37
37
  */
38
38
  export declare function collectExpressions(tree: Hcl2JsonTree): string[];
39
39
  /**
@@ -44,9 +44,15 @@ export declare function collectExpressions(tree: Hcl2JsonTree): string[];
44
44
  * marks the referring node dynamic. An edge is recorded only when its target
45
45
  * resolves to a known resource/module node — references to `var`/`local`/data
46
46
  * are dropped.
47
+ *
48
+ * `output` blocks are not nodes either — nothing carves an output — but they
49
+ * do reference, and a reference to a carved resource breaks the surviving plan
50
+ * exactly like a resource's does. So an output contributes an edge tagged
51
+ * `fromKind: "output"` from the pseudo-address `output.<name>` (#1638),
52
+ * which the scorer weights lower and `carve bridge` patches.
47
53
  */
48
54
  export declare function buildGraph(tree: Hcl2JsonTree, exprRefs: ExpressionRefs): TfGraph;
49
- /** Edges where other nodes depend on `address` (each → a surviving-TF data-source patch). */
55
+ /** Edges where something in the surviving Terraform depends on `address`. */
50
56
  export declare function inboundEdges(graph: TfGraph, address: string): TfEdge[];
51
57
  /** Edges where `address` depends on other nodes (each → a deferred deploy-time input). */
52
58
  export declare function outboundEdges(graph: TfGraph, address: string): TfEdge[];
@@ -1 +1 @@
1
- {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/terraform/graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAU,MAAM,SAAS,CAAC;AAErE;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;AAKpE,UAAU,MAAM;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA4BD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe/D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,EAAE,CAc/D;AA4DD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAwEhF;AAED,6FAA6F;AAC7F,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAEtE;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvE"}
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/terraform/graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAU,MAAM,SAAS,CAAC;AAErE;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;AAKpE,UAAU,MAAM;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA4BD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe/D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,EAAE,CAe/D;AA4DD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAoFhF;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAEtE;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvE"}
@@ -1 +1 @@
1
- {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/terraform/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAErD,wDAAwD;AACxD,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAChE,yFAAyF;IACzF,yBAAyB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;CACxG;AAED,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,KAAK,EAAE,OAAO;CAQ3B;AAED;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC,CAMtD;AA+CD,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,OAAO,CAAC,CAWvG"}
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/terraform/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAErD,wDAAwD;AACxD,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAChE,yFAAyF;IACzF,yBAAyB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;CACxG;AAED,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,KAAK,EAAE,OAAO;CAQ3B;AAED;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC,CAMtD;AAkDD,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,OAAO,CAAC,CAWvG"}
@@ -6,11 +6,18 @@
6
6
  * score = 100
7
7
  * - 12 * inbound # survivors that depend on this → each a TF data-source patch
8
8
  * - 4 * outbound # this depends on survivors → a deferred deploy-time input
9
+ * - 4 * outputs # an output block reads this → a one-line rewrite (#1638)
9
10
  * - 15 * (tier - 1) # native-spec map: tier1=0, tier2=-15, tier3=-30
10
11
  * - 10 * has_dynamic # count / for_each / data present
11
12
  * - 3 * (instances - 1) # state-expanded instance count
12
13
  * clamp 0..100 (unsupported provider/type → 0)
13
14
  *
15
+ * An `output` block referencing the target is a real inbound dependency — the
16
+ * surviving plan errors on the dangling reference — so it cannot score as
17
+ * free. It is not a data-source patch either: bridging it rewrites one
18
+ * expression in a block that manages no infrastructure, the same order of work
19
+ * as recording an outbound deferred input. Hence 4, not 12.
20
+ *
14
21
  * Sub-resources that inline into a parent (see `FOLDS_INTO`) are folded into the
15
22
  * parent's carve set: they are removed from the ranking and their edge to the
16
23
  * parent is not counted as inbound — inlining them is free, not boundary work.
@@ -18,8 +25,11 @@
18
25
  import type { TfGraph } from "./types.js";
19
26
  export type PeelabilityBand = "clean leaf" | "carvable w/ edits" | "leave in Terraform";
20
27
  export interface PeelabilityBreakdown {
28
+ /** Inbound edges from resource/module blocks — a data-source patch each. */
21
29
  inbound: number;
22
30
  outbound: number;
31
+ /** Inbound edges from `output` blocks — a one-line rewrite each (#1638). */
32
+ outputs: number;
23
33
  tier: 1 | 2 | 3 | null;
24
34
  hasDynamic: boolean;
25
35
  instances: number;
@@ -27,6 +37,7 @@ export interface PeelabilityBreakdown {
27
37
  penalties: {
28
38
  inbound: number;
29
39
  outbound: number;
40
+ outputs: number;
30
41
  tier: number;
31
42
  dynamic: number;
32
43
  instances: number;
@@ -1 +1 @@
1
- {"version":3,"file":"score.d.ts","sourceRoot":"","sources":["../../src/terraform/score.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAU,MAAM,SAAS,CAAC;AAE/C,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,mBAAmB,GAAG,oBAAoB,CAAC;AAExF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,SAAS,EAAE;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,eAAe,CAAC;IACtB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,oBAAoB,CAAC;CACjC;AAID,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAItD;AAiFD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,EAAE,CAMzD"}
1
+ {"version":3,"file":"score.d.ts","sourceRoot":"","sources":["../../src/terraform/score.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAU,MAAM,SAAS,CAAC;AAE/C,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,mBAAmB,GAAG,oBAAoB,CAAC;AAExF,MAAM,WAAW,oBAAoB;IACnC,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,SAAS,EAAE;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,eAAe,CAAC;IACtB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,oBAAoB,CAAC;CACjC;AAID,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAItD;AA6FD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,EAAE,CAMzD"}
@@ -44,6 +44,15 @@ export interface TfNode {
44
44
  export interface TfEdge {
45
45
  from: string;
46
46
  to: string;
47
+ /**
48
+ * The kind of block the reference came from, when it is not one of the
49
+ * graph's own nodes. `"output"` marks a root-module `output` block (#1638):
50
+ * a real dependency — `terraform plan` errors on the dangling reference the
51
+ * moment the carve lands — but a cheaper one to bridge than a resource's,
52
+ * since the patch is a single expression edit in a block that manages
53
+ * nothing. Absent means the reference came from a resource or module node.
54
+ */
55
+ fromKind?: "output";
47
56
  /** The attribute path(s) referenced, e.g. `["id", "arn"]`. */
48
57
  attrs: string[];
49
58
  /**
@@ -68,6 +77,8 @@ export interface Hcl2JsonTree {
68
77
  resource?: Record<string, Record<string, unknown[]>>;
69
78
  module?: Record<string, unknown[]>;
70
79
  data?: Record<string, Record<string, unknown[]>>;
80
+ /** Root-module `output` blocks. Not nodes — they reference, they are not carvable (#1638). */
81
+ output?: Record<string, unknown[]>;
71
82
  [k: string]: unknown;
72
83
  }
73
84
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/terraform/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,+EAA+E;AAC/E,MAAM,WAAW,MAAM;IACrB,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC5B,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,UAAU,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,8DAA8D;IAC9D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;;;OAKG;IACH,GAAG,EAAE,MAAM,EAAE,CAAC;CACf;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/terraform/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,+EAA+E;AAC/E,MAAM,WAAW,MAAM;IACrB,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC5B,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,UAAU,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,8DAA8D;IAC9D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;;;OAKG;IACH,GAAG,EAAE,MAAM,EAAE,CAAC;CACf;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IACjD,8FAA8F;IAC9F,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IACnC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.44.6",
3
+ "version": "0.44.7",
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",
@@ -162,6 +162,38 @@ describe("carveBridge", () => {
162
162
  });
163
163
  });
164
164
 
165
+ test("an output-only dependency is patched, not left dangling (#1638)", async () => {
166
+ if (!parserAvailable) return;
167
+ const dir = mkdtempSync(join(tmpdir(), "chant-bridge-outputs-"));
168
+ try {
169
+ // Nothing but an output reads the bucket. Before outputs entered the
170
+ // graph this produced no data source and no rewrite at all, so the
171
+ // surviving plan broke on the dangling reference at handoff.
172
+ writeFileSync(join(dir, "bucket.tf"), BUCKET_TF);
173
+ writeFileSync(
174
+ join(dir, "outputs.tf"),
175
+ `output "assets_bucket" {\n value = aws_s3_bucket.assets.bucket\n}\n`,
176
+ );
177
+ const out = join(dir, "carveout");
178
+ const res = await carveBridge({ from: dir, select: "aws_s3_bucket.assets", output: out });
179
+ expect(res.ok).toBe(true);
180
+ expect(res.plan!.outputRewrites).toEqual(["output.assets_bucket"]);
181
+
182
+ expect(readFileSync(join(out, "aws_s3_bucket-assets-datasources.tf"), "utf-8")).toContain(
183
+ 'data "aws_s3_bucket" "assets"',
184
+ );
185
+ expect(readFileSync(join(out, "outputs.tf"), "utf-8")).toContain("value = data.aws_s3_bucket.assets.bucket");
186
+
187
+ const patch = readFileSync(res.patchPath!, "utf-8");
188
+ expect(patch).toContain("diff --git a/outputs.tf b/outputs.tf");
189
+ expect(patch).toContain("- value = aws_s3_bucket.assets.bucket");
190
+ expect(patch).toContain("+ value = data.aws_s3_bucket.assets.bucket");
191
+ expect(formatCarveBridge(res)).toContain("1 output block(s) repointed at the data source: output.assets_bucket");
192
+ } finally {
193
+ rmSync(dir, { recursive: true, force: true });
194
+ }
195
+ });
196
+
165
197
  test("formatCarveBridge summarizes data sources, rewires, and safety", async () => {
166
198
  if (!parserAvailable) return;
167
199
  await withEstate(async (dir) => {
@@ -183,6 +183,9 @@ export function formatCarveBridge(result: CarveBridgeResult): string {
183
183
  if (changed.length) {
184
184
  L.push(` ${changed.length} survivor file(s) rewired: ${changed.map((r) => basename(r.path)).join(", ")}`);
185
185
  }
186
+ if (p.outputRewrites.length) {
187
+ L.push(` ${p.outputRewrites.length} output block(s) repointed at the data source: ${p.outputRewrites.join(", ")}`);
188
+ }
186
189
  if (p.deferredInputs.length) {
187
190
  L.push(` ${p.deferredInputs.length} deferred deploy-time input(s) (wired at apply):`);
188
191
  for (const d of p.deferredInputs) L.push(` - ${d.note}`);
@@ -3,6 +3,7 @@ import { mkdtempSync, writeFileSync, rmSync, readFileSync } from "fs";
3
3
  import { tmpdir } from "os";
4
4
  import { join } from "path";
5
5
  import { carveEmit, formatCarveEmit } from "./carve-emit";
6
+ import { lintCommand } from "./lint";
6
7
  import { loadHcl2json } from "../../terraform/parse";
7
8
  import { readCarveManifest } from "../../terraform/manifest";
8
9
  import type { ImportResult, LiveImportOptions } from "./import";
@@ -212,6 +213,112 @@ resource "aws_subnet" "a" {
212
213
  }
213
214
  });
214
215
 
216
+ test("folded sub-resources land in the emitted bucket's properties (#1637)", async () => {
217
+ if (!parserAvailable) return;
218
+ const dir = mkdtempSync(join(tmpdir(), "chant-emit-fold-"));
219
+ try {
220
+ writeFileSync(
221
+ join(dir, "main.tf"),
222
+ `
223
+ resource "aws_s3_bucket" "assets" { bucket = "myapp-assets-prod" }
224
+ resource "aws_s3_bucket_versioning" "assets" {
225
+ bucket = aws_s3_bucket.assets.id
226
+ versioning_configuration { status = "Enabled" }
227
+ }
228
+ resource "aws_s3_bucket_public_access_block" "assets" {
229
+ bucket = aws_s3_bucket.assets.id
230
+ block_public_acls = true
231
+ block_public_policy = true
232
+ ignore_public_acls = true
233
+ restrict_public_buckets = true
234
+ }
235
+ `,
236
+ );
237
+ const attrs = (extra: Record<string, unknown>) => ({ id: "myapp-assets-prod", bucket: "myapp-assets-prod", ...extra });
238
+ writeFileSync(
239
+ join(dir, "terraform.tfstate"),
240
+ JSON.stringify({
241
+ version: 4,
242
+ resources: [
243
+ {
244
+ mode: "managed",
245
+ type: "aws_s3_bucket",
246
+ name: "assets",
247
+ instances: [
248
+ {
249
+ attributes: attrs({
250
+ arn: "arn:aws:s3:::myapp-assets-prod",
251
+ versioning: [{ enabled: true, mfa_delete: false }],
252
+ server_side_encryption_configuration: [
253
+ {
254
+ rule: [
255
+ {
256
+ apply_server_side_encryption_by_default: [{ sse_algorithm: "AES256", kms_master_key_id: "" }],
257
+ bucket_key_enabled: false,
258
+ },
259
+ ],
260
+ },
261
+ ],
262
+ }),
263
+ },
264
+ ],
265
+ },
266
+ {
267
+ mode: "managed",
268
+ type: "aws_s3_bucket_versioning",
269
+ name: "assets",
270
+ instances: [{ attributes: attrs({ versioning_configuration: [{ status: "Enabled", mfa_delete: "" }] }) }],
271
+ },
272
+ {
273
+ mode: "managed",
274
+ type: "aws_s3_bucket_public_access_block",
275
+ name: "assets",
276
+ instances: [
277
+ {
278
+ attributes: attrs({
279
+ block_public_acls: true,
280
+ block_public_policy: true,
281
+ ignore_public_acls: true,
282
+ restrict_public_buckets: true,
283
+ }),
284
+ },
285
+ ],
286
+ },
287
+ ],
288
+ }),
289
+ );
290
+
291
+ const out = join(dir, "carveout");
292
+ const res = await carveEmit(
293
+ { from: dir, select: "aws_s3_bucket.assets", statePath: join(dir, "terraform.tfstate"), output: out },
294
+ { plugins: [], liveImport },
295
+ );
296
+ expect(res.ok).toBe(true);
297
+
298
+ // The fold is applied, not merely announced: the emitted Bucket carries
299
+ // the versioning and public-access block the Terraform declared.
300
+ const emitted = readFileSync(join(out, "src", "assets.ts"), "utf-8");
301
+ expect(emitted).toContain('VersioningConfiguration: {"Status":"Enabled"}');
302
+ expect(emitted).toContain('"BlockPublicAcls":true');
303
+ expect(emitted).toContain('"RestrictPublicBuckets":true');
304
+ expect(emitted).toContain('BucketEncryption: {"ServerSideEncryptionConfiguration"');
305
+
306
+ expect(res.folded).toEqual([
307
+ { address: "aws_s3_bucket_public_access_block.assets", props: ["PublicAccessBlockConfiguration"] },
308
+ { address: "aws_s3_bucket_versioning.assets", props: ["VersioningConfiguration"] },
309
+ ]);
310
+ const text = formatCarveEmit(res);
311
+ expect(text).toContain("aws_s3_bucket_versioning.assets (VersioningConfiguration)");
312
+ expect(text).toContain("aws_s3_bucket_public_access_block.assets (PublicAccessBlockConfiguration)");
313
+
314
+ // And the emitted project still lints clean (no errors).
315
+ const lint = await lintCommand({ path: out, format: "stylish" });
316
+ expect(lint.diagnostics.filter((d) => d.severity === "error")).toEqual([]);
317
+ } finally {
318
+ rmSync(dir, { recursive: true, force: true });
319
+ }
320
+ });
321
+
215
322
  test("without --state or --env it explains both adoption sources", async () => {
216
323
  if (!parserAvailable) return;
217
324
  await withEstate(async (dir) => {
@@ -17,9 +17,15 @@ import { basename, join, resolve } from "path";
17
17
  import { parseTerraformDir, Hcl2JsonNotInstalled } from "../../terraform/parse";
18
18
  import { boundaryReport, deferredParamName, type CarveReport } from "../../terraform/carve";
19
19
  import { resolveTier } from "../../terraform/tier-map";
20
- import { readStateResource } from "../../terraform/state";
20
+ import { readStateResource, type StateResource } from "../../terraform/state";
21
21
  import { writeCarveManifest, type CarveManifest } from "../../terraform/manifest";
22
- import { adoptFromState, canAdoptFromState, supportedStateAdoptionTypes, type DeferredParam } from "../../terraform/adopt-state";
22
+ import {
23
+ adoptFromState,
24
+ canAdoptFromState,
25
+ supportedStateAdoptionTypes,
26
+ type DeferredParam,
27
+ type FoldedContribution,
28
+ } from "../../terraform/adopt-state";
23
29
  import { getChantVersion } from "./init";
24
30
  import type { LexiconPlugin, ResourceSelector } from "../../lexicon";
25
31
  import type { ImportResult, LiveImportOptions } from "./import";
@@ -67,6 +73,8 @@ export interface CarveEmitResult {
67
73
  scaffolded?: string[];
68
74
  /** Deferred outbound inputs declared as build parameters in the emitted project (#998). */
69
75
  params?: DeferredParam[];
76
+ /** Folded sub-resources and the props each joined into the emitted parent (#1637). */
77
+ folded?: FoldedContribution[];
70
78
  /** The persisted carve state manifest bridge/apply compose with. */
71
79
  manifestPath?: string;
72
80
  }
@@ -125,7 +133,13 @@ export async function carveEmit(opts: CarveEmitOptions, deps: CarveEmitDeps): Pr
125
133
  // value the carved block read enters the emitted project as a declared
126
134
  // param, defaulted to the value the state resolved.
127
135
  const params = deferredParams(report, stateResource.attributes);
128
- const adopted = adoptFromState(stateResource, params);
136
+ // The carve set's folded sub-resources come out of the same state file:
137
+ // their mappable attributes belong in the parent's emitted props (#1637).
138
+ const folded = report.carveSet
139
+ .filter((m) => m.foldedInto)
140
+ .map((m) => readStateResource(opts.statePath!, m.address))
141
+ .filter((r): r is StateResource => r !== null);
142
+ const adopted = adoptFromState(stateResource, params, folded);
129
143
  if (!adopted) return { ok: false, error: `Could not adopt ${opts.select} from state.` };
130
144
 
131
145
  // The output dir is a buildable chant project: source in src/, plus the
@@ -139,7 +153,16 @@ export async function carveEmit(opts: CarveEmitOptions, deps: CarveEmitDeps): Pr
139
153
  const scaffolded = scaffoldProject(outDir, lexicon, params);
140
154
 
141
155
  const manifestPath = persistManifest(outDir, opts, report, tfType, "tfstate", [outPath], params);
142
- return { ok: true, report, source: "tfstate", emittedFiles: [outPath], scaffolded, manifestPath, params };
156
+ return {
157
+ ok: true,
158
+ report,
159
+ source: "tfstate",
160
+ emittedFiles: [outPath],
161
+ scaffolded,
162
+ manifestPath,
163
+ params,
164
+ folded: adopted.folded,
165
+ };
143
166
  }
144
167
 
145
168
  // ── Adoption path 2: live import (cloud→code) ──
@@ -335,7 +358,16 @@ export function formatCarveEmit(result: CarveEmitResult): string {
335
358
  lines.push(` State manifest: ${result.manifestPath} — carve bridge/apply pick the target up from here.`);
336
359
  }
337
360
  if (r.carveSet.length > 1) {
338
- const folded = r.carveSet.filter((m) => m.foldedInto).map((m) => m.address);
361
+ // Say what each fold actually contributed — announcing the fold without the
362
+ // properties was the bug behind #1637.
363
+ const contributed = new Map((result.folded ?? []).map((f) => [f.address, f.props]));
364
+ const folded = r.carveSet
365
+ .filter((m) => m.foldedInto)
366
+ .map((m) => {
367
+ const props = contributed.get(m.address);
368
+ if (props === undefined) return m.address;
369
+ return props.length ? `${m.address} (${props.join(", ")})` : `${m.address} (nothing mappable)`;
370
+ });
339
371
  lines.push(` Folded in: ${folded.join(", ")}`);
340
372
  }
341
373
  lines.push("");