@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
package/src/yaml.test.ts CHANGED
@@ -226,6 +226,94 @@ describe("parseScalar", () => {
226
226
  expect(parseScalar("hello")).toBe("hello");
227
227
  });
228
228
  });
229
+ // Quoted scalars (#1860) — stripping the delimiters is only half the job; the
230
+ // escapes inside them have to be decoded too. A double-quoted `\n` that stayed
231
+ // two literal characters is what sent a seven-line setup script to a shell as
232
+ // one line.
233
+ describe("parseScalar quoted scalars (#1860)", () => {
234
+ test("double-quoted escapes decode", () => {
235
+ expect(parseScalar(String.raw`"a\nb"`)).toBe("a\nb");
236
+ expect(parseScalar(String.raw`"a\tb"`)).toBe("a\tb");
237
+ expect(parseScalar(String.raw`"say \"hi\""`)).toBe('say "hi"');
238
+ expect(parseScalar(String.raw`"back\\slash"`)).toBe("back\\slash");
239
+ expect(parseScalar(String.raw`"a\/b"`)).toBe("a/b");
240
+ });
241
+
242
+ test("hex, unicode and 32-bit escapes", () => {
243
+ expect(parseScalar(String.raw`"\x41"`)).toBe("A");
244
+ expect(parseScalar(String.raw`"A"`)).toBe("A");
245
+ expect(parseScalar(String.raw`"\U0001F600"`)).toBe("\u{1F600}");
246
+ });
247
+
248
+ test("an escaped line break folds away with its indentation", () => {
249
+ expect(parseScalar('"a\\\n b"')).toBe("ab");
250
+ });
251
+
252
+ test("single-quoted '' is one quote", () => {
253
+ expect(parseScalar("'it''s'")).toBe("it's");
254
+ expect(parseScalar("'''quoted'''")).toBe("'quoted'");
255
+ });
256
+
257
+ test("a lone quote character is not a quoted scalar", () => {
258
+ expect(parseScalar('"')).toBe('"');
259
+ expect(parseScalar("'")).toBe("'");
260
+ });
261
+
262
+ test("a body with a bare quote keeps the old strip-only behaviour", () => {
263
+ expect(parseScalar('"a" + "b"')).toBe('a" + "b');
264
+ });
265
+
266
+ test("an unknown or truncated escape keeps the old strip-only behaviour", () => {
267
+ expect(parseScalar(String.raw`"a\qb"`)).toBe(String.raw`a\qb`);
268
+ expect(parseScalar(String.raw`"\x4"`)).toBe(String.raw`\x4`);
269
+ });
270
+ });
271
+
272
+ // Round-trip parity for the scalars `emitYAML` quotes: what it writes,
273
+ // `parseYAML` must read back unchanged. The single-quote half of #1860 was
274
+ // invisible until this existed. Multiline strings are excluded on purpose —
275
+ // the emitter sends those through a block scalar, which #910 already covers
276
+ // and which does not preserve trailing newlines.
277
+ describe("emitYAML/parseYAML round trip (#1860)", () => {
278
+ const values = [
279
+ "'quoted'",
280
+ "#hash's",
281
+ "it's a test",
282
+ "plain",
283
+ "yes",
284
+ "a: b",
285
+ "$VAR",
286
+ "",
287
+ ];
288
+
289
+ for (const v of values) {
290
+ test(`round-trips ${JSON.stringify(v)}`, () => {
291
+ expect(parseYAML(`k: ${emitYAML(v, 0)}\n`).k).toBe(v);
292
+ });
293
+ }
294
+ });
295
+
296
+ // The fountain lexicon emits multiline strings as `JSON.stringify(s)` — a
297
+ // double-quoted scalar, not a block scalar — so this is the shape that
298
+ // actually reached the applier (#1860).
299
+ describe("double-quoted multiline manifest values (#1860)", () => {
300
+ test("a joined shell script keeps its newlines", () => {
301
+ const script = [
302
+ "set -e",
303
+ "sudo service postgresql start || true",
304
+ `sudo -u postgres psql -tc "ALTER USER postgres PASSWORD 'postgres'" || true`,
305
+ "cd /workspace/fountain",
306
+ ].join("\n");
307
+
308
+ const yaml = `spec:\n setup_script: ${JSON.stringify(script)}\n`;
309
+ const parsed = parseYAML(yaml) as { spec: { setup_script: string } };
310
+
311
+ expect(parsed.spec.setup_script).toBe(script);
312
+ expect(parsed.spec.setup_script.split("\n")).toHaveLength(4);
313
+ expect(parsed.spec.setup_script).not.toContain(String.raw`\n`);
314
+ });
315
+ });
316
+
229
317
 
