@intentius/chant 0.41.20 → 0.44.1

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 (144) hide show
  1. package/dist/build.d.ts +15 -1
  2. package/dist/build.d.ts.map +1 -1
  3. package/dist/cli/commands/build.d.ts.map +1 -1
  4. package/dist/cli/commands/carve-apply.d.ts +9 -0
  5. package/dist/cli/commands/carve-apply.d.ts.map +1 -1
  6. package/dist/cli/commands/carve-bridge.d.ts +7 -0
  7. package/dist/cli/commands/carve-bridge.d.ts.map +1 -1
  8. package/dist/cli/commands/carve-emit.d.ts +7 -0
  9. package/dist/cli/commands/carve-emit.d.ts.map +1 -1
  10. package/dist/cli/commands/init.d.ts +2 -0
  11. package/dist/cli/commands/init.d.ts.map +1 -1
  12. package/dist/cli/handlers/carve-apply.d.ts.map +1 -1
  13. package/dist/cli/handlers/components.d.ts.map +1 -1
  14. package/dist/cli/handlers/explain.d.ts +11 -0
  15. package/dist/cli/handlers/explain.d.ts.map +1 -0
  16. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  17. package/dist/cli/main.d.ts.map +1 -1
  18. package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
  19. package/dist/cli/plugins.d.ts +8 -0
  20. package/dist/cli/plugins.d.ts.map +1 -1
  21. package/dist/cli/registry.d.ts +2 -0
  22. package/dist/cli/registry.d.ts.map +1 -1
  23. package/dist/codegen/docs-rule-scanning.d.ts +11 -0
  24. package/dist/codegen/docs-rule-scanning.d.ts.map +1 -1
  25. package/dist/codegen/okf-lexicon.d.ts +36 -0
  26. package/dist/codegen/okf-lexicon.d.ts.map +1 -0
  27. package/dist/codegen/package.d.ts +2 -1
  28. package/dist/codegen/package.d.ts.map +1 -1
  29. package/dist/components/deploy-units.d.ts.map +1 -1
  30. package/dist/components/verbs/sbom.d.ts +1 -1
  31. package/dist/components/verbs/sbom.d.ts.map +1 -1
  32. package/dist/components/verbs/vuln-gate.d.ts +15 -4
  33. package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
  34. package/dist/components/verbs/vuln-scan.d.ts +18 -6
  35. package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
  36. package/dist/config.d.ts +12 -0
  37. package/dist/config.d.ts.map +1 -1
  38. package/dist/deep-observation.d.ts +13 -0
  39. package/dist/deep-observation.d.ts.map +1 -1
  40. package/dist/lexicon.d.ts +47 -0
  41. package/dist/lexicon.d.ts.map +1 -1
  42. package/dist/lifecycle/observe.d.ts.map +1 -1
  43. package/dist/lifecycle/snapshot.d.ts.map +1 -1
  44. package/dist/okf.d.ts +57 -0
  45. package/dist/okf.d.ts.map +1 -0
  46. package/dist/terraform/__fixtures__/build-graph.d.ts +13 -0
  47. package/dist/terraform/__fixtures__/build-graph.d.ts.map +1 -0
  48. package/dist/terraform/adopt-state.d.ts +33 -1
  49. package/dist/terraform/adopt-state.d.ts.map +1 -1
  50. package/dist/terraform/aws-resources.d.ts.map +1 -1
  51. package/dist/terraform/bridge.d.ts +7 -1
  52. package/dist/terraform/bridge.d.ts.map +1 -1
  53. package/dist/terraform/carve.d.ts +12 -0
  54. package/dist/terraform/carve.d.ts.map +1 -1
  55. package/dist/terraform/excise.d.ts +31 -0
  56. package/dist/terraform/excise.d.ts.map +1 -0
  57. package/dist/terraform/graduate.d.ts +12 -0
  58. package/dist/terraform/graduate.d.ts.map +1 -1
  59. package/dist/terraform/graph.d.ts +35 -5
  60. package/dist/terraform/graph.d.ts.map +1 -1
  61. package/dist/terraform/manifest.d.ts +89 -0
  62. package/dist/terraform/manifest.d.ts.map +1 -0
  63. package/dist/terraform/parse.d.ts +9 -4
  64. package/dist/terraform/parse.d.ts.map +1 -1
  65. package/dist/terraform/tier-map.d.ts +4 -3
  66. package/dist/terraform/tier-map.d.ts.map +1 -1
  67. package/dist/terraform/types.d.ts +7 -0
  68. package/dist/terraform/types.d.ts.map +1 -1
  69. package/dist/terraform/unified-diff.d.ts +11 -0
  70. package/dist/terraform/unified-diff.d.ts.map +1 -0
  71. package/package.json +1 -1
  72. package/src/__snapshots__/okf.test.ts.snap +381 -0
  73. package/src/audit/rules-doc.ts +1 -1
  74. package/src/build.test.ts +95 -0
  75. package/src/build.ts +46 -1
  76. package/src/cli/commands/build.ts +9 -1
  77. package/src/cli/commands/carve-apply.test.ts +94 -2
  78. package/src/cli/commands/carve-apply.ts +62 -11
  79. package/src/cli/commands/carve-bridge.test.ts +82 -4
  80. package/src/cli/commands/carve-bridge.ts +79 -11
  81. package/src/cli/commands/carve-emit-state.test.ts +110 -1
  82. package/src/cli/commands/carve-emit.ts +184 -8
  83. package/src/cli/commands/init.ts +1 -1
  84. package/src/cli/handlers/carve-apply.ts +1 -0
  85. package/src/cli/handlers/components.ts +4 -2
  86. package/src/cli/handlers/explain.test.ts +93 -0
  87. package/src/cli/handlers/explain.ts +60 -0
  88. package/src/cli/handlers/lifecycle.ts +16 -9
  89. package/src/cli/main.ts +21 -5
  90. package/src/cli/mcp/server.test.ts +17 -0
  91. package/src/cli/mcp/tools/explain.ts +15 -3
  92. package/src/cli/plugins.ts +17 -0
  93. package/src/cli/registry.ts +2 -0
  94. package/src/codegen/__snapshots__/okf-lexicon.test.ts.snap +170 -0
  95. package/src/codegen/docs-rule-scanning.ts +35 -14
  96. package/src/codegen/okf-lexicon.test.ts +248 -0
  97. package/src/codegen/okf-lexicon.ts +303 -0
  98. package/src/codegen/package.ts +29 -4
  99. package/src/components/config-defaults.test.ts +30 -0
  100. package/src/components/deploy-units.test.ts +13 -0
  101. package/src/components/deploy-units.ts +5 -0
  102. package/src/components/verbs/__fixtures__/grype-with-kev-epss.json +1 -0
  103. package/src/components/verbs/__fixtures__/trivy-with-kev-epss.json +631 -0
  104. package/src/components/verbs/exploitability-roundtrip.test.ts +190 -0
  105. package/src/components/verbs/sbom.ts +1 -1
  106. package/src/components/verbs/vuln-gate.test.ts +144 -1
  107. package/src/components/verbs/vuln-gate.ts +61 -10
  108. package/src/components/verbs/vuln-scan.test.ts +94 -0
  109. package/src/components/verbs/vuln-scan.ts +44 -9
  110. package/src/config.test.ts +21 -0
  111. package/src/config.ts +18 -0
  112. package/src/deep-observation.test.ts +19 -0
  113. package/src/deep-observation.ts +32 -13
  114. package/src/lexicon.ts +50 -0
  115. package/src/lifecycle/observe.test.ts +18 -0
  116. package/src/lifecycle/observe.ts +10 -3
  117. package/src/lifecycle/snapshot.ts +5 -4
  118. package/src/okf.test.ts +169 -0
  119. package/src/okf.ts +308 -0
  120. package/src/terraform/__fixtures__/build-graph.ts +25 -0
  121. package/src/terraform/adopt-state.test.ts +34 -1
  122. package/src/terraform/adopt-state.ts +69 -2
  123. package/src/terraform/aws-resources.test.ts +52 -2
  124. package/src/terraform/aws-resources.ts +95 -4
  125. package/src/terraform/bridge.test.ts +38 -14
  126. package/src/terraform/bridge.ts +48 -14
  127. package/src/terraform/carve.test.ts +9 -7
  128. package/src/terraform/carve.ts +0 -0
  129. package/src/terraform/excise.test.ts +92 -0
  130. package/src/terraform/excise.ts +139 -0
  131. package/src/terraform/graduate.test.ts +73 -6
  132. package/src/terraform/graduate.ts +49 -0
  133. package/src/terraform/graph.test.ts +73 -10
  134. package/src/terraform/graph.ts +124 -47
  135. package/src/terraform/manifest.test.ts +105 -0
  136. package/src/terraform/manifest.ts +162 -0
  137. package/src/terraform/parse.test.ts +39 -1
  138. package/src/terraform/parse.ts +36 -9
  139. package/src/terraform/score.test.ts +8 -8
  140. package/src/terraform/state.test.ts +4 -4
  141. package/src/terraform/tier-map.ts +60 -8
  142. package/src/terraform/types.ts +7 -0
  143. package/src/terraform/unified-diff.test.ts +77 -0
  144. package/src/terraform/unified-diff.ts +146 -0
