@intentius/chant 0.45.0 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/dist/audit/core.d.ts +21 -3
  2. package/dist/audit/core.d.ts.map +1 -1
  3. package/dist/audit/discover.d.ts +3 -2
  4. package/dist/audit/discover.d.ts.map +1 -1
  5. package/dist/audit/rules-doc.d.ts.map +1 -1
  6. package/dist/build.d.ts +3 -3
  7. package/dist/build.d.ts.map +1 -1
  8. package/dist/cli/commands/build.d.ts.map +1 -1
  9. package/dist/cli/commands/check-lexicon.d.ts +14 -0
  10. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  11. package/dist/cli/commands/lexicon-surface-diff.d.ts +6 -0
  12. package/dist/cli/commands/lexicon-surface-diff.d.ts.map +1 -1
  13. package/dist/cli/commands/lint.d.ts.map +1 -1
  14. package/dist/cli/handlers/lifecycle.d.ts +13 -0
  15. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  16. package/dist/cli/handlers/search.d.ts.map +1 -1
  17. package/dist/cli/main.d.ts.map +1 -1
  18. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  19. package/dist/cli/mcp/tools/explain.d.ts +6 -0
  20. package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
  21. package/dist/cli/plugins.d.ts +1 -1
  22. package/dist/cli/plugins.d.ts.map +1 -1
  23. package/dist/cli/registry.d.ts +7 -0
  24. package/dist/cli/registry.d.ts.map +1 -1
  25. package/dist/cli/reporters/stylish.d.ts +15 -1
  26. package/dist/cli/reporters/stylish.d.ts.map +1 -1
  27. package/dist/codegen/lexicon-regen.d.ts +11 -0
  28. package/dist/codegen/lexicon-regen.d.ts.map +1 -1
  29. package/dist/codegen/validate.d.ts +10 -0
  30. package/dist/codegen/validate.d.ts.map +1 -1
  31. package/dist/components/auto-release.d.ts +4 -0
  32. package/dist/components/auto-release.d.ts.map +1 -1
  33. package/dist/components/starter-plugin.d.ts +2 -0
  34. package/dist/components/starter-plugin.d.ts.map +1 -1
  35. package/dist/components/verbs/ensure-secret.d.ts +50 -0
  36. package/dist/components/verbs/ensure-secret.d.ts.map +1 -0
  37. package/dist/components/verbs/index.d.ts +8 -0
  38. package/dist/components/verbs/index.d.ts.map +1 -1
  39. package/dist/components/verbs/r2-sync.d.ts +76 -0
  40. package/dist/components/verbs/r2-sync.d.ts.map +1 -0
  41. package/dist/components/verbs/wrangler.d.ts +108 -0
  42. package/dist/components/verbs/wrangler.d.ts.map +1 -0
  43. package/dist/config.d.ts +54 -0
  44. package/dist/config.d.ts.map +1 -1
  45. package/dist/deep-observation.d.ts +14 -0
  46. package/dist/deep-observation.d.ts.map +1 -1
  47. package/dist/effect-receipt.d.ts +177 -0
  48. package/dist/effect-receipt.d.ts.map +1 -0
  49. package/dist/env.d.ts +12 -1
  50. package/dist/env.d.ts.map +1 -1
  51. package/dist/fold/subset.d.ts +15 -2
  52. package/dist/fold/subset.d.ts.map +1 -1
  53. package/dist/index.d.ts +4 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/lexicon.d.ts +223 -2
  56. package/dist/lexicon.d.ts.map +1 -1
  57. package/dist/lifecycle/change-set.d.ts +33 -5
  58. package/dist/lifecycle/change-set.d.ts.map +1 -1
  59. package/dist/lifecycle/index.d.ts +3 -0
  60. package/dist/lifecycle/index.d.ts.map +1 -1
  61. package/dist/lifecycle/observation-baseline.d.ts +21 -3
  62. package/dist/lifecycle/observation-baseline.d.ts.map +1 -1
  63. package/dist/lifecycle/receipt-plan.d.ts +62 -0
  64. package/dist/lifecycle/receipt-plan.d.ts.map +1 -0
  65. package/dist/lifecycle/release-ledger.d.ts +20 -0
  66. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  67. package/dist/lifecycle/teardown.d.ts +132 -0
  68. package/dist/lifecycle/teardown.d.ts.map +1 -0
  69. package/dist/lifecycle/unobserved-gate.d.ts +67 -0
  70. package/dist/lifecycle/unobserved-gate.d.ts.map +1 -0
  71. package/dist/lint/engine.d.ts +6 -2
  72. package/dist/lint/engine.d.ts.map +1 -1
  73. package/dist/lint/knowledge-checks.d.ts +48 -0
  74. package/dist/lint/knowledge-checks.d.ts.map +1 -0
  75. package/dist/lint/output-checks.d.ts +5 -0
  76. package/dist/lint/output-checks.d.ts.map +1 -0
  77. package/dist/lint/pipeline-change-gate.d.ts +101 -0
  78. package/dist/lint/pipeline-change-gate.d.ts.map +1 -0
  79. package/dist/lint/post-synth.d.ts +12 -0
  80. package/dist/lint/post-synth.d.ts.map +1 -1
  81. package/dist/lint/receipt-checks.d.ts +9 -0
  82. package/dist/lint/receipt-checks.d.ts.map +1 -0
  83. package/dist/lint/rule.d.ts +31 -0
  84. package/dist/lint/rule.d.ts.map +1 -1
  85. package/dist/lint/rules/cor021-env-literal-name.d.ts +3 -0
  86. package/dist/lint/rules/cor021-env-literal-name.d.ts.map +1 -0
  87. package/dist/lint/rules/cor022-receipt-leaf.d.ts +13 -0
  88. package/dist/lint/rules/cor022-receipt-leaf.d.ts.map +1 -0
  89. package/dist/lint/rules/cor024-receipt-secret-pointer.d.ts +3 -0
  90. package/dist/lint/rules/cor024-receipt-secret-pointer.d.ts.map +1 -0
  91. package/dist/lint/rules/evl001-non-literal-expression.d.ts.map +1 -1
  92. package/dist/lint/rules/index.d.ts +4 -1
  93. package/dist/lint/rules/index.d.ts.map +1 -1
  94. package/dist/okf-read.d.ts +78 -0
  95. package/dist/okf-read.d.ts.map +1 -0
  96. package/dist/op/builders.d.ts +134 -8
  97. package/dist/op/builders.d.ts.map +1 -1
  98. package/dist/op/index.d.ts +4 -2
  99. package/dist/op/index.d.ts.map +1 -1
  100. package/dist/op/local-executor.d.ts +2 -1
  101. package/dist/op/local-executor.d.ts.map +1 -1
  102. package/dist/op/receipt-store.d.ts +138 -0
  103. package/dist/op/receipt-store.d.ts.map +1 -0
  104. package/dist/op/types.d.ts +31 -1
  105. package/dist/op/types.d.ts.map +1 -1
  106. package/dist/secret-materialization.d.ts +138 -0
  107. package/dist/secret-materialization.d.ts.map +1 -0
  108. package/dist/secret-provenance.d.ts +218 -0
  109. package/dist/secret-provenance.d.ts.map +1 -0
  110. package/dist/serializer.d.ts +11 -0
  111. package/dist/serializer.d.ts.map +1 -1
  112. package/dist/testing.d.ts +136 -0
  113. package/dist/testing.d.ts.map +1 -0
  114. package/dist/yaml.d.ts.map +1 -1
  115. package/package.json +9 -1
  116. package/src/audit/core.test.ts +57 -0
  117. package/src/audit/core.ts +0 -0
  118. package/src/audit/detect-bundle.test.ts +1 -1
  119. package/src/audit/discover.test.ts +24 -0
  120. package/src/audit/discover.ts +11 -2
  121. package/src/audit/rules-doc.ts +11 -1
  122. package/src/build.test.ts +41 -0
  123. package/src/build.ts +34 -6
  124. package/src/cli/commands/__fixtures__/audit-fountain/agents/fleet.yaml +27 -0
  125. package/src/cli/commands/__fixtures__/audit-fountain/k8s/deploy.yaml +16 -0
  126. package/src/cli/commands/__fixtures__/audit-fountain-clean/fleet.yaml +20 -0
  127. package/src/cli/commands/audit.test.ts +53 -0
  128. package/src/cli/commands/audit.ts +1 -1
  129. package/src/cli/commands/build.test.ts +211 -0
  130. package/src/cli/commands/build.ts +126 -8
  131. package/src/cli/commands/check-lexicon.test.ts +45 -1
  132. package/src/cli/commands/check-lexicon.ts +45 -0
  133. package/src/cli/commands/lexicon-surface-diff.ts +9 -0
  134. package/src/cli/commands/lexicon-surface-diff.update.test.ts +112 -0
  135. package/src/cli/commands/lint.ts +32 -7
  136. package/src/cli/handlers/explain.test.ts +70 -1
  137. package/src/cli/handlers/graph.ts +4 -4
  138. package/src/cli/handlers/lifecycle.test.ts +345 -1
  139. package/src/cli/handlers/lifecycle.ts +300 -11
  140. package/src/cli/handlers/search.ts +5 -2
  141. package/src/cli/main.ts +12 -1
  142. package/src/cli/mcp/resource-handlers.ts +38 -1
  143. package/src/cli/mcp/server.test.ts +58 -1
  144. package/src/cli/mcp/tools/explain.ts +51 -2
  145. package/src/cli/plugins.ts +4 -2
  146. package/src/cli/registry.ts +7 -0
  147. package/src/cli/reporters/stylish.test.ts +154 -0
  148. package/src/cli/reporters/stylish.ts +154 -33
  149. package/src/codegen/lexicon-regen.ts +19 -1
  150. package/src/codegen/validate.test.ts +33 -0
  151. package/src/codegen/validate.ts +21 -2
  152. package/src/components/auto-release.ts +6 -0
  153. package/src/components/registry.test.ts +7 -2
  154. package/src/components/starter-plugin.ts +17 -0
  155. package/src/components/verbs/ensure-secret.test.ts +130 -0
  156. package/src/components/verbs/ensure-secret.ts +79 -0
  157. package/src/components/verbs/index.ts +8 -0
  158. package/src/components/verbs/r2-sync.test.ts +107 -0
  159. package/src/components/verbs/r2-sync.ts +124 -0
  160. package/src/components/verbs/wrangler.test.ts +170 -0
  161. package/src/components/verbs/wrangler.ts +241 -0
  162. package/src/config.test.ts +55 -0
  163. package/src/config.ts +88 -1
  164. package/src/deep-observation.test.ts +19 -0
  165. package/src/deep-observation.ts +17 -0
  166. package/src/effect-receipt-exclusion.test.ts +190 -0
  167. package/src/effect-receipt.test.ts +419 -0
  168. package/src/effect-receipt.ts +412 -0
  169. package/src/env.test.ts +35 -1
  170. package/src/env.ts +17 -3
  171. package/src/fold/subset.test.ts +26 -0
  172. package/src/fold/subset.ts +45 -19
  173. package/src/index.ts +4 -0
  174. package/src/lexicon.ts +227 -2
  175. package/src/lifecycle/change-set.ts +46 -7
  176. package/src/lifecycle/index.ts +3 -0
  177. package/src/lifecycle/observation-baseline.test.ts +46 -0
  178. package/src/lifecycle/observation-baseline.ts +33 -1
  179. package/src/lifecycle/receipt-plan.test.ts +250 -0
  180. package/src/lifecycle/receipt-plan.ts +249 -0
  181. package/src/lifecycle/release-ledger.ts +20 -0
  182. package/src/lifecycle/teardown.test.ts +568 -0
  183. package/src/lifecycle/teardown.ts +359 -0
  184. package/src/lifecycle/unobserved-gate.test.ts +109 -0
  185. package/src/lifecycle/unobserved-gate.ts +102 -0
  186. package/src/lint/engine.ts +7 -1
  187. package/src/lint/knowledge-checks.test.ts +80 -0
  188. package/src/lint/knowledge-checks.ts +74 -0
  189. package/src/lint/output-checks.test.ts +85 -0
  190. package/src/lint/output-checks.ts +99 -0
  191. package/src/lint/pipeline-change-gate.test.ts +144 -0
  192. package/src/lint/pipeline-change-gate.ts +153 -0
  193. package/src/lint/post-synth.ts +15 -0
  194. package/src/lint/receipt-checks.test.ts +101 -0
  195. package/src/lint/receipt-checks.ts +93 -0
  196. package/src/lint/rule.ts +23 -0
  197. package/src/lint/rules/cor021-env-literal-name.test.ts +128 -0
  198. package/src/lint/rules/cor021-env-literal-name.ts +114 -0
  199. package/src/lint/rules/cor022-receipt-leaf.test.ts +116 -0
  200. package/src/lint/rules/cor022-receipt-leaf.ts +130 -0
  201. package/src/lint/rules/cor024-receipt-secret-pointer.test.ts +121 -0
  202. package/src/lint/rules/cor024-receipt-secret-pointer.ts +218 -0
  203. package/src/lint/rules/evl001-non-literal-expression.test.ts +27 -0
  204. package/src/lint/rules/evl001-non-literal-expression.ts +8 -1
  205. package/src/lint/rules/index.ts +10 -1
  206. package/src/okf-read.test.ts +149 -0
  207. package/src/okf-read.ts +197 -0
  208. package/src/op/builders.ts +179 -8
  209. package/src/op/effect-step.test.ts +311 -0
  210. package/src/op/index.ts +10 -3
  211. package/src/op/local-executor.ts +172 -25
  212. package/src/op/op.test.ts +25 -2
  213. package/src/op/receipt-store.ts +211 -0
  214. package/src/op/types.ts +33 -1
  215. package/src/secret-materialization.test.ts +199 -0
  216. package/src/secret-materialization.ts +235 -0
  217. package/src/secret-provenance.test.ts +388 -0
  218. package/src/secret-provenance.ts +475 -0
  219. package/src/serializer.ts +12 -0
  220. package/src/testing.test.ts +261 -0
  221. package/src/testing.ts +338 -0
  222. package/src/yaml.test.ts +88 -0
  223. package/src/yaml.ts +76 -6
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Generated-once secret materialization — the one implementation behind the
3
+ * `ensure-secret` capability verb (components/verbs/ensure-secret.ts) and the
4
+ * `ensureSecret(...)` op step builder (op/builders.ts). chant #1829, epic
5
+ * #1365 decisions 3 and 6.
6
+ *
7
+ * The contract is read-then-write:
8
+ *
9
+ * - Absent: mint once, through the store adapter, and report `created`.
10
+ * - Present: verify presence, the declared key-set, and any declared
11
+ * metadata, then STOP — present means done. Never mint over an existing
12
+ * value. Never rotate implicitly. A re-run of a whole deploy leaves the
13
+ * stored bytes untouched.
14
+ * - Mismatch: fail loudly, naming what mismatched — key names and metadata
15
+ * keys, never a value or a value-derived hash (#1365 decision 6).
16
+ *
17
+ * The constitutional line (../secret-provenance.ts): no code path here may
18
+ * hold, log, hash, or compare a secret value. That is structural, not
19
+ * discipline:
20
+ *
21
+ * - {@link ensureSecretMaterialization} never calls the generator. It hands
22
+ * the generator to the store adapter's `create`, which consumes the
23
+ * material as it writes. The engine's result type has no field that could
24
+ * carry material.
25
+ * - The generator produces {@link SecretMaterial}, an opaque single-use
26
+ * handle. The plaintext lives in a module-private WeakMap, not on the
27
+ * object: enumeration, `JSON.stringify`, `String(...)`, and `util.inspect`
28
+ * all see only a redaction marker. Only {@link consumeSecretMaterial} —
29
+ * meant for store adapters, at the write — yields the plaintext, exactly
30
+ * once; a second consume throws.
31
+ * - `describe` returns key names and metadata only, so the mismatch check
32
+ * has nothing value-shaped to compare even by accident.
33
+ *
34
+ * Store adapters are per-provider (#1830 is the k8s row); core defines only
35
+ * the seam.
36
+ */
37
+ /**
38
+ * A single-use, opaque handle to generated secret material. The plaintext is
39
+ * not a property of this object — it lives in a module-private WeakMap — so
40
+ * spreading, enumerating, stringifying, or logging the handle yields only a
41
+ * redaction marker. A store adapter redeems it with
42
+ * {@link consumeSecretMaterial} at the moment it writes; nothing else can.
43
+ */
44
+ export declare class SecretMaterial {
45
+ private constructor();
46
+ /** Wrap plaintext in an opaque handle. Call inside a generator only. */
47
+ static mint(plaintext: string): SecretMaterial;
48
+ toString(): string;
49
+ toJSON(): string;
50
+ }
51
+ /**
52
+ * Redeem a {@link SecretMaterial} handle for its plaintext — exactly once.
53
+ * For store adapters only, at the write to the backing store. Throws if the
54
+ * handle was already consumed (or was never minted here): material flows to
55
+ * the store exactly once and is retained nowhere.
56
+ */
57
+ export declare function consumeSecretMaterial(material: SecretMaterial): string;
58
+ /**
59
+ * Produces the material for one key of a secret being minted. Called by the
60
+ * store adapter (never by the engine) once per declared key, at create time —
61
+ * generation is apply-time I/O, never synthesis.
62
+ */
63
+ export type SecretMaterialGenerator = (key: string) => SecretMaterial | Promise<SecretMaterial>;
64
+ /** 32 bytes from the CSPRNG, base64url — the default mint. */
65
+ export declare const defaultSecretMaterialGenerator: SecretMaterialGenerator;
66
+ /** What `describe` reports about an existing secret: key NAMES and metadata. Never values. */
67
+ export interface SecretStoreDescription {
68
+ /** The key names present in the stored secret. */
69
+ readonly keys: readonly string[];
70
+ /** Store metadata (e.g. k8s labels/annotations the provider surfaces). */
71
+ readonly metadata?: Readonly<Record<string, string>>;
72
+ }
73
+ /**
74
+ * A provider's view of one secret store — the seam #1830 (k8s) and future
75
+ * provider rows implement. `create` receives the generator and consumes each
76
+ * key's material as it writes; no method returns material.
77
+ */
78
+ export interface SecretStoreAdapter {
79
+ /** Whether a secret of this name exists in the store. */
80
+ exists(name: string): Promise<boolean>;
81
+ /** Key names and metadata of an existing secret. Never values. */
82
+ describe(name: string): Promise<SecretStoreDescription>;
83
+ /**
84
+ * Create the secret, minting material for each declared key via
85
+ * `generate` and writing it straight to the store. Returns nothing:
86
+ * material must not travel back through this seam.
87
+ */
88
+ create(name: string, keys: readonly string[], generate: SecretMaterialGenerator): Promise<void>;
89
+ }
90
+ /** The declared contract to ensure — names and keys only, never material. */
91
+ export interface EnsureSecretSpec {
92
+ /** The secret's name as the store knows it. */
93
+ readonly name: string;
94
+ /** The declared key-set. Creation mints one value per key; verification compares names. */
95
+ readonly keys: readonly string[];
96
+ /** Declared metadata an existing secret must carry (compared per key; mismatches are reported by KEY). */
97
+ readonly metadata?: Readonly<Record<string, string>>;
98
+ }
99
+ /** What `ensureSecretMaterialization` did. No field can carry material. */
100
+ export interface EnsureSecretOutcome {
101
+ /** `created` = minted now (first materialization); `present` = existed and matched the contract (no write). */
102
+ readonly outcome: "created" | "present";
103
+ /** The secret's name. */
104
+ readonly name: string;
105
+ /** The declared key names the contract was checked (or minted) against. */
106
+ readonly keys: readonly string[];
107
+ }
108
+ /**
109
+ * The loud failure: an existing secret does not match its declared contract.
110
+ * `mismatches` names what differed — key names and metadata keys only.
111
+ * Constructing one with anything value-shaped is the reviewer's tripwire;
112
+ * nothing in this module can, because nothing in this module holds a value.
113
+ */
114
+ export declare class SecretContractMismatchError extends Error {
115
+ /** The secret's name. */
116
+ readonly secretName: string;
117
+ /** Human-readable mismatch descriptions, naming keys — never values. */
118
+ readonly mismatches: readonly string[];
119
+ constructor(
120
+ /** The secret's name. */
121
+ secretName: string,
122
+ /** Human-readable mismatch descriptions, naming keys — never values. */
123
+ mismatches: readonly string[]);
124
+ }
125
+ /**
126
+ * Ensure a `generated-once` secret exists and matches its declared contract.
127
+ * Read-then-write:
128
+ *
129
+ * - Absent: `store.create(name, keys, generate)` — one mint, then done.
130
+ * - Present and matching: no write of any kind; returns `present`.
131
+ * - Present and mismatching: throws {@link SecretContractMismatchError}
132
+ * naming the missing/unexpected key names and mismatched metadata keys.
133
+ *
134
+ * The engine never calls `generate` itself and never sees what the adapter
135
+ * writes; its return value carries names only.
136
+ */
137
+ export declare function ensureSecretMaterialization(store: SecretStoreAdapter, spec: EnsureSecretSpec, generate?: SecretMaterialGenerator): Promise<EnsureSecretOutcome>;
138
+ //# sourceMappingURL=secret-materialization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-materialization.d.ts","sourceRoot":"","sources":["../src/secret-materialization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAWH;;;;;;GAMG;AACH,qBAAa,cAAc;IACzB,OAAO;IAEP,wEAAwE;IACxE,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc;IAM9C,QAAQ,IAAI,MAAM;IAIlB,MAAM,IAAI,MAAM;CAQjB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAStE;AAED;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;AAEhG,8DAA8D;AAC9D,eAAO,MAAM,8BAA8B,EAAE,uBACe,CAAC;AAI7D,8FAA8F;AAC9F,MAAM,WAAW,sBAAsB;IACrC,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtD;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,yDAAyD;IACzD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACxD;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjG;AAID,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2FAA2F;IAC3F,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,0GAA0G;IAC1G,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtD;AAED,2EAA2E;AAC3E,MAAM,WAAW,mBAAmB;IAClC,+GAA+G;IAC/G,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;IACxC,yBAAyB;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IAElD,yBAAyB;aACT,UAAU,EAAE,MAAM;IAClC,wEAAwE;aACxD,UAAU,EAAE,SAAS,MAAM,EAAE;;IAH7C,yBAAyB;IACT,UAAU,EAAE,MAAM;IAClC,wEAAwE;IACxD,UAAU,EAAE,SAAS,MAAM,EAAE;CAQhD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,kBAAkB,EACzB,IAAI,EAAE,gBAAgB,EACtB,QAAQ,GAAE,uBAAwD,GACjE,OAAO,CAAC,mBAAmB,CAAC,CA0C9B"}
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Secret provenance vocabulary (chant #1828, epic #1365).
3
+ *
4
+ * A secret's PROVENANCE is where its value comes from — never what the value
5
+ * is. chant's constitutional line on secrets: no code path may hold, log,
6
+ * hash, or compare a secret value. This module makes that structural: the
7
+ * declaration types have no field that could carry material, the factory
8
+ * rejects material-shaped fields at both the type level (`never` fields) and
9
+ * at runtime (own-property check, naming only the offending KEY), and
10
+ * "mismatch" anywhere downstream means presence, declared key-set, and
11
+ * metadata — never a value or a value-derived hash (#1365 decision 6).
12
+ *
13
+ * The closed kind set:
14
+ *
15
+ * - `referenced` — the value exists out of band (a human or an external
16
+ * process put it where consumers read it). chant records only that the
17
+ * estate depends on it, which is what lets the consumed-but-unproduced
18
+ * check (#1382) ship as an error instead of a guess.
19
+ * - `from-provider` — a declared provider binding materializes it (the
20
+ * operator/CRD seam; `K8s::Infisical::InfisicalSecret` from #1321 is the
21
+ * exemplar). The declaration POINTS at that binding, it does not re-model
22
+ * it.
23
+ * - `generated-once` — minted on first materialization, then never
24
+ * regenerated (present means done). The declaration carries contract flags
25
+ * only — e.g. the declared key-set — never mint parameters holding
26
+ * material. `generated-once` secrets never enter the prunable set (#1365
27
+ * decision 5).
28
+ * - `committed-encrypted` — sops-style ciphertext committed in the repo,
29
+ * decrypted by the delivery system (Flux) straight into the target. The
30
+ * declaration records a repo-relative PATH to the ciphertext, never the
31
+ * bytes: the factory is pure and touches no filesystem, so it still folds
32
+ * under `--sandbox`. The bytes are read at `buildRoots()` — the one
33
+ * sanctioned impure seam — and emitted as a sidecar file, never as a
34
+ * document in the primary output. See
35
+ * `docs/design/committed-encrypted-sops-provenance.md`.
36
+ *
37
+ * Declarations are serializer-neutral: discovery collects them like any
38
+ * entity (they are Declarables), but `partitionByLexicon` (../build.ts)
39
+ * excludes them from every serializer partition, so no lexicon ever emits
40
+ * them. They are data that lint rules and lexicons READ (via
41
+ * {@link collectSecretDeclarations}), not output.
42
+ */
43
+ import { type Declarable } from "./declarable.js";
44
+ /** The closed union of secret origins. */
45
+ export type SecretProvenance = "referenced" | "from-provider" | "generated-once" | "committed-encrypted";
46
+ /** Every provenance kind, for exhaustiveness checks. */
47
+ export declare const SECRET_PROVENANCE_KINDS: readonly SecretProvenance[];
48
+ /**
49
+ * Encryption tools a {@link CommittedEncryptedSecretDeclaration} understands.
50
+ * A closed union with one member, not a free string: a second tool later is a
51
+ * deliberate widening with a detection rule attached, not an unvalidated
52
+ * string that silently means nothing.
53
+ */
54
+ export type SecretEncryption = "sops";
55
+ /** Every encryption tool, for exhaustiveness checks. */
56
+ export declare const SECRET_ENCRYPTION_TOOLS: readonly SecretEncryption[];
57
+ /** Marker symbol identifying a secret provenance declaration. `Symbol.for` so
58
+ * it survives the entity-wire codec (../discovery/entity-wire-codec.ts). */
59
+ export declare const SECRET_DECLARATION_MARKER: unique symbol;
60
+ /** The `entityType` every secret declaration carries. */
61
+ export declare const SECRET_DECLARATION_ENTITY_TYPE = "Chant::SecretProvenance";
62
+ /**
63
+ * Points a `from-provider` declaration at the provider binding that
64
+ * materializes the secret — the CRD seam, not a re-model of it. The binding
65
+ * itself is an ordinary declarable in its own lexicon (e.g. an
66
+ * `InfisicalSecret` instance, #1321); this ref names it so lint can resolve
67
+ * the pair without core depending on any lexicon.
68
+ */
69
+ export interface SecretProviderRef {
70
+ /** Entity name (export name) of the provider binding declarable in this project. */
71
+ readonly binding: string;
72
+ /**
73
+ * Expected `entityType` of the binding (e.g.
74
+ * `"K8s::Infisical::InfisicalSecret"`). Optional; when present, lint can
75
+ * verify the named binding is actually the kind of seam the declaration
76
+ * claims.
77
+ */
78
+ readonly entityType?: string;
79
+ }
80
+ /**
81
+ * Fields that would carry secret material, forbidden by construction. Typed
82
+ * as `never` on every factory input so they fail to compile even when the
83
+ * input object was widened or spread (plain excess-property checks only catch
84
+ * object literals). The runtime check in {@link declareSecret} is the same
85
+ * list, for untyped callers.
86
+ */
87
+ interface NoSecretMaterial {
88
+ readonly value?: never;
89
+ readonly data?: never;
90
+ readonly stringData?: never;
91
+ readonly material?: never;
92
+ readonly plaintext?: never;
93
+ readonly ciphertext?: never;
94
+ }
95
+ /** Fields shared by every secret declaration. */
96
+ interface SecretDeclarationBase extends Declarable {
97
+ readonly [SECRET_DECLARATION_MARKER]: true;
98
+ readonly lexicon: "chant";
99
+ readonly entityType: typeof SECRET_DECLARATION_ENTITY_TYPE;
100
+ /** The secret's name as consumers know it (e.g. the k8s Secret name). */
101
+ readonly name: string;
102
+ /** Which of the closed kind set this declaration is. */
103
+ readonly provenance: SecretProvenance;
104
+ }
105
+ /** A secret whose value exists out of band. */
106
+ export interface ReferencedSecretDeclaration extends SecretDeclarationBase {
107
+ readonly provenance: "referenced";
108
+ /** Where consumers find it — free-form (a namespace, a vault path, a doc link). */
109
+ readonly scope?: string;
110
+ }
111
+ /** A secret a declared provider binding materializes. */
112
+ export interface FromProviderSecretDeclaration extends SecretDeclarationBase {
113
+ readonly provenance: "from-provider";
114
+ readonly provider: SecretProviderRef;
115
+ }
116
+ /** A secret minted on first materialization and never regenerated. */
117
+ export interface GeneratedOnceSecretDeclaration extends SecretDeclarationBase {
118
+ readonly provenance: "generated-once";
119
+ /**
120
+ * The declared key-set of the materialized secret. This is the contract a
121
+ * materializer (#1830) checks for mismatch — presence and keys, never
122
+ * values.
123
+ */
124
+ readonly keys?: readonly string[];
125
+ }
126
+ /**
127
+ * A secret whose ciphertext is committed to the repo and decrypted by the
128
+ * delivery system on the way into the target.
129
+ *
130
+ * The declaration is a POINTER: `file` is a repo-relative path, and nothing
131
+ * here carries bytes. That is what keeps the factory pure — the ciphertext is
132
+ * read at `buildRoots()` and emitted as a sidecar, never inlined into the
133
+ * primary output an applier reads.
134
+ */
135
+ export interface CommittedEncryptedSecretDeclaration extends SecretDeclarationBase {
136
+ readonly provenance: "committed-encrypted";
137
+ /** Repo-relative path to the committed ciphertext file. */
138
+ readonly file: string;
139
+ /** Encryption tool. Defaults to `"sops"` when omitted. */
140
+ readonly encryption: SecretEncryption;
141
+ /**
142
+ * Public recipient identifiers — age recipients or PGP fingerprints. Public
143
+ * by definition, which is the opposite of material; a private key here is
144
+ * refused by the factory.
145
+ */
146
+ readonly recipients?: readonly string[];
147
+ /**
148
+ * The declared key-set of the decrypted Secret — the same contract meaning
149
+ * as {@link GeneratedOnceSecretDeclaration.keys}: presence and key names,
150
+ * never values. SOPS leaves key NAMES cleartext, so this is checkable
151
+ * against the file itself.
152
+ */
153
+ readonly keys?: readonly string[];
154
+ }
155
+ /** A secret provenance declaration — the discriminant is `provenance`. */
156
+ export type SecretDeclaration = ReferencedSecretDeclaration | FromProviderSecretDeclaration | GeneratedOnceSecretDeclaration | CommittedEncryptedSecretDeclaration;
157
+ /** Narrow a declaration to the committed-encrypted kind. */
158
+ export declare function isCommittedEncryptedSecret(decl: SecretDeclaration): decl is CommittedEncryptedSecretDeclaration;
159
+ /** Factory input for a `referenced` secret. */
160
+ export interface ReferencedSecretInput extends NoSecretMaterial {
161
+ readonly name: string;
162
+ readonly provenance: "referenced";
163
+ readonly scope?: string;
164
+ }
165
+ /** Factory input for a `from-provider` secret. */
166
+ export interface FromProviderSecretInput extends NoSecretMaterial {
167
+ readonly name: string;
168
+ readonly provenance: "from-provider";
169
+ readonly provider: SecretProviderRef;
170
+ }
171
+ /** Factory input for a `generated-once` secret. */
172
+ export interface GeneratedOnceSecretInput extends NoSecretMaterial {
173
+ readonly name: string;
174
+ readonly provenance: "generated-once";
175
+ readonly keys?: readonly string[];
176
+ }
177
+ /** Factory input for a `committed-encrypted` secret. */
178
+ export interface CommittedEncryptedSecretInput extends NoSecretMaterial {
179
+ readonly name: string;
180
+ readonly provenance: "committed-encrypted";
181
+ /**
182
+ * Repo-relative path to the committed ciphertext file — `file`, never
183
+ * `ciphertext`: the declaration points at bytes, it does not carry them,
184
+ * and `ciphertext` is a forbidden material field.
185
+ */
186
+ readonly file: string;
187
+ /** Encryption tool. Closed union; `"sops"` is its only member today. */
188
+ readonly encryption?: SecretEncryption;
189
+ /** Public recipient identifiers — age recipients or PGP fingerprints. */
190
+ readonly recipients?: readonly string[];
191
+ /** The declared key-set of the decrypted Secret. Names only, never values. */
192
+ readonly keys?: readonly string[];
193
+ }
194
+ export type SecretDeclarationInput = ReferencedSecretInput | FromProviderSecretInput | GeneratedOnceSecretInput | CommittedEncryptedSecretInput;
195
+ /**
196
+ * Declare a secret's provenance. The returned object is a locked Declarable:
197
+ * discovery collects it like any entity, `chant list` shows it, lint and
198
+ * lexicons read it — and no serializer ever emits it.
199
+ *
200
+ * Only the fields the kind defines are copied onto the declaration; anything
201
+ * else on the input — in particular anything that could carry material — is
202
+ * either a compile error ({@link NoSecretMaterial}) or a thrown error here.
203
+ * The thrown message names the offending KEY only, never its value.
204
+ */
205
+ export declare function declareSecret(input: ReferencedSecretInput): ReferencedSecretDeclaration;
206
+ export declare function declareSecret(input: FromProviderSecretInput): FromProviderSecretDeclaration;
207
+ export declare function declareSecret(input: GeneratedOnceSecretInput): GeneratedOnceSecretDeclaration;
208
+ export declare function declareSecret(input: CommittedEncryptedSecretInput): CommittedEncryptedSecretDeclaration;
209
+ /** Type guard for a secret provenance declaration. */
210
+ export declare function isSecretDeclaration(value: unknown): value is SecretDeclaration;
211
+ /**
212
+ * Extract the secret declarations from a discovered entity map — the read
213
+ * surface for lint rules (#1382) and lexicon materializers (#1830). Keyed by
214
+ * entity name (export name), the same key `DiscoveryResult.entities` uses.
215
+ */
216
+ export declare function collectSecretDeclarations(entities: ReadonlyMap<string, Declarable>): Map<string, SecretDeclaration>;
217
+ export {};
218
+ //# sourceMappingURL=secret-provenance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-provenance.d.ts","sourceRoot":"","sources":["../src/secret-provenance.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,17 @@ export interface SerializeContext {
18
18
  * ad-hoc builds (e.g. context tools) that pass no config.
19
19
  */
20
20
  config?: Record<string, unknown>;
21
+ /**
22
+ * Effect receipts (#1832, epic #1703) declared in this lexicon's partition.
23
+ * Withheld from the `entities` map by the build: receipts are observe-only
24
+ * to the generic apply path, and the serialized output is what feeds
25
+ * appliers, so a receipt must never enter the apply-bound document. A
26
+ * serializer that wants to render receipts for visibility (#1835) reads
27
+ * them here and puts them anywhere BUT the section an applier writes from;
28
+ * a serializer that ignores this field emits nothing for them, which is the
29
+ * safe default.
30
+ */
31
+ receipts?: ReadonlyMap<string, Declarable>;
21
32
  }
22
33
  /**
23
34
  * Result of serialization that may include additional files (e.g. nested stack templates).
@@ -1 +1 @@
1
- {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;;;;;;;;;;;OAaG;IACH,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEtC;;;;;OAKG;IACH,SAAS,CACP,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EACjC,OAAO,CAAC,EAAE,aAAa,EAAE,EACzB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,GAAG,gBAAgB,CAAC;IAE7B;;;;;OAKG;IACH,iBAAiB,CAAC,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC;CACpD"}
1
+ {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEjC;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;;;;;;;;;;;OAaG;IACH,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEtC;;;;;OAKG;IACH,SAAS,CACP,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EACjC,OAAO,CAAC,EAAE,aAAa,EAAE,EACzB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,GAAG,gBAAgB,CAAC;IAE7B;;;;;OAKG;IACH,iBAAiB,CAAC,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC;CACpD"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * `@intentius/chant/testing` (#1224) — the live-stack test harness.
3
+ *
4
+ * A vitest suite deploys a real instance of its project once, asserts against
5
+ * it, and tears it down — emulator-locally on a dev machine, real-cloud in CI,
6
+ * with the same suite text. One call composes machinery that already exists:
7
+ *
8
+ * - **build** — the same programmatic path `chant build` takes: config
9
+ * resolution (`buildParams` + the ownership marker) mirrored from the CLI,
10
+ * then `build()` over the project's own serializers.
11
+ * - **apply** — an additive `nativeApply` per built lexicon output, run
12
+ * in-process through the local Op executor (`runOpLocally`). `deleteMode`
13
+ * is `"never"`: a test deploy creates and updates, nothing else.
14
+ * - **destroy** — #1222's marker-scoped teardown (`executeTeardown`), called
15
+ * in-process for exactly this suite's environment. Stateless by design:
16
+ * a crashed suite's environment is recovered by calling destroy again (or
17
+ * `chant lifecycle teardown <env> --yes`).
18
+ *
19
+ * ## Isolation
20
+ *
21
+ * Every deploy targets its own environment: `test-<suite>-<nonce>` by default
22
+ * ({@link testEnvName}), so parallel CI jobs never collide. The environment
23
+ * is the teardown key — everything the deploy stamps carries the ownership
24
+ * marker `{ stack, env }`, and `destroy()` selects on that identity and
25
+ * nothing else. A project that declares `environments` must legalize the
26
+ * dynamic names with a pattern entry (#1221):
27
+ *
28
+ * ```ts
29
+ * environments: ["dev", "prod", { name: "test-*", endpoint: "http://localhost:4566" }]
30
+ * ```
31
+ *
32
+ * ## Emulators
33
+ *
34
+ * The harness is emulator-aware the same way `--live` reads are (#1166): the
35
+ * target environment's declared `endpoint` is injected into each lexicon's
36
+ * ambient endpoint variable (`AWS_ENDPOINT_URL`, …) for the apply and the
37
+ * teardown — unless the variable is already set. Ambient always wins, so the
38
+ * env vars `chant emulator up --json` reports (#920) take precedence when
39
+ * exported, and a CI job pointing the identical suite at a real account just
40
+ * leaves both unset.
41
+ */
42
+ import type { Declarable } from "./declarable.js";
43
+ import type { SerializerResult } from "./serializer.js";
44
+ import { type BuildParamValue } from "./build-params.js";
45
+ import type { LexiconPlugin } from "./lexicon.js";
46
+ import { type ActivityFn, type ActivityProfile } from "./op/activity-registry.js";
47
+ import { type TeardownReport } from "./lifecycle/teardown.js";
48
+ export interface DeployStackOptions {
49
+ /** The chant project directory (where the infra source lives). */
50
+ dir: string;
51
+ /**
52
+ * Explicit environment name. Overrides the default `test-<suite>-<nonce>`
53
+ * derivation — for a shared long-lived test environment, or a test that
54
+ * needs a deterministic name. Must be legal for the project's declared
55
+ * `environments`, exactly as `--env` would be.
56
+ */
57
+ env?: string;
58
+ /**
59
+ * Suite name folded into the default environment name. Defaults to the
60
+ * project directory's basename.
61
+ */
62
+ suite?: string;
63
+ /** Build parameters, exactly as `--param name=value` flags would supply them. */
64
+ params?: Record<string, BuildParamValue>;
65
+ /**
66
+ * Loaded lexicon plugins. Defaults to loading the project's own declared/
67
+ * detected lexicons — pass this only to substitute test doubles.
68
+ */
69
+ plugins?: LexiconPlugin[];
70
+ /** Activity implementations for the apply. Defaults to the real registry. */
71
+ activities?: Map<string, ActivityFn>;
72
+ /** Activity profiles (timeouts/retries). Defaults to the lexicon-declared table. */
73
+ profiles?: Record<string, ActivityProfile>;
74
+ /**
75
+ * Extra lexicon-name → `nativeApply` target entries, merged over the
76
+ * built-in map (aws → cloudformation, k8s → kubectl, azure → arm,
77
+ * gcp → gcp, fly → fly). A seam for tests and out-of-tree lexicons.
78
+ */
79
+ applyTargets?: Record<string, string>;
80
+ }
81
+ /** The handle a suite holds between `beforeAll` and `afterAll`. */
82
+ export interface DeployedStack {
83
+ /** The built outputs, keyed by lexicon — what was deployed. */
84
+ outputs: Map<string, string | SerializerResult>;
85
+ /** The discovered entities, keyed by name. */
86
+ entities: Map<string, Declarable>;
87
+ /** The environment this deploy targeted — the teardown key. */
88
+ env: string;
89
+ /**
90
+ * Tear down everything carrying this suite's marker `{ stack, env }`.
91
+ * Throws {@link TeardownIncompleteError} when any candidate failed to
92
+ * delete or the plan had holes — an environment that cannot be called
93
+ * clean is a test failure, never a silent leak. Safe to call again:
94
+ * teardown is stateless, and a second call over a clean environment
95
+ * plans nothing.
96
+ */
97
+ destroy(): Promise<TeardownReport>;
98
+ }
99
+ /**
100
+ * Thrown by {@link DeployedStack.destroy} when the environment cannot be
101
+ * called clean: candidates still `failed` after the retry pass, or the plan
102
+ * had holes (#1089 — parts of the estate could not be read, so "nothing
103
+ * left" would be a claim about what was readable, not about the environment).
104
+ * Carries the full report for diagnosis.
105
+ */
106
+ export declare class TeardownIncompleteError extends Error {
107
+ readonly report: TeardownReport;
108
+ constructor(report: TeardownReport);
109
+ }
110
+ /**
111
+ * The default environment name for one suite run: `test-<suite>-<nonce>`.
112
+ * The `test-` prefix is what a project's `"test-*"` pattern entry (#1221)
113
+ * legalizes; the nonce keeps parallel CI jobs of the same suite apart.
114
+ */
115
+ export declare function testEnvName(suite: string): string;
116
+ /**
117
+ * Deploy a live instance of the project in `dir` for one test suite.
118
+ *
119
+ * Build (the CLI's own config resolution: `buildParams`, ownership marker,
120
+ * build roots) + additive apply (one `nativeApply` per built output with a
121
+ * native mechanism, via the local Op executor). Returns the handle the suite
122
+ * holds: outputs, entities, the environment name, and `destroy`.
123
+ *
124
+ * The ownership marker stamped on every resource is `{ stack: ownership.stack,
125
+ * env: <this deploy's environment> }` — the marker env always follows the
126
+ * suite environment, whatever `ownership.env` in config says, because the
127
+ * marker is the only thing `destroy()` selects on. A project with no
128
+ * `ownership.stack` is refused up front: a deploy that stamps nothing is a
129
+ * deploy nothing can sweep.
130
+ *
131
+ * Failures are thrown, never returned: build errors, unresolved parameters,
132
+ * an illegal environment name, and a failed apply (the local executor's
133
+ * `OpRunFailure`) all reject the returned promise.
134
+ */
135
+ export declare function deployStack(options: DeployStackOptions): Promise<DeployedStack>;
136
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAMrD,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAI1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG/C,OAAO,EAAgC,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE7G,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAgB5E,MAAM,WAAW,kBAAkB;IACjC,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACzC;;;OAGG;IACH,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,6EAA6E;IAC7E,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACrC,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC3C;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC;IAChD,8CAA8C;IAC9C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;OAOG;IACH,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;CACpC;AAED;;;;;;GAMG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;aACpB,MAAM,EAAE,cAAc;gBAAtB,MAAM,EAAE,cAAc;CAkBnD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUjD;AA8BD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CAkHrF"}
@@ -1 +1 @@
1
- {"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../src/yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA0BH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAgH/D;AAMD,sCAAsC;AACtC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASlE;AAiFD;;GAEG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,WAAW,CA8Eb;AA0GD;;GAEG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,WAAW,CAqGb;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAelD"}
1
+ {"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../src/yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA0BH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAgH/D;AAMD,sCAAsC;AACtC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASlE;AAiFD;;GAEG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,WAAW,CA8Eb;AA0GD;;GAEG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,WAAW,CAqGb;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAwBlD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.45.0",
3
+ "version": "0.49.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",
@@ -64,6 +64,11 @@
64
64
  "types": "./dist/components/index.d.ts",
65
65
  "default": "./src/components/index.ts"
66
66
  },
67
+ "./testing": {
68
+ "development": "./src/testing.ts",
69
+ "types": "./dist/testing.d.ts",
70
+ "default": "./src/testing.ts"
71
+ },
67
72
  "./*": {
68
73
  "development": "./src/*.ts",
69
74
  "types": "./dist/*.d.ts",
@@ -86,5 +91,8 @@
86
91
  "devDependencies": {
87
92
  "@cdktf/hcl2json": "^0.21.0",
88
93
  "ajv": "^8.20.0"
94
+ },
95
+ "overrides": {
96
+ "esbuild": "^0.28.1"
89
97
  }
90
98
  }
@@ -187,4 +187,61 @@ describe("auditFiles", () => {
187
187
  });
188
188
  expect(findings.map((f) => f.checkId)).toEqual(["OK001"]);
189
189
  });
190
+
191
+ test("parse-to-graph: an injected entities parser lets entity-reading checks fire per file (#1567)", async () => {
192
+ const entityCheck: PostSynthCheck = {
193
+ id: "ENT001",
194
+ description: "reads ctx.entities, not ctx.outputs",
195
+ check: (ctx) =>
196
+ [...ctx.entities.keys()].map((name) => ({ checkId: "ENT001", severity: "warning" as const, message: `saw ${name}`, entity: name })),
197
+ };
198
+ const findings = await auditFiles(
199
+ [
200
+ { path: "a.yaml", content: "one", lexicon: "custom" },
201
+ { path: "b.yaml", content: "two", lexicon: "custom" },
202
+ ],
203
+ {
204
+ checksProvider: async () => [entityCheck],
205
+ entitiesProvider: async () => (content) => new Map([[content, { entityType: "T::X" } as never]]),
206
+ },
207
+ );
208
+ expect(findings.map((f) => `${f.file}:${f.entity}`).sort()).toEqual(["a.yaml:one", "b.yaml:two"]);
209
+ });
210
+
211
+ test("parse-to-graph merge: the same entity name in two files keeps both — a duplicate check sees the collision cross-file", async () => {
212
+ const dupCheck: PostSynthCheck = {
213
+ id: "DUP001",
214
+ description: "fires when more than one entity is present",
215
+ check: (ctx) => (ctx.entities.size > 1 ? [{ checkId: "DUP001", severity: "error" as const, message: "name declared twice" }] : []),
216
+ };
217
+ const findings = await auditFiles(
218
+ [
219
+ { path: "a.yaml", content: "x", lexicon: "custom" },
220
+ { path: "b.yaml", content: "y", lexicon: "custom" },
221
+ ],
222
+ {
223
+ checksProvider: async () => [dupCheck],
224
+ // both files declare the entity name "dev" — the merged pass must keep both
225
+ entitiesProvider: async () => () => new Map([["dev", { entityType: "T::X" } as never]]),
226
+ },
227
+ );
228
+ const dup = findings.find((f) => f.checkId === "DUP001");
229
+ expect(dup).toBeDefined();
230
+ expect(dup!.file).toBe(CROSS_FILE);
231
+ });
232
+
233
+ test("an entities parser that throws contributes no entities and does not abort the audit", async () => {
234
+ const entityCheck: PostSynthCheck = {
235
+ id: "ENT002",
236
+ description: "counts entities",
237
+ check: (ctx) => (ctx.entities.size > 0 ? [{ checkId: "ENT002", severity: "warning" as const, message: "saw entities" }] : []),
238
+ };
239
+ const findings = await auditFiles([{ path: "a.yaml", content: "x", lexicon: "custom" }], {
240
+ checksProvider: async () => [entityCheck],
241
+ entitiesProvider: async () => () => {
242
+ throw new Error("unparseable");
243
+ },
244
+ });
245
+ expect(findings).toEqual([]);
246
+ });
190
247
  });
package/src/audit/core.ts CHANGED
Binary file
@@ -9,7 +9,7 @@ import { fileURLToPath } from "url";
9
9
  * service content-detect all lexicons on Workers.
10
10
  */
11
11
  const repoRoot = fileURLToPath(new URL("../../../../", import.meta.url));
12
- const LEXICONS = ["k8s", "docker", "aws", "azure", "gcp", "helm"];
12
+ const LEXICONS = ["k8s", "docker", "aws", "azure", "gcp", "helm", "fountain"];
13
13
 
14
14
  describe("detectTemplate modules are edge-bundle clean", () => {
15
15
  test("importing every lexicon's /detect pulls in no typescript or plugin.ts", async () => {