@intentius/chant 0.49.0 → 0.50.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 (247) hide show
  1. package/dist/audit/catalog.d.ts +13 -3
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/core.d.ts +9 -0
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts +6 -0
  6. package/dist/audit/discover.d.ts.map +1 -1
  7. package/dist/audit/fetch.d.ts.map +1 -1
  8. package/dist/audit/report-html.d.ts.map +1 -1
  9. package/dist/audit/report-model.d.ts +6 -0
  10. package/dist/audit/report-model.d.ts.map +1 -1
  11. package/dist/audit/report.d.ts.map +1 -1
  12. package/dist/audit/rules-doc.d.ts.map +1 -1
  13. package/dist/audit/secrets.d.ts +95 -0
  14. package/dist/audit/secrets.d.ts.map +1 -0
  15. package/dist/audit/wrangler.d.ts +33 -0
  16. package/dist/audit/wrangler.d.ts.map +1 -0
  17. package/dist/build.d.ts.map +1 -1
  18. package/dist/cli/commands/audit.d.ts +7 -0
  19. package/dist/cli/commands/audit.d.ts.map +1 -1
  20. package/dist/cli/commands/build.d.ts +23 -0
  21. package/dist/cli/commands/build.d.ts.map +1 -1
  22. package/dist/cli/handlers/build.d.ts.map +1 -1
  23. package/dist/cli/handlers/components.d.ts +31 -0
  24. package/dist/cli/handlers/components.d.ts.map +1 -1
  25. package/dist/cli/handlers/lifecycle.d.ts +11 -0
  26. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  27. package/dist/cli/handlers/operator.d.ts +32 -0
  28. package/dist/cli/handlers/operator.d.ts.map +1 -0
  29. package/dist/cli/handlers/scenario.d.ts +39 -0
  30. package/dist/cli/handlers/scenario.d.ts.map +1 -0
  31. package/dist/cli/main.d.ts.map +1 -1
  32. package/dist/cli/mcp/server.d.ts +35 -2
  33. package/dist/cli/mcp/server.d.ts.map +1 -1
  34. package/dist/cli/mcp/types.d.ts +29 -1
  35. package/dist/cli/mcp/types.d.ts.map +1 -1
  36. package/dist/cli/registry.d.ts +14 -2
  37. package/dist/cli/registry.d.ts.map +1 -1
  38. package/dist/codegen/docs-rule-scanning.d.ts.map +1 -1
  39. package/dist/components/capability.d.ts +17 -2
  40. package/dist/components/capability.d.ts.map +1 -1
  41. package/dist/components/cli-support.d.ts +7 -0
  42. package/dist/components/cli-support.d.ts.map +1 -1
  43. package/dist/components/component.d.ts +15 -0
  44. package/dist/components/component.d.ts.map +1 -1
  45. package/dist/components/driver.d.ts.map +1 -1
  46. package/dist/components/verbs/index.d.ts +6 -1
  47. package/dist/components/verbs/index.d.ts.map +1 -1
  48. package/dist/components/verbs/run-agent.d.ts +499 -0
  49. package/dist/components/verbs/run-agent.d.ts.map +1 -0
  50. package/dist/components/verbs/sign.d.ts +30 -0
  51. package/dist/components/verbs/sign.d.ts.map +1 -1
  52. package/dist/composite.d.ts +6 -1
  53. package/dist/composite.d.ts.map +1 -1
  54. package/dist/discovery/collect.d.ts.map +1 -1
  55. package/dist/discovery/fold-import.d.ts +15 -1
  56. package/dist/discovery/fold-import.d.ts.map +1 -1
  57. package/dist/discovery/fold-rank.d.ts +66 -0
  58. package/dist/discovery/fold-rank.d.ts.map +1 -0
  59. package/dist/discovery/index.d.ts +15 -0
  60. package/dist/discovery/index.d.ts.map +1 -1
  61. package/dist/discovery/param-deps.d.ts +17 -0
  62. package/dist/discovery/param-deps.d.ts.map +1 -0
  63. package/dist/fold/fold.d.ts +55 -2
  64. package/dist/fold/fold.d.ts.map +1 -1
  65. package/dist/fold/subset.d.ts +21 -14
  66. package/dist/fold/subset.d.ts.map +1 -1
  67. package/dist/lexicon-schema.d.ts +2 -0
  68. package/dist/lexicon-schema.d.ts.map +1 -1
  69. package/dist/lexicon.d.ts +93 -0
  70. package/dist/lexicon.d.ts.map +1 -1
  71. package/dist/lifecycle/converge-ledger.d.ts +90 -0
  72. package/dist/lifecycle/converge-ledger.d.ts.map +1 -0
  73. package/dist/lifecycle/deep-diff.d.ts +18 -0
  74. package/dist/lifecycle/deep-diff.d.ts.map +1 -1
  75. package/dist/lifecycle/deep-observe.d.ts +9 -1
  76. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  77. package/dist/lifecycle/gate-ledger.d.ts +33 -0
  78. package/dist/lifecycle/gate-ledger.d.ts.map +1 -0
  79. package/dist/lifecycle/git.d.ts +145 -21
  80. package/dist/lifecycle/git.d.ts.map +1 -1
  81. package/dist/lifecycle/index.d.ts +4 -0
  82. package/dist/lifecycle/index.d.ts.map +1 -1
  83. package/dist/lifecycle/lease.d.ts +113 -0
  84. package/dist/lifecycle/lease.d.ts.map +1 -0
  85. package/dist/lifecycle/scenario-eval.d.ts +42 -0
  86. package/dist/lifecycle/scenario-eval.d.ts.map +1 -0
  87. package/dist/lifecycle/scenario.d.ts +163 -0
  88. package/dist/lifecycle/scenario.d.ts.map +1 -0
  89. package/dist/lifecycle/symptoms.d.ts +63 -0
  90. package/dist/lifecycle/symptoms.d.ts.map +1 -0
  91. package/dist/lint/output-docs.d.ts +94 -0
  92. package/dist/lint/output-docs.d.ts.map +1 -0
  93. package/dist/lint/post-synth.d.ts +29 -0
  94. package/dist/lint/post-synth.d.ts.map +1 -1
  95. package/dist/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.d.ts +11 -0
  96. package/dist/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.d.ts.map +1 -0
  97. package/dist/lsp/lexicon-providers.d.ts +7 -0
  98. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  99. package/dist/op/activity-contract.d.ts +139 -0
  100. package/dist/op/activity-contract.d.ts.map +1 -0
  101. package/dist/op/builders.d.ts +42 -2
  102. package/dist/op/builders.d.ts.map +1 -1
  103. package/dist/op/converge-rule.d.ts +161 -0
  104. package/dist/op/converge-rule.d.ts.map +1 -0
  105. package/dist/op/generate-pipeline.d.ts +39 -0
  106. package/dist/op/generate-pipeline.d.ts.map +1 -0
  107. package/dist/op/index.d.ts +14 -0
  108. package/dist/op/index.d.ts.map +1 -1
  109. package/dist/op/local-executor.d.ts.map +1 -1
  110. package/dist/op/op-verb-class.d.ts +42 -0
  111. package/dist/op/op-verb-class.d.ts.map +1 -0
  112. package/dist/op/operator.d.ts +128 -0
  113. package/dist/op/operator.d.ts.map +1 -0
  114. package/dist/op/step-output-ref.d.ts +187 -0
  115. package/dist/op/step-output-ref.d.ts.map +1 -0
  116. package/dist/op/types.d.ts +18 -1
  117. package/dist/op/types.d.ts.map +1 -1
  118. package/dist/provenance.d.ts +73 -3
  119. package/dist/provenance.d.ts.map +1 -1
  120. package/dist/runtime-adapter.d.ts +7 -1
  121. package/dist/runtime-adapter.d.ts.map +1 -1
  122. package/dist/serializer.d.ts +18 -0
  123. package/dist/serializer.d.ts.map +1 -1
  124. package/dist/toml.d.ts +40 -5
  125. package/dist/toml.d.ts.map +1 -1
  126. package/package.json +1 -1
  127. package/src/audit/catalog.test.ts +1 -1
  128. package/src/audit/catalog.ts +75 -3
  129. package/src/audit/core.ts +9 -0
  130. package/src/audit/discover.ts +29 -2
  131. package/src/audit/fetch.test.ts +216 -3
  132. package/src/audit/fetch.ts +270 -59
  133. package/src/audit/report-html.ts +5 -2
  134. package/src/audit/report-model.ts +9 -0
  135. package/src/audit/report.test.ts +22 -0
  136. package/src/audit/report.ts +3 -2
  137. package/src/audit/rules-doc.ts +2 -0
  138. package/src/audit/secrets.test.ts +303 -0
  139. package/src/audit/secrets.ts +406 -0
  140. package/src/audit/wrangler.test.ts +230 -0
  141. package/src/audit/wrangler.ts +290 -0
  142. package/src/build.ts +8 -3
  143. package/src/cli/command-group.ts +1 -1
  144. package/src/cli/commands/__fixtures__/schemas/sarif-2.1.0.schema.json +2882 -0
  145. package/src/cli/commands/audit.test.ts +215 -1
  146. package/src/cli/commands/audit.ts +86 -17
  147. package/src/cli/commands/build.test.ts +167 -2
  148. package/src/cli/commands/build.ts +114 -23
  149. package/src/cli/handlers/build.ts +2 -0
  150. package/src/cli/handlers/components.test.ts +199 -1
  151. package/src/cli/handlers/components.ts +160 -3
  152. package/src/cli/handlers/graph.test.ts +20 -0
  153. package/src/cli/handlers/graph.ts +10 -1
  154. package/src/cli/handlers/lifecycle.ts +12 -4
  155. package/src/cli/handlers/operator.test.ts +255 -0
  156. package/src/cli/handlers/operator.ts +240 -0
  157. package/src/cli/handlers/scenario.test.ts +456 -0
  158. package/src/cli/handlers/scenario.ts +330 -0
  159. package/src/cli/main.test.ts +23 -0
  160. package/src/cli/main.ts +72 -1
  161. package/src/cli/mcp/server.test.ts +265 -2
  162. package/src/cli/mcp/server.ts +84 -7
  163. package/src/cli/mcp/types.ts +27 -1
  164. package/src/cli/registry.ts +14 -2
  165. package/src/codegen/docs-rule-scanning.test.ts +42 -0
  166. package/src/codegen/docs-rule-scanning.ts +25 -2
  167. package/src/components/README.md +7 -0
  168. package/src/components/capability.ts +17 -2
  169. package/src/components/cli-support.test.ts +17 -0
  170. package/src/components/cli-support.ts +13 -1
  171. package/src/components/component-schema.test.ts +32 -0
  172. package/src/components/component.schema.json +6 -0
  173. package/src/components/component.test.ts +21 -0
  174. package/src/components/component.ts +15 -0
  175. package/src/components/driver.ts +12 -4
  176. package/src/components/verbs/index.ts +6 -1
  177. package/src/components/verbs/run-agent.test.ts +683 -0
  178. package/src/components/verbs/run-agent.ts +786 -0
  179. package/src/components/verbs/sign.test.ts +19 -0
  180. package/src/components/verbs/sign.ts +34 -2
  181. package/src/composite.ts +31 -2
  182. package/src/discovery/collect.ts +11 -2
  183. package/src/discovery/fold-import.test.ts +54 -0
  184. package/src/discovery/fold-import.ts +178 -38
  185. package/src/discovery/fold-rank.test.ts +197 -0
  186. package/src/discovery/fold-rank.ts +346 -0
  187. package/src/discovery/index.ts +16 -1
  188. package/src/discovery/param-deps.test.ts +118 -0
  189. package/src/discovery/param-deps.ts +170 -0
  190. package/src/fold/fold.test.ts +6 -2
  191. package/src/fold/fold.ts +184 -3
  192. package/src/fold/subset.test.ts +82 -19
  193. package/src/fold/subset.ts +79 -41
  194. package/src/lexicon-schema.ts +3 -0
  195. package/src/lexicon.ts +103 -2
  196. package/src/lifecycle/converge-ledger.test.ts +199 -0
  197. package/src/lifecycle/converge-ledger.ts +179 -0
  198. package/src/lifecycle/deep-diff.test.ts +79 -1
  199. package/src/lifecycle/deep-diff.ts +23 -0
  200. package/src/lifecycle/deep-observe.ts +13 -2
  201. package/src/lifecycle/gate-ledger.test.ts +103 -0
  202. package/src/lifecycle/gate-ledger.ts +140 -0
  203. package/src/lifecycle/git.test.ts +430 -0
  204. package/src/lifecycle/git.ts +446 -84
  205. package/src/lifecycle/index.ts +4 -0
  206. package/src/lifecycle/lease.test.ts +343 -0
  207. package/src/lifecycle/lease.ts +270 -0
  208. package/src/lifecycle/scenario-eval.test.ts +199 -0
  209. package/src/lifecycle/scenario-eval.ts +158 -0
  210. package/src/lifecycle/scenario.test.ts +195 -0
  211. package/src/lifecycle/scenario.ts +321 -0
  212. package/src/lifecycle/symptoms.test.ts +116 -0
  213. package/src/lifecycle/symptoms.ts +126 -0
  214. package/src/lint/output-docs.test.ts +220 -0
  215. package/src/lint/output-docs.ts +204 -0
  216. package/src/lint/post-synth.test.ts +97 -0
  217. package/src/lint/post-synth.ts +45 -0
  218. package/src/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.ts +26 -0
  219. package/src/lint/rules/comp/comp.test.ts +49 -1
  220. package/src/lint/rules/evl001-non-literal-expression.test.ts +8 -3
  221. package/src/lint/rules/evl001-non-literal-expression.ts +6 -6
  222. package/src/lsp/lexicon-providers.test.ts +44 -0
  223. package/src/lsp/lexicon-providers.ts +11 -1
  224. package/src/op/activity-contract.test.ts +180 -0
  225. package/src/op/activity-contract.ts +278 -0
  226. package/src/op/builders-exports.test.ts +17 -1
  227. package/src/op/builders.ts +59 -5
  228. package/src/op/converge-rule.test.ts +179 -0
  229. package/src/op/converge-rule.ts +311 -0
  230. package/src/op/generate-pipeline.test.ts +53 -0
  231. package/src/op/generate-pipeline.ts +99 -0
  232. package/src/op/index.ts +30 -0
  233. package/src/op/local-executor.test.ts +92 -0
  234. package/src/op/local-executor.ts +45 -9
  235. package/src/op/op-verb-class.test.ts +126 -0
  236. package/src/op/op-verb-class.ts +115 -0
  237. package/src/op/operator.test.ts +346 -0
  238. package/src/op/operator.ts +213 -0
  239. package/src/op/step-output-ref.test.ts +334 -0
  240. package/src/op/step-output-ref.ts +453 -0
  241. package/src/op/types.ts +18 -1
  242. package/src/provenance.test.ts +151 -4
  243. package/src/provenance.ts +118 -4
  244. package/src/runtime-adapter.ts +31 -10
  245. package/src/serializer.ts +18 -0
  246. package/src/toml.test.ts +157 -384
  247. package/src/toml.ts +371 -5