@@ -8,8 +8,14 @@
8
8
  * advisor fails with a one-line install hint.
9
9
  */
10
10
  import type { Hcl2JsonTree, TfGraph } from "./types.js";
11
- /** Minimal shape of the parser export we depend on. */
12
- type Hcl2JsonParse = (filename: string, hcl: string) => Promise<Hcl2JsonTree>;
11
+ /** Minimal shape of the parser exports we depend on. */
12
+ export interface Hcl2Json {
13
+ parse: (filename: string, hcl: string) => Promise<Hcl2JsonTree>;
14
+ /** Expression-AST reference extraction (#998) — one traversal accessor per reference. */
15
+ getReferencesInExpression: (filename: string, expression: string) => Promise<Array<{
16
+ value: string;
17
+ }>>;
18
+ }
13
19
  export declare class Hcl2JsonNotInstalled extends Error {
14
20
  constructor(cause: unknown);
15
21
  }
@@ -17,7 +23,7 @@ export declare class Hcl2JsonNotInstalled extends Error {
17
23
  * Lazy-load the optional HCL parser. Throws `Hcl2JsonNotInstalled` with an
18
24
  * install hint when the package is missing, rather than a raw MODULE_NOT_FOUND.
19
25
  */
20
- export declare function loadHcl2json(): Promise<Hcl2JsonParse>;
26
+ export declare function loadHcl2json(): Promise<Hcl2Json>;
21
27
  export interface ParseTerraformOptions {
22
28
  /** Opt-in `.tfstate` path (#214 T2): overlays state-expanded instance counts. */
23
29
  statePath?: string;
@@ -29,5 +35,4 @@ export interface ParseTerraformOptions {
29
35
  * instance counts become accurate (see `state.ts`).
30
36
  */
31
37
  export declare function parseTerraformDir(dir: string, opts?: ParseTerraformOptions): Promise<TfGraph>;
32
- export {};
33
38
  //# sourceMappingURL=parse.d.ts.map
@@ -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,uDAAuD;AACvD,KAAK,aAAa,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAE9E,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,KAAK,EAAE,OAAO;CAQ3B;AAED;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,aAAa,CAAC,CAO3D;AAuBD,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;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"}
@@ -29,9 +29,10 @@ export declare const TIER_MAP: Record<string, TierInfo>;
29
29
  */
30
30
  export declare const FOLDS_INTO: Record<string, string>;
31
31
  /**
32
- * The HCL attribute carrying a resource's physical name, per type — derived
33
- * from the AWS carve-out table. Used for the graph's identity and the
34
- * live-import hint. Absent fall back to the TF logical name.
32
+ * The HCL attribute carrying a resource's physical name, per type — AWS entries
33
+ * derived from the carve-out table, non-AWS listed directly. A dotted entry is
34
+ * a path into nested blocks (`manifest.metadata.name`). Used for the graph's
35
+ * identity and the live-import hint. Absent → fall back to the TF logical name.
35
36
  */
36
37
  export declare const IDENTITY_ATTR: Record<string, string>;
37
38
  export declare function resolveTier(tfType: string): TierInfo | null;
@@ -1 +1 @@
1
- {"version":3,"file":"tier-map.d.ts","sourceRoot":"","sources":["../../src/terraform/tier-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,sEAAsE;AACtE,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAK7C,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAO7C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAEhD,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAE3D"}
1
+ {"version":3,"file":"tier-map.d.ts","sourceRoot":"","sources":["../../src/terraform/tier-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;CAChB;AA0CD,sEAAsE;AACtE,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAK7C,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAiB7C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGhD,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAE3D"}
@@ -46,6 +46,13 @@ export interface TfEdge {
46
46
  to: string;
47
47
  /** The attribute path(s) referenced, e.g. `["id", "arn"]`. */
48
48
  attrs: string[];
49
+ /**
50
+ * The referring block's own top-level attribute(s) the reference sits in,
51
+ * e.g. `["vpc_id"]` for a subnet reading its VPC (#998). This is what a
52
+ * deferred outbound input is named after when emit turns it into a build
53
+ * parameter. Empty when the reference came from a non-object block shape.
54
+ */
55
+ via: string[];
49
56
  }
50
57
  export interface TfGraph {
51
58
  nodes: TfNode[];
@@ -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;CACjB;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,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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Minimal unified-diff generator for the carve bridge patch (#998). Emits
3
+ * git-applyable hunks (`git apply`, or plain `patch -p1`) so the whole survivor
4
+ * edit — data sources plus rewired references — lands in one reviewable file.
5
+ * LCS-based; bridge inputs are hand-sized `.tf` files, so O(n·m) is fine.
6
+ */
7
+ /** One file's diff, `a/<path>` → `b/<path>`, with git headers. Empty when unchanged. */
8
+ export declare function unifiedDiff(path: string, oldText: string, newText: string, context?: number): string;
9
+ /** A new-file diff (`/dev/null` → `b/<path>`), git-applyable. */
10
+ export declare function newFileDiff(path: string, text: string): string;
11
+ //# sourceMappingURL=unified-diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unified-diff.d.ts","sourceRoot":"","sources":["../../src/terraform/unified-diff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,SAAI,GAAG,MAAM,CA0B/F;AAED,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAa9D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.41.20",
3
+ "version": "0.44.1",
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",
@@ -0,0 +1,381 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`buildOkfBundle over shipped examples > aws lambda-s3 emits a conformant, snapshot-stable bundle 1`] = `
4
+ [
5
+ {
6
+ "content": "---
7
+ type: AWS::S3::Bucket
8
+ title: appBucket
9
+ description: aws resource of type AWS::S3::Bucket
10
+ name: appBucket
11
+ lexicon: aws
12
+ kind: resource
13
+ source: main.ts
14
+ composite: LambdaS3
15
+ composite_instance: app
16
+ ---
17
+ Declared in \`main.ts\`.
18
+
19
+ ## Referenced by
20
+
21
+ - [appFunc](/aws/appFunc.md)
22
+ - [appRole](/aws/appRole.md)
23
+ ",
24
+ "path": "aws/appBucket.md",
25
+ },
26
+ {
27
+ "content": "---
28
+ type: AWS::Lambda::Function
29
+ title: appFunc
30
+ description: aws resource of type AWS::Lambda::Function
31
+ name: appFunc
32
+ lexicon: aws
33
+ kind: resource
34
+ source: main.ts
35
+ composite: LambdaS3
36
+ composite_instance: app
37
+ ---
38
+ Declared in \`main.ts\`.
39
+
40
+ ## Depends on
41
+
42
+ - [appBucket](/aws/appBucket.md)
43
+ - [appRole](/aws/appRole.md)
44
+ ",
45
+ "path": "aws/appFunc.md",
46
+ },
47
+ {
48
+ "content": "---
49
+ type: AWS::IAM::Role
50
+ title: appRole
51
+ description: aws resource of type AWS::IAM::Role
52
+ name: appRole
53
+ lexicon: aws
54
+ kind: resource
55
+ source: main.ts
56
+ composite: LambdaS3
57
+ composite_instance: app
58
+ ---
59
+ Declared in \`main.ts\`.
60
+
61
+ ## Depends on
62
+
63
+ - [appBucket](/aws/appBucket.md)
64
+
65
+ ## Referenced by
66
+
67
+ - [appFunc](/aws/appFunc.md)
68
+ ",
69
+ "path": "aws/appRole.md",
70
+ },
71
+ {
72
+ "content": "---
73
+ type: Output
74
+ title: bucketArn
75
+ description: aws output of type Output
76
+ name: bucketArn
77
+ lexicon: aws
78
+ kind: output
79
+ source: outputs.ts
80
+ ---
81
+ Declared in \`outputs.ts\`.
82
+ ",
83
+ "path": "aws/bucketArn.md",
84
+ },
85
+ {
86
+ "content": "---
87
+ type: AWS::CloudFormation::Parameter
88
+ title: environment
89
+ description: aws resource of type AWS::CloudFormation::Parameter
90
+ name: environment
91
+ lexicon: aws
92
+ kind: resource
93
+ source: params.ts
94
+ ---
95
+ Declared in \`params.ts\`.
96
+ ",
97
+ "path": "aws/environment.md",
98
+ },
99
+ {
100
+ "content": "---
101
+ type: chant:aws:defaultTags
102
+ title: tags
103
+ description: aws resource of type chant:aws:defaultTags
104
+ name: tags
105
+ lexicon: aws
106
+ kind: resource
107
+ source: tags.ts
108
+ ---
109
+ Declared in \`tags.ts\`.
110
+ ",
111
+ "path": "aws/tags.md",
112
+ },
113
+ {
114
+ "content": "---
115
+ okf_version: '0.2'
116
+ ---
117
+
118
+ # Lexicon: aws
119
+
120
+ * [appBucket](/aws/appBucket.md) - aws resource of type AWS::S3::Bucket
121
+ * [appFunc](/aws/appFunc.md) - aws resource of type AWS::Lambda::Function
122
+ * [appRole](/aws/appRole.md) - aws resource of type AWS::IAM::Role
123
+ * [bucketArn](/aws/bucketArn.md) - aws output of type Output
124
+ * [environment](/aws/environment.md) - aws resource of type AWS::CloudFormation::Parameter
125
+ * [tags](/aws/tags.md) - aws resource of type chant:aws:defaultTags
126
+ ",
127
+ "path": "index.md",
128
+ },
129
+ ]
130
+ `;
131
+
132
+ exports[`buildOkfBundle over shipped examples > gitlab node-pipeline emits a conformant, snapshot-stable bundle 1`] = `
133
+ [
134
+ {
135
+ "content": "---
136
+ type: GitLab::CI::Job
137
+ title: appBuild
138
+ description: gitlab resource of type GitLab::CI::Job
139
+ name: appBuild
140
+ lexicon: gitlab
141
+ kind: resource
142
+ source: pipeline.ts
143
+ composite: NodePipeline
144
+ composite_instance: app
145
+ ---
146
+ Declared in \`pipeline.ts\`.
147
+ ",
148
+ "path": "gitlab/appBuild.md",
149
+ },
150
+ {
151
+ "content": "---
152
+ type: GitLab::CI::Default
153
+ title: appDefaults
154
+ description: gitlab resource of type GitLab::CI::Default
155
+ name: appDefaults
156
+ lexicon: gitlab
157
+ kind: resource
158
+ source: pipeline.ts
159
+ composite: NodePipeline
160
+ composite_instance: app
161
+ ---
162
+ Declared in \`pipeline.ts\`.
163
+ ",
164
+ "path": "gitlab/appDefaults.md",
165
+ },
166
+ {
167
+ "content": "---
168
+ type: GitLab::CI::Job
169
+ title: appTest
170
+ description: gitlab resource of type GitLab::CI::Job
171
+ name: appTest
172
+ lexicon: gitlab
173
+ kind: resource
174
+ source: pipeline.ts
175
+ composite: NodePipeline
176
+ composite_instance: app
177
+ ---
178
+ Declared in \`pipeline.ts\`.
179
+ ",
180
+ "path": "gitlab/appTest.md",
181
+ },
182
+ {
183
+ "content": "---
184
+ okf_version: '0.2'
185
+ ---
186
+
187
+ # Lexicon: gitlab
188
+
189
+ * [appBuild](/gitlab/appBuild.md) - gitlab resource of type GitLab::CI::Job
190
+ * [appDefaults](/gitlab/appDefaults.md) - gitlab resource of type GitLab::CI::Default
191
+ * [appTest](/gitlab/appTest.md) - gitlab resource of type GitLab::CI::Job
192
+ ",
193
+ "path": "index.md",
194
+ },
195
+ ]
196
+ `;
197
+
198
+ exports[`buildOkfBundle over shipped examples > k8s layered-config emits a conformant, snapshot-stable bundle 1`] = `
199
+ [
200
+ {
201
+ "content": "---
202
+ okf_version: '0.2'
203
+ ---
204
+
205
+ # Lexicon: k8s
206
+
207
+ * [devAppDeployment](/k8s/devAppDeployment.md) - k8s resource of type K8s::Apps::Deployment
208
+ * [devAppService](/k8s/devAppService.md) - k8s resource of type K8s::Core::Service
209
+ * [prodAppDeployment](/k8s/prodAppDeployment.md) - k8s resource of type K8s::Apps::Deployment
210
+ * [prodAppIngress](/k8s/prodAppIngress.md) - k8s resource of type K8s::Networking::Ingress
211
+ * [prodAppPdb](/k8s/prodAppPdb.md) - k8s resource of type K8s::Policy::PodDisruptionBudget
212
+ * [prodAppService](/k8s/prodAppService.md) - k8s resource of type K8s::Core::Service
213
+ * [stagingAppDeployment](/k8s/stagingAppDeployment.md) - k8s resource of type K8s::Apps::Deployment
214
+ * [stagingAppIngress](/k8s/stagingAppIngress.md) - k8s resource of type K8s::Networking::Ingress
215
+ * [stagingAppPdb](/k8s/stagingAppPdb.md) - k8s resource of type K8s::Policy::PodDisruptionBudget
216
+ * [stagingAppService](/k8s/stagingAppService.md) - k8s resource of type K8s::Core::Service
217
+ ",
218
+ "path": "index.md",
219
+ },
220
+ {
221
+ "content": "---
222
+ type: K8s::Apps::Deployment
223
+ title: devAppDeployment
224
+ description: k8s resource of type K8s::Apps::Deployment
225
+ name: devAppDeployment
226
+ lexicon: k8s
227
+ kind: resource
228
+ source: web.ts
229
+ composite: WebApp
230
+ composite_instance: devApp
231
+ ---
232
+ Declared in \`web.ts\`.
233
+ ",
234
+ "path": "k8s/devAppDeployment.md",
235
+ },
236
+ {
237
+ "content": "---
238
+ type: K8s::Core::Service
239
+ title: devAppService
240
+ description: k8s resource of type K8s::Core::Service
241
+ name: devAppService
242
+ lexicon: k8s
243
+ kind: resource
244
+ source: web.ts
245
+ composite: WebApp
246
+ composite_instance: devApp
247
+ ---
248
+ Declared in \`web.ts\`.
249
+ ",
250
+ "path": "k8s/devAppService.md",
251
+ },
252
+ {
253
+ "content": "---
254
+ type: K8s::Apps::Deployment
255
+ title: prodAppDeployment
256
+ description: k8s resource of type K8s::Apps::Deployment
257
+ name: prodAppDeployment
258
+ lexicon: k8s
259
+ kind: resource
260
+ source: web.ts
261
+ composite: WebApp
262
+ composite_instance: prodApp
263
+ ---
264
+ Declared in \`web.ts\`.
265
+ ",
266
+ "path": "k8s/prodAppDeployment.md",
267
+ },
268
+ {
269
+ "content": "---
270
+ type: K8s::Networking::Ingress
271
+ title: prodAppIngress
272
+ description: k8s resource of type K8s::Networking::Ingress
273
+ name: prodAppIngress
274
+ lexicon: k8s
275
+ kind: resource
276
+ source: web.ts
277
+ composite: WebApp
278
+ composite_instance: prodApp
279
+ ---
280
+ Declared in \`web.ts\`.
281
+ ",
282
+ "path": "k8s/prodAppIngress.md",
283
+ },
284
+ {
285
+ "content": "---
286
+ type: K8s::Policy::PodDisruptionBudget
287
+ title: prodAppPdb
288
+ description: k8s resource of type K8s::Policy::PodDisruptionBudget
289
+ name: prodAppPdb
290
+ lexicon: k8s
291
+ kind: resource
292
+ source: web.ts
293
+ composite: WebApp
294
+ composite_instance: prodApp
295
+ ---
296
+ Declared in \`web.ts\`.
297
+ ",
298
+ "path": "k8s/prodAppPdb.md",
299
+ },
300
+ {
301
+ "content": "---
302
+ type: K8s::Core::Service
303
+ title: prodAppService
304
+ description: k8s resource of type K8s::Core::Service
305
+ name: prodAppService
306
+ lexicon: k8s
307
+ kind: resource
308
+ source: web.ts
309
+ composite: WebApp
310
+ composite_instance: prodApp
311
+ ---
312
+ Declared in \`web.ts\`.
313
+ ",
314
+ "path": "k8s/prodAppService.md",
315
+ },
316
+ {
317
+ "content": "---
318
+ type: K8s::Apps::Deployment
319
+ title: stagingAppDeployment
320
+ description: k8s resource of type K8s::Apps::Deployment
321
+ name: stagingAppDeployment
322
+ lexicon: k8s
323
+ kind: resource
324
+ source: web.ts
325
+ composite: WebApp
326
+ composite_instance: stagingApp
327
+ ---
328
+ Declared in \`web.ts\`.
329
+ ",
330
+ "path": "k8s/stagingAppDeployment.md",
331
+ },
332
+ {
333
+ "content": "---
334
+ type: K8s::Networking::Ingress
335
+ title: stagingAppIngress
336
+ description: k8s resource of type K8s::Networking::Ingress
337
+ name: stagingAppIngress
338
+ lexicon: k8s
339
+ kind: resource
340
+ source: web.ts
341
+ composite: WebApp
342
+ composite_instance: stagingApp
343
+ ---
344
+ Declared in \`web.ts\`.
345
+ ",
346
+ "path": "k8s/stagingAppIngress.md",
347
+ },
348
+ {
349
+ "content": "---
350
+ type: K8s::Policy::PodDisruptionBudget
351
+ title: stagingAppPdb
352
+ description: k8s resource of type K8s::Policy::PodDisruptionBudget
353
+ name: stagingAppPdb
354
+ lexicon: k8s
355
+ kind: resource
356
+ source: web.ts
357
+ composite: WebApp
358
+ composite_instance: stagingApp
359
+ ---
360
+ Declared in \`web.ts\`.
361
+ ",
362
+ "path": "k8s/stagingAppPdb.md",
363
+ },
364
+ {
365
+ "content": "---
366
+ type: K8s::Core::Service
367
+ title: stagingAppService
368
+ description: k8s resource of type K8s::Core::Service
369
+ name: stagingAppService
370
+ lexicon: k8s
371
+ kind: resource
372
+ source: web.ts
373
+ composite: WebApp
374
+ composite_instance: stagingApp
375
+ ---
376
+ Declared in \`web.ts\`.
377
+ ",
378
+ "path": "k8s/stagingAppService.md",
379
+ },
380
+ ]
381
+ `;
@@ -14,7 +14,7 @@ const GROUPS: Array<{ heading: string; prefixes: string[]; blurb: string }> = [
14
14
  { heading: "GitHub Actions (GHA)", prefixes: ["GHA"], blurb: "Also applied to Forgejo workflows, which are GitHub-dialect." },
15
15
  { heading: "GitLab CI (WGL)", prefixes: ["WGL"], blurb: "" },
16
16
  { heading: "Forgejo (WFJ)", prefixes: ["WFJ"], blurb: "" },
17
- { heading: "Kubernetes (WK8 / ARGO)", prefixes: ["WK8", "ARGO"], blurb: "Run against Kubernetes manifests." },
17
+ { heading: "Kubernetes (WK8 / ARGO / FLUX)", prefixes: ["WK8", "ARGO", "FLUX"], blurb: "Run against Kubernetes manifests." },
18
18
  { heading: "Docker (DKRD)", prefixes: ["DKRD"], blurb: "Run against Dockerfiles and Compose files." },
19
19
  { heading: "AWS CloudFormation (WAW / COR / EXT)", prefixes: ["WAW", "COR", "EXT"], blurb: "Run against CloudFormation templates (JSON or YAML)." },
20
20
  { heading: "Azure ARM (AZR)", prefixes: ["AZR"], blurb: "Run against ARM deployment templates (JSON)." },
package/src/build.test.ts CHANGED
@@ -257,6 +257,101 @@ export const testEntity = {
257
257
  });
258
258
  });
259
259
 
260
+ describe("build-root contributors (#1548 piece 3)", () => {
261
+ let testDir: string;
262
+
263
+ beforeEach(async () => {
264
+ testDir = join(tmpdir(), `chant-buildroots-test-${Date.now()}-${Math.random()}`);
265
+ await mkdir(testDir, { recursive: true });
266
+ });
267
+
268
+ afterEach(async () => {
269
+ await rm(testDir, { recursive: true, force: true });
270
+ });
271
+
272
+ const entity = (entityType: string): Declarable =>
273
+ ({
274
+ lexicon: "test",
275
+ entityType,
276
+ kind: "resource",
277
+ props: {},
278
+ [DECLARABLE_MARKER]: true,
279
+ }) as unknown as Declarable;
280
+
281
+ const recordingSerializer = (seen: string[][]): Serializer => ({
282
+ name: "test",
283
+ rulePrefix: "TEST",
284
+ serialize: (entities) => {
285
+ seen.push([...entities.keys()]);
286
+ return "out";
287
+ },
288
+ });
289
+
290
+ test("contributed entities join the entity set and reach the serializer", async () => {
291
+ const seen: string[][] = [];
292
+ const result = await build(testDir, [recordingSerializer(seen)], undefined, {
293
+ buildRoots: [
294
+ async () => ({
295
+ entities: new Map([["overlays/prod/deploymentApp", entity("TestEntity")]]),
296
+ warnings: ["rendered a stray doc"],
297
+ }),
298
+ ],
299
+ });
300
+
301
+ expect(result.errors).toEqual([]);
302
+ expect(result.entities.has("overlays/prod/deploymentApp")).toBe(true);
303
+ expect(seen).toEqual([["overlays/prod/deploymentApp"]]);
304
+ expect(result.warnings).toContain("rendered a stray doc");
305
+ });
306
+
307
+ test("a contributor that throws becomes a build error carrying its message, not a thrown stack", async () => {
308
+ const result = await build(testDir, [], undefined, {
309
+ buildRoots: [
310
+ async () => {
311
+ throw new Error("neither the `kustomize` binary nor `kubectl` was found on PATH");
312
+ },
313
+ ],
314
+ });
315
+
316
+ expect(result.errors).toHaveLength(1);
317
+ expect(result.errors[0].message).toContain("neither the `kustomize` binary nor `kubectl`");
318
+ });
319
+
320
+ test("a contributed name colliding with a discovered entity is a build error, never an overwrite", async () => {
321
+ await writeFile(
322
+ join(testDir, "app.infra.ts"),
323
+ `
324
+ export const discovered = {
325
+ lexicon: "test",
326
+ entityType: "Discovered",
327
+ props: {},
328
+ [Symbol.for("chant.declarable")]: true,
329
+ };
330
+ `,
331
+ );
332
+
333
+ const result = await build(testDir, [recordingSerializer([])], undefined, {
334
+ buildRoots: [async () => ({ entities: new Map([["discovered", entity("Contributed")]]) })],
335
+ });
336
+
337
+ expect(result.errors.some((e) => e.message.includes('"discovered" collides'))).toBe(true);
338
+ expect(result.entities.get("discovered")?.entityType).toBe("Discovered");
339
+ });
340
+
341
+ test("contributors run exactly once per build", async () => {
342
+ let calls = 0;
343
+ await build(testDir, [], undefined, {
344
+ buildRoots: [
345
+ async () => {
346
+ calls++;
347
+ return { entities: new Map() };
348
+ },
349
+ ],
350
+ });
351
+ expect(calls).toBe(1);
352
+ });
353
+ });
354
+
260
355
  describe("partitionByLexicon", () => {
261
356
  test("partitions entities by lexicon", () => {
262
357
  const entities = new Map<string, Declarable>([
package/src/build.ts CHANGED
@@ -2,7 +2,7 @@ import type { Declarable } from "./declarable";
2
2
  import type { Serializer, SerializerResult } from "./serializer";
3
3
  import type { OwnershipMarker } from "./ownership";
4
4
  import type { BuildError, DiscoveryErrorType } from "./errors";
5
- import type { IntrinsicDef } from "./lexicon";
5
+ import type { IntrinsicDef, BuildRootContribution } from "./lexicon";
6
6
  import type { BuildParamProvenance } from "./provenance";
7
7
  import { DiscoveryError, BuildError as BuildErrorClass } from "./errors";
8
8
  import { LexiconOutput, isLexiconOutput } from "./lexicon-output";
@@ -215,6 +215,21 @@ export interface BuildOptions {
215
215
  * carries the already-resolved records for provenance.
216
216
  */
217
217
  buildParams?: BuildParamProvenance[];
218
+
219
+ /**
220
+ * chant #1548 piece 3 — lexicon-contributed build roots: closures the CLI
221
+ * binds from each configured plugin's `buildRoots(ctx)` hook (see
222
+ * `collectBuildRootContributors` in ./cli/plugins.ts), each rendering a
223
+ * non-chant-source root (a kustomize overlay dir) into entities. Run once,
224
+ * at the TOP-LEVEL build only (never repeated for nested child projects),
225
+ * after discovery and before partitioning — so contributed entities are
226
+ * serialized, ownership-stamped, post-synth-checked and observed exactly
227
+ * like discovered ones. A contributor that throws becomes a build error
228
+ * carrying its message (the k8s hook's missing-binary refusal names the
229
+ * binaries); a contributed name colliding with a discovered entity is a
230
+ * build error, never a silent overwrite.
231
+ */
232
+ buildRoots?: Array<() => Promise<BuildRootContribution>>;
218
233
  }
219
234
 
220
235
  export interface BuildResult {
@@ -657,6 +672,36 @@ async function buildFromDiscoveryResult(
657
672
  }
658
673
  }
659
674
 
675
+ // Step 4b (#1548 piece 3): lexicon-contributed build roots — rendered
676
+ // non-source roots (kustomize dirs) joining the entity set before
677
+ // partitioning, so everything downstream (serialization with ownership
678
+ // stamping, post-synth checks, observation) treats them as declared.
679
+ // Top-level build only: nested child builds receive the same options
680
+ // object, and re-running the contributors there would duplicate every
681
+ // contributed entity once per child.
682
+ if (!parentBuildStack && options?.buildRoots) {
683
+ for (const contribute of options.buildRoots) {
684
+ try {
685
+ const contribution = await contribute();
686
+ for (const w of contribution.warnings ?? []) warnings.push(w);
687
+ for (const [name, entity] of contribution.entities) {
688
+ if (discoveryResult.entities.has(name)) {
689
+ errors.push(
690
+ new BuildErrorClass(
691
+ "",
692
+ `Build-root entity "${name}" collides with a discovered entity of the same name`,
693
+ ),
694
+ );
695
+ continue;
696
+ }
697
+ discoveryResult.entities.set(name, entity);
698
+ }
699
+ } catch (error) {
700
+ errors.push(new BuildErrorClass("", error instanceof Error ? error.message : String(error)));
701
+ }
702
+ }
703
+ }
704
+
660
705
  // Step 5: Partition entities by lexicon
661
706
  const partitions = partitionByLexicon(discoveryResult.entities);
662
707