230
318
  // Block scalars (#910) — round-trip parity with js-yaml for literal/folded/chomping,
231
319
  // including block scalars nested inside array items (the case that mis-parsed).
package/src/yaml.ts CHANGED
@@ -559,15 +559,85 @@ export function parseScalar(value: string): unknown {
559
559
  if (value === "" || value === "~" || value === "null") return null;
560
560
  if (value === "true" || value === "yes") return true;
561
561
  if (value === "false" || value === "no") return false;
562
- // Strip quotes
563
- if (
564
- (value.startsWith("'") && value.endsWith("'")) ||
565
- (value.startsWith('"') && value.endsWith('"'))
566
- ) {
567
- return value.slice(1, -1);
562
+ // Quoted scalars carry escapes, and stripping the quotes is only half the
563
+ // job: a double-quoted `\n` is a newline, a single-quoted `''` is one
564
+ // quote. Dropping that step leaves the escape sequence in the value as
565
+ // literal characters, which is how a multiline `setup_script` reached a
566
+ // shell as one line of `set -e\nsudo ...` (#1860).
567
+ if (value.length >= 2) {
568
+ if (value.startsWith('"') && value.endsWith('"')) {
569
+ const unescaped = unescapeDoubleQuoted(value.slice(1, -1));
570
+ // `null` means the body held a bare `"`, so this was never a single
571
+ // well-formed scalar (`"a" + "b"`). Keep the old behaviour there.
572
+ return unescaped ?? value.slice(1, -1);
573
+ }
574
+ if (value.startsWith("'") && value.endsWith("'")) {
575
+ return value.slice(1, -1).replace(/''/g, "'");
576
+ }
568
577
  }
569
578
  // Number
570
579
  const num = Number(value);
571
580
  if (!isNaN(num) && value !== "") return num;
572
581
  return value;
573
582
  }
583
+
584
+ /** YAML double-quoted escapes, per the spec's escape table. */
585
+ const DQ_ESCAPES: Record<string, string> = {
586
+ "0": "\0",
587
+ a: "\x07",
588
+ b: "\b",
589
+ t: "\t",
590
+ "\t": "\t",
591
+ n: "\n",
592
+ v: "\v",
593
+ f: "\f",
594
+ r: "\r",
595
+ e: "\x1b",
596
+ " ": " ",
597
+ '"': '"',
598
+ "/": "/",
599
+ "\\": "\\",
600
+ N: "\x85",
601
+ _: "\xa0",
602
+ L: "\u2028",
603
+ P: "\u2029",
604
+ };
605
+
606
+ /**
607
+ * Decode the body of a double-quoted YAML scalar.
608
+ *
609
+ * Returns `null` when the body holds an unescaped `"`, which means the caller
610
+ * was not looking at one quoted scalar after all.
611
+ */
612
+ function unescapeDoubleQuoted(body: string): string | null {
613
+ let out = "";
614
+ for (let i = 0; i < body.length; i++) {
615
+ const ch = body[i];
616
+ if (ch === '"') return null;
617
+ if (ch !== "\\") {
618
+ out += ch;
619
+ continue;
620
+ }
621
+ const esc = body[++i];
622
+ if (esc === undefined) return null;
623
+ // \xXX, \uXXXX, \UXXXXXXXX
624
+ const width = esc === "x" ? 2 : esc === "u" ? 4 : esc === "U" ? 8 : 0;
625
+ if (width > 0) {
626
+ const hex = body.slice(i + 1, i + 1 + width);
627
+ if (hex.length < width || !/^[0-9a-fA-F]+$/.test(hex)) return null;
628
+ out += String.fromCodePoint(parseInt(hex, 16));
629
+ i += width;
630
+ continue;
631
+ }
632
+ // An escaped line break is a line continuation: it and the following
633
+ // indentation fold away to nothing.
634
+ if (esc === "\n") {
635
+ while (body[i + 1] === " " || body[i + 1] === "\t") i++;
636
+ continue;
637
+ }
638
+ const mapped = DQ_ESCAPES[esc];
639
+ if (mapped === undefined) return null;
640
+ out += mapped;
641
+ }
642
+ return out;
643
+ }