package/src/provenance.ts CHANGED
@@ -5,9 +5,11 @@
5
5
  * entity object, so it never serializes into the emitted YAML/JSON — it is build
6
6
  * metadata, not declared configuration. Read it back with {@link getProvenance}.
7
7
  *
8
- * This is entity-level provenance (which file declared it, and which composite
9
- * expanded it), not a YAML-line source map. It answers "where did this resource
10
- * come from?", which is the question an agent asks before changing it.
8
+ * Entity-level provenance (which file declared it, and which composite expanded
9
+ * it) answers "where did this resource come from?", which is the question an
10
+ * agent asks before changing it. {@link PathOrigin} (chant #1443) answers the
11
+ * follow-up — "what set THIS field" — for the paths a build can attribute
12
+ * without guessing. Neither is a YAML-line source map.
11
13
  *
12
14
  * {@link BuildParamProvenance} (chant #1064) is the other half: not "where did
13
15
  * this ENTITY come from" but "what INPUTS was this whole BUILD invoked with" —
@@ -30,6 +32,58 @@ export interface EntityProvenance {
30
32
  * collapse a composite to a single node at coarse diagram detail levels (#494).
31
33
  */
32
34
  compositeInstance?: string;
35
+ /**
36
+ * Per-path origins within the entity's `props` (chant #1443). Read with
37
+ * {@link originOfPath}, which does the prefix resolution these keys are
38
+ * written for.
39
+ *
40
+ * Keys are dotted property paths only — `spec.template.spec.containers` —
41
+ * with `""` for the whole entity. They never carry an array segment, because
42
+ * a producing mechanism attributes a whole array at once: the coarsest path
43
+ * it actually determines is the honest one to record, and a positional key
44
+ * would go stale the moment an element moved.
45
+ *
46
+ * Query paths come from #1441's diff addressing and may carry `[#key]` for an
47
+ * element of an associative list or `[n]` for a positional one. Resolution is
48
+ * longest segment-boundary prefix, so `spec.containers[#app].image` finds a
49
+ * key recorded at `spec.containers`, and a key recorded at
50
+ * `spec.containers.image` never matches it. The two grammars agree without
51
+ * the recorder needing the lexicon's ordering hook, which it has no access to.
52
+ *
53
+ * Absent means this build could not record path origins, not that nothing
54
+ * governs the entity's fields — the run path executes modules and has no
55
+ * expression to inspect, and a sandboxed child's provenance does not survive
56
+ * the wire (`discovery/entity-wire-codec.ts` drops build metadata).
57
+ */
58
+ paths?: Record<string, PathOrigin>;
59
+ }
60
+
61
+ /**
62
+ * What produced one path inside an entity's props.
63
+ *
64
+ * - `authored` — written in the source file that declared the entity.
65
+ * - `composite` — the entity was expanded by a composite, or the path was
66
+ * supplied by `propagate()`'s shared props at the instance.
67
+ * - `build-param` — the build parameters the authored expression for this path
68
+ * *reads*, which is a syntactic dependency, not a value taint. For
69
+ * `replicas: params.tier === "prod" ? 5 : 1` a taint says the value came from
70
+ * the literal `5`, which is true and useless; the dependency says the field
71
+ * is governed by `tier`, which is what an editor needs to know. Under-reports
72
+ * (a shape the collector does not follow drops a dependency) and never
73
+ * over-reports a parameter the expression does not mention.
74
+ */
75
+ export type PathOrigin =
76
+ | { kind: "authored" }
77
+ | { kind: "composite"; composite: string; instance: string }
78
+ | { kind: "build-param"; params: string[] };
79
+
80
+ /** True when `prefix` addresses `path` or an ancestor of it, on a segment boundary. */
81
+ function isPathPrefix(prefix: string, path: string): boolean {
82
+ if (prefix === "") return true;
83
+ if (!path.startsWith(prefix)) return false;
84
+ if (path.length === prefix.length) return true;
85
+ const next = path[prefix.length];
86
+ return next === "." || next === "[";
33
87
  }
34
88
 
35
89
  /**
@@ -44,10 +98,16 @@ export function setProvenance(entity: object, prov: EntityProvenance): void {
44
98
  existing.sourceFile ??= prov.sourceFile;
45
99
  existing.composite ??= prov.composite;
46
100
  existing.compositeInstance ??= prov.compositeInstance;
101
+ if (prov.paths) {
102
+ existing.paths ??= {};
103
+ for (const [path, origin] of Object.entries(prov.paths)) {
104
+ existing.paths[path] ??= origin;
105
+ }
106
+ }
47
107
  return;
48
108
  }
49
109
  Object.defineProperty(entity, PROVENANCE, {
50
- value: { ...prov },
110
+ value: { ...prov, ...(prov.paths ? { paths: { ...prov.paths } } : {}) },
51
111
  enumerable: false,
52
112
  writable: true,
53
113
  configurable: true,
@@ -59,6 +119,60 @@ export function getProvenance(entity: object): EntityProvenance | undefined {
59
119
  return (entity as Record<symbol, unknown>)[PROVENANCE] as EntityProvenance | undefined;
60
120
  }
61
121
 
122
+ /**
123
+ * Record one path's origin, first writer wins — the same rule
124
+ * {@link setProvenance} applies to entity-level fields, and for the same
125
+ * reason: expansion runs innermost-composite-first, so the earliest writer is
126
+ * the most specific one.
127
+ */
128
+ export function setPathProvenance(entity: object, path: string, origin: PathOrigin): void {
129
+ setProvenance(entity, { paths: { [path]: origin } });
130
+ }
131
+
132
+ /**
133
+ * An entity's recorded path origins in sorted key order, or `undefined` when
134
+ * none were recorded. Sorted because a build's outputs are compared byte for
135
+ * byte and object key order is not something insertion should decide.
136
+ */
137
+ export function getPathProvenance(entity: object): Record<string, PathOrigin> | undefined {
138
+ const paths = getProvenance(entity)?.paths;
139
+ if (!paths) return undefined;
140
+ const keys = Object.keys(paths).sort();
141
+ if (keys.length === 0) return undefined;
142
+ const out: Record<string, PathOrigin> = {};
143
+ for (const key of keys) out[key] = paths[key];
144
+ return out;
145
+ }
146
+
147
+ /**
148
+ * The origin governing `path` — the longest recorded key that addresses it or
149
+ * one of its ancestors. See {@link PROVENANCE_PATH_GRAMMAR}.
150
+ */
151
+ export function originOfPath(
152
+ paths: Readonly<Record<string, PathOrigin>> | undefined,
153
+ path: string,
154
+ ): PathOrigin | undefined {
155
+ if (!paths) return undefined;
156
+ let best: string | undefined;
157
+ for (const key of Object.keys(paths)) {
158
+ if (!isPathPrefix(key, path)) continue;
159
+ if (best === undefined || key.length > best.length) best = key;
160
+ }
161
+ return best === undefined ? undefined : paths[best];
162
+ }
163
+
164
+ /** One-line rendering of an origin, for diff output. */
165
+ export function describePathOrigin(origin: PathOrigin): string {
166
+ switch (origin.kind) {
167
+ case "authored":
168
+ return "authored";
169
+ case "composite":
170
+ return `composite ${origin.composite} (${origin.instance})`;
171
+ case "build-param":
172
+ return `param ${origin.params.join(", ")}`;
173
+ }
174
+ }
175
+
62
176
  /**
63
177
  * One resolved build-time parameter (chant #1064, see ../build-params.ts): its
64
178
  * final value and which source won it, so a build's parameter inputs are
@@ -34,8 +34,13 @@ export interface RuntimeAdapter {
34
34
  readonly hashAlgorithm: string;
35
35
  /** Test whether filePath matches a glob pattern */
36
36
  globMatch(pattern: string, filePath: string): boolean;
37
- /** Spawn a child process and collect output */
38
- spawn(cmd: string[], opts?: { cwd?: string }): Promise<SpawnResult>;
37
+ /**
38
+ * Spawn a child process and collect output. When `opts.stdin` is set, it is
39
+ * written to the child's stdin and the stream is closed — the caller never
40
+ * needs to round-trip content through a shell (`sh -c "echo … | cmd"`) just
41
+ * to feed a command that reads from stdin (e.g. `git hash-object --stdin`).
42
+ */
43
+ spawn(cmd: string[], opts?: { cwd?: string; stdin?: string }): Promise<SpawnResult>;
39
44
  /** Commands to use when spawning package manager / executor */
40
45
  readonly commands: RuntimeCommands;
41
46
  }
@@ -59,16 +64,32 @@ class NodeRuntimeAdapter implements RuntimeAdapter {
59
64
  return picomatch(pattern)(filePath);
60
65
  }
61
66
 
62
- async spawn(cmd: string[], opts?: { cwd?: string }): Promise<SpawnResult> {
67
+ async spawn(cmd: string[], opts?: { cwd?: string; stdin?: string }): Promise<SpawnResult> {
63
68
  return new Promise((resolve) => {
64
- execFile(cmd[0], cmd.slice(1), { cwd: opts?.cwd, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
65
- resolve({
66
- stdout: stdout ?? "",
67
- stderr: stderr ?? "",
68
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
69
- exitCode: err ? (err as any).code ?? 1 : 0,
69
+ const child = execFile(
70
+ cmd[0],
71
+ cmd.slice(1),
72
+ { cwd: opts?.cwd, maxBuffer: 10 * 1024 * 1024 },
73
+ (err, stdout, stderr) => {
74
+ resolve({
75
+ stdout: stdout ?? "",
76
+ stderr: stderr ?? "",
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ exitCode: err ? (err as any).code ?? 1 : 0,
79
+ });
80
+ },
81
+ );
82
+ // Content is written verbatim (utf-8) — no shell involved, so nothing
83
+ // reinterprets backslash-escape sequences or needs quote-escaping.
84
+ // The error handler is load-bearing: a child that exits without reading
85
+ // stdin (any command that ignores it) makes this write race the pipe
86
+ // teardown, and an unhandled EPIPE 'error' event crashes the process.
87
+ if (child.stdin) {
88
+ child.stdin.on("error", () => {
89
+ /* EPIPE: child exited without consuming stdin — harmless */
70
90
  });
71
- });
91
+ child.stdin.end(opts?.stdin ?? "");
92
+ }
72
93
  });
73
94
  }
74
95
  }
package/src/serializer.ts CHANGED
@@ -42,6 +42,24 @@ export interface SerializerResult {
42
42
  primary: string;
43
43
  /** Additional files keyed by filename (e.g. "network.template.json" → content) */
44
44
  files?: Record<string, string>;
45
+ /**
46
+ * Basenames from `files` that must reach disk byte-for-byte — never
47
+ * `JSON.parse`d, key-sorted, or reformatted to YAML by the build's
48
+ * additional-file writer (chant#1937).
49
+ *
50
+ * A separate field rather than a per-file flag on `files` (e.g. widening
51
+ * its value to `string | { content: string; verbatim: boolean }`) so every
52
+ * existing lexicon serializer that returns `files: Record<string, string>`
53
+ * keeps compiling and behaving unchanged: this list defaults to empty, and
54
+ * only a serializer that actually carries opaque/pre-formatted bytes
55
+ * (committed ciphertext, a vendored file) needs to populate it.
56
+ *
57
+ * Before this field existed, "JSON.parse fails on this content" was the
58
+ * only way to opt out of round-tripping — true for YAML that happens not to
59
+ * parse as JSON, but never a structural guarantee (YAML is a JSON
60
+ * superset).
61
+ */
62
+ verbatimFiles?: string[];
45
63
  /**
46
64
  * Non-fatal diagnostics produced during serialization (e.g. a dialect
47
65
  * dropping keys the target platform ignores). The build pipeline collects