ppef 1.0.1 → 1.2.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 (295) hide show
  1. package/README.md +76 -125
  2. package/bin/ppef.mjs +20 -0
  3. package/dist/__tests__/cli/evaluate-command.integration.test.d.ts +8 -0
  4. package/dist/__tests__/cli/evaluate-command.integration.test.d.ts.map +1 -0
  5. package/dist/__tests__/cli/evaluate-command.integration.test.js +308 -0
  6. package/dist/__tests__/cli/evaluate-command.integration.test.js.map +1 -0
  7. package/dist/__tests__/evaluators/claims-evaluator.unit.test.d.ts +8 -0
  8. package/dist/__tests__/evaluators/claims-evaluator.unit.test.d.ts.map +1 -0
  9. package/dist/__tests__/evaluators/claims-evaluator.unit.test.js +405 -0
  10. package/dist/__tests__/evaluators/claims-evaluator.unit.test.js.map +1 -0
  11. package/dist/__tests__/evaluators/metrics-evaluator.unit.test.d.ts +8 -0
  12. package/dist/__tests__/evaluators/metrics-evaluator.unit.test.d.ts.map +1 -0
  13. package/dist/__tests__/evaluators/metrics-evaluator.unit.test.js +424 -0
  14. package/dist/__tests__/evaluators/metrics-evaluator.unit.test.js.map +1 -0
  15. package/dist/__tests__/evaluators/registry.unit.test.d.ts +7 -0
  16. package/dist/__tests__/evaluators/registry.unit.test.d.ts.map +1 -0
  17. package/dist/__tests__/evaluators/registry.unit.test.js +173 -0
  18. package/dist/__tests__/evaluators/registry.unit.test.js.map +1 -0
  19. package/dist/__tests__/evaluators/robustness-evaluator.unit.test.d.ts +8 -0
  20. package/dist/__tests__/evaluators/robustness-evaluator.unit.test.d.ts.map +1 -0
  21. package/dist/__tests__/evaluators/robustness-evaluator.unit.test.js +260 -0
  22. package/dist/__tests__/evaluators/robustness-evaluator.unit.test.js.map +1 -0
  23. package/dist/__tests__/framework-pipeline.integration.test.js +49 -20
  24. package/dist/__tests__/framework-pipeline.integration.test.js.map +1 -1
  25. package/dist/__tests__/index-exports.unit.test.d.ts +8 -0
  26. package/dist/__tests__/index-exports.unit.test.d.ts.map +1 -0
  27. package/dist/__tests__/index-exports.unit.test.js +124 -0
  28. package/dist/__tests__/index-exports.unit.test.js.map +1 -0
  29. package/dist/__tests__/registry-executor.integration.test.js +12 -9
  30. package/dist/__tests__/registry-executor.integration.test.js.map +1 -1
  31. package/dist/aggregation/__tests__/aggregators.unit.test.d.ts +7 -0
  32. package/dist/aggregation/__tests__/aggregators.unit.test.d.ts.map +1 -0
  33. package/dist/aggregation/__tests__/aggregators.unit.test.js +350 -0
  34. package/dist/aggregation/__tests__/aggregators.unit.test.js.map +1 -0
  35. package/dist/aggregation/__tests__/pipeline.unit.test.d.ts +7 -0
  36. package/dist/aggregation/__tests__/pipeline.unit.test.d.ts.map +1 -0
  37. package/dist/aggregation/__tests__/pipeline.unit.test.js +213 -0
  38. package/dist/aggregation/__tests__/pipeline.unit.test.js.map +1 -0
  39. package/dist/aggregation/aggregators.d.ts +9 -0
  40. package/dist/aggregation/aggregators.d.ts.map +1 -1
  41. package/dist/aggregation/aggregators.js +1 -1
  42. package/dist/aggregation/aggregators.js.map +1 -1
  43. package/dist/aggregation/index.d.ts +1 -1
  44. package/dist/aggregation/index.d.ts.map +1 -1
  45. package/dist/aggregation/index.js +1 -1
  46. package/dist/aggregation/index.js.map +1 -1
  47. package/dist/aggregation/pipeline.d.ts.map +1 -1
  48. package/dist/aggregation/pipeline.js +40 -3
  49. package/dist/aggregation/pipeline.js.map +1 -1
  50. package/dist/claims/index.d.ts +6 -3
  51. package/dist/claims/index.d.ts.map +1 -1
  52. package/dist/claims/index.js +6 -3
  53. package/dist/claims/index.js.map +1 -1
  54. package/dist/cli/__tests__/aggregate.command.unit.test.d.ts +7 -0
  55. package/dist/cli/__tests__/aggregate.command.unit.test.d.ts.map +1 -0
  56. package/dist/cli/__tests__/aggregate.command.unit.test.js +399 -0
  57. package/dist/cli/__tests__/aggregate.command.unit.test.js.map +1 -0
  58. package/dist/cli/__tests__/binary-sut.integration.test.d.ts +8 -0
  59. package/dist/cli/__tests__/binary-sut.integration.test.d.ts.map +1 -0
  60. package/dist/cli/__tests__/binary-sut.integration.test.js +165 -0
  61. package/dist/cli/__tests__/binary-sut.integration.test.js.map +1 -0
  62. package/dist/cli/__tests__/commands.unit.test.d.ts +10 -0
  63. package/dist/cli/__tests__/commands.unit.test.d.ts.map +1 -0
  64. package/dist/cli/__tests__/commands.unit.test.js +217 -0
  65. package/dist/cli/__tests__/commands.unit.test.js.map +1 -0
  66. package/dist/cli/__tests__/config-loader.unit.test.d.ts +7 -0
  67. package/dist/cli/__tests__/config-loader.unit.test.d.ts.map +1 -0
  68. package/dist/cli/__tests__/config-loader.unit.test.js +611 -0
  69. package/dist/cli/__tests__/config-loader.unit.test.js.map +1 -0
  70. package/dist/cli/__tests__/index.unit.test.d.ts +10 -0
  71. package/dist/cli/__tests__/index.unit.test.d.ts.map +1 -0
  72. package/dist/cli/__tests__/index.unit.test.js +65 -0
  73. package/dist/cli/__tests__/index.unit.test.js.map +1 -0
  74. package/dist/cli/__tests__/logger.unit.test.d.ts +11 -0
  75. package/dist/cli/__tests__/logger.unit.test.d.ts.map +1 -0
  76. package/dist/cli/__tests__/logger.unit.test.js +180 -0
  77. package/dist/cli/__tests__/logger.unit.test.js.map +1 -0
  78. package/dist/cli/__tests__/module-loader.unit.test.d.ts +11 -0
  79. package/dist/cli/__tests__/module-loader.unit.test.d.ts.map +1 -0
  80. package/dist/cli/__tests__/module-loader.unit.test.js +262 -0
  81. package/dist/cli/__tests__/module-loader.unit.test.js.map +1 -0
  82. package/dist/cli/__tests__/output-writer.unit.test.d.ts +10 -0
  83. package/dist/cli/__tests__/output-writer.unit.test.d.ts.map +1 -0
  84. package/dist/cli/__tests__/output-writer.unit.test.js +216 -0
  85. package/dist/cli/__tests__/output-writer.unit.test.js.map +1 -0
  86. package/dist/cli/__tests__/plan.command.unit.test.d.ts +7 -0
  87. package/dist/cli/__tests__/plan.command.unit.test.d.ts.map +1 -0
  88. package/dist/cli/__tests__/plan.command.unit.test.js +289 -0
  89. package/dist/cli/__tests__/plan.command.unit.test.js.map +1 -0
  90. package/dist/cli/__tests__/run.command.unit.test.d.ts +7 -0
  91. package/dist/cli/__tests__/run.command.unit.test.d.ts.map +1 -0
  92. package/dist/cli/__tests__/run.command.unit.test.js +422 -0
  93. package/dist/cli/__tests__/run.command.unit.test.js.map +1 -0
  94. package/dist/cli/__tests__/validate.command.unit.test.d.ts +7 -0
  95. package/dist/cli/__tests__/validate.command.unit.test.d.ts.map +1 -0
  96. package/dist/cli/__tests__/validate.command.unit.test.js +226 -0
  97. package/dist/cli/__tests__/validate.command.unit.test.js.map +1 -0
  98. package/dist/cli/command-deps.d.ts +137 -0
  99. package/dist/cli/command-deps.d.ts.map +1 -0
  100. package/dist/cli/command-deps.js +7 -0
  101. package/dist/cli/command-deps.js.map +1 -0
  102. package/dist/cli/commands/aggregate.d.ts +35 -0
  103. package/dist/cli/commands/aggregate.d.ts.map +1 -0
  104. package/dist/cli/commands/aggregate.js +124 -0
  105. package/dist/cli/commands/aggregate.js.map +1 -0
  106. package/dist/cli/commands/evaluate.d.ts +41 -0
  107. package/dist/cli/commands/evaluate.d.ts.map +1 -0
  108. package/dist/cli/commands/evaluate.js +287 -0
  109. package/dist/cli/commands/evaluate.js.map +1 -0
  110. package/dist/cli/commands/plan.d.ts +36 -0
  111. package/dist/cli/commands/plan.d.ts.map +1 -0
  112. package/dist/cli/commands/plan.js +109 -0
  113. package/dist/cli/commands/plan.js.map +1 -0
  114. package/dist/cli/commands/run.d.ts +33 -0
  115. package/dist/cli/commands/run.d.ts.map +1 -0
  116. package/dist/cli/commands/run.js +277 -0
  117. package/dist/cli/commands/run.js.map +1 -0
  118. package/dist/cli/commands/validate.d.ts +27 -0
  119. package/dist/cli/commands/validate.d.ts.map +1 -0
  120. package/dist/cli/commands/validate.js +88 -0
  121. package/dist/cli/commands/validate.js.map +1 -0
  122. package/dist/cli/config-loader.d.ts +30 -0
  123. package/dist/cli/config-loader.d.ts.map +1 -0
  124. package/dist/cli/config-loader.js +181 -0
  125. package/dist/cli/config-loader.js.map +1 -0
  126. package/dist/cli/index.d.ts +27 -0
  127. package/dist/cli/index.d.ts.map +1 -0
  128. package/dist/cli/index.js +60 -0
  129. package/dist/cli/index.js.map +1 -0
  130. package/dist/cli/logger.d.ts +75 -0
  131. package/dist/cli/logger.d.ts.map +1 -0
  132. package/dist/cli/logger.js +131 -0
  133. package/dist/cli/logger.js.map +1 -0
  134. package/dist/cli/module-loader.d.ts +68 -0
  135. package/dist/cli/module-loader.d.ts.map +1 -0
  136. package/dist/cli/module-loader.js +134 -0
  137. package/dist/cli/module-loader.js.map +1 -0
  138. package/dist/cli/output-writer.d.ts +51 -0
  139. package/dist/cli/output-writer.d.ts.map +1 -0
  140. package/dist/cli/output-writer.js +65 -0
  141. package/dist/cli/output-writer.js.map +1 -0
  142. package/dist/cli/types.d.ts +193 -0
  143. package/dist/cli/types.d.ts.map +1 -0
  144. package/dist/cli/types.js +7 -0
  145. package/dist/cli/types.js.map +1 -0
  146. package/dist/collector/__tests__/result-collector.unit.test.d.ts +7 -0
  147. package/dist/collector/__tests__/result-collector.unit.test.d.ts.map +1 -0
  148. package/dist/collector/__tests__/result-collector.unit.test.js +1021 -0
  149. package/dist/collector/__tests__/result-collector.unit.test.js.map +1 -0
  150. package/dist/collector/__tests__/schema.unit.test.d.ts +7 -0
  151. package/dist/collector/__tests__/schema.unit.test.d.ts.map +1 -0
  152. package/dist/collector/__tests__/schema.unit.test.js +360 -0
  153. package/dist/collector/__tests__/schema.unit.test.js.map +1 -0
  154. package/dist/evaluators/claims-evaluator.d.ts +87 -0
  155. package/dist/evaluators/claims-evaluator.d.ts.map +1 -0
  156. package/dist/evaluators/claims-evaluator.js +289 -0
  157. package/dist/evaluators/claims-evaluator.js.map +1 -0
  158. package/dist/evaluators/exploratory-evaluator.d.ts +136 -0
  159. package/dist/evaluators/exploratory-evaluator.d.ts.map +1 -0
  160. package/dist/evaluators/exploratory-evaluator.js +545 -0
  161. package/dist/evaluators/exploratory-evaluator.js.map +1 -0
  162. package/dist/evaluators/index.d.ts +13 -0
  163. package/dist/evaluators/index.d.ts.map +1 -0
  164. package/dist/evaluators/index.js +14 -0
  165. package/dist/evaluators/index.js.map +1 -0
  166. package/dist/evaluators/metrics-evaluator.d.ts +114 -0
  167. package/dist/evaluators/metrics-evaluator.d.ts.map +1 -0
  168. package/dist/evaluators/metrics-evaluator.js +433 -0
  169. package/dist/evaluators/metrics-evaluator.js.map +1 -0
  170. package/dist/evaluators/registry.d.ts +106 -0
  171. package/dist/evaluators/registry.d.ts.map +1 -0
  172. package/dist/evaluators/registry.js +148 -0
  173. package/dist/evaluators/registry.js.map +1 -0
  174. package/dist/evaluators/robustness-evaluator.d.ts +57 -0
  175. package/dist/evaluators/robustness-evaluator.d.ts.map +1 -0
  176. package/dist/evaluators/robustness-evaluator.js +186 -0
  177. package/dist/evaluators/robustness-evaluator.js.map +1 -0
  178. package/dist/executor/__tests__/binary-sut.unit.test.d.ts +8 -0
  179. package/dist/executor/__tests__/binary-sut.unit.test.d.ts.map +1 -0
  180. package/dist/executor/__tests__/binary-sut.unit.test.js +313 -0
  181. package/dist/executor/__tests__/binary-sut.unit.test.js.map +1 -0
  182. package/dist/executor/__tests__/checkpoint-manager.unit.test.js +83 -1
  183. package/dist/executor/__tests__/checkpoint-manager.unit.test.js.map +1 -1
  184. package/dist/executor/__tests__/checkpoint-merge-bug.diagnostic.test.d.ts +3 -6
  185. package/dist/executor/__tests__/checkpoint-merge-bug.diagnostic.test.d.ts.map +1 -1
  186. package/dist/executor/__tests__/checkpoint-merge-bug.diagnostic.test.js +428 -159
  187. package/dist/executor/__tests__/checkpoint-merge-bug.diagnostic.test.js.map +1 -1
  188. package/dist/executor/__tests__/checkpoint-storage.unit.test.js +148 -1
  189. package/dist/executor/__tests__/checkpoint-storage.unit.test.js.map +1 -1
  190. package/dist/executor/__tests__/executor.unit.test.js +123 -8
  191. package/dist/executor/__tests__/executor.unit.test.js.map +1 -1
  192. package/dist/executor/__tests__/memory-monitor.unit.test.d.ts +7 -0
  193. package/dist/executor/__tests__/memory-monitor.unit.test.d.ts.map +1 -0
  194. package/dist/executor/__tests__/memory-monitor.unit.test.js +285 -0
  195. package/dist/executor/__tests__/memory-monitor.unit.test.js.map +1 -0
  196. package/dist/executor/__tests__/parallel-executor.unit.test.d.ts +2 -1
  197. package/dist/executor/__tests__/parallel-executor.unit.test.d.ts.map +1 -1
  198. package/dist/executor/__tests__/parallel-executor.unit.test.js +426 -156
  199. package/dist/executor/__tests__/parallel-executor.unit.test.js.map +1 -1
  200. package/dist/executor/__tests__/resource-calculator.unit.test.d.ts +10 -0
  201. package/dist/executor/__tests__/resource-calculator.unit.test.d.ts.map +1 -0
  202. package/dist/executor/__tests__/resource-calculator.unit.test.js +104 -0
  203. package/dist/executor/__tests__/resource-calculator.unit.test.js.map +1 -0
  204. package/dist/executor/__tests__/run-id.unit.test.d.ts +8 -0
  205. package/dist/executor/__tests__/run-id.unit.test.d.ts.map +1 -0
  206. package/dist/executor/__tests__/run-id.unit.test.js +156 -0
  207. package/dist/executor/__tests__/run-id.unit.test.js.map +1 -0
  208. package/dist/executor/__tests__/worker-entry.integration.test.d.ts +24 -0
  209. package/dist/executor/__tests__/worker-entry.integration.test.d.ts.map +1 -0
  210. package/dist/executor/__tests__/worker-entry.integration.test.js +82 -0
  211. package/dist/executor/__tests__/worker-entry.integration.test.js.map +1 -0
  212. package/dist/executor/__tests__/worker-entry.unit.test.d.ts +7 -0
  213. package/dist/executor/__tests__/worker-entry.unit.test.d.ts.map +1 -0
  214. package/dist/executor/__tests__/worker-entry.unit.test.js +364 -0
  215. package/dist/executor/__tests__/worker-entry.unit.test.js.map +1 -0
  216. package/dist/executor/__tests__/worker-threads-executor.unit.test.d.ts +8 -0
  217. package/dist/executor/__tests__/worker-threads-executor.unit.test.d.ts.map +1 -0
  218. package/dist/executor/__tests__/worker-threads-executor.unit.test.js +276 -0
  219. package/dist/executor/__tests__/worker-threads-executor.unit.test.js.map +1 -0
  220. package/dist/executor/binary-sut.d.ts +105 -0
  221. package/dist/executor/binary-sut.d.ts.map +1 -0
  222. package/dist/executor/binary-sut.js +174 -0
  223. package/dist/executor/binary-sut.js.map +1 -0
  224. package/dist/executor/checkpoint-storage.d.ts.map +1 -1
  225. package/dist/executor/checkpoint-storage.js +6 -4
  226. package/dist/executor/checkpoint-storage.js.map +1 -1
  227. package/dist/executor/executor.d.ts +28 -0
  228. package/dist/executor/executor.d.ts.map +1 -1
  229. package/dist/executor/executor.js +85 -24
  230. package/dist/executor/executor.js.map +1 -1
  231. package/dist/executor/index.d.ts +4 -0
  232. package/dist/executor/index.d.ts.map +1 -1
  233. package/dist/executor/index.js +4 -0
  234. package/dist/executor/index.js.map +1 -1
  235. package/dist/executor/parallel-executor.d.ts +186 -0
  236. package/dist/executor/parallel-executor.d.ts.map +1 -1
  237. package/dist/executor/parallel-executor.js +218 -83
  238. package/dist/executor/parallel-executor.js.map +1 -1
  239. package/dist/executor/resource-calculator.d.ts +49 -0
  240. package/dist/executor/resource-calculator.d.ts.map +1 -0
  241. package/dist/executor/resource-calculator.js +129 -0
  242. package/dist/executor/resource-calculator.js.map +1 -0
  243. package/dist/executor/run-id.d.ts.map +1 -1
  244. package/dist/executor/run-id.js +8 -1
  245. package/dist/executor/run-id.js.map +1 -1
  246. package/dist/executor/worker-entry.d.ts +2 -0
  247. package/dist/executor/worker-entry.d.ts.map +1 -1
  248. package/dist/executor/worker-entry.js +46 -55
  249. package/dist/executor/worker-entry.js.map +1 -1
  250. package/dist/executor/worker-executor.d.ts +257 -0
  251. package/dist/executor/worker-executor.d.ts.map +1 -0
  252. package/dist/executor/worker-executor.js +308 -0
  253. package/dist/executor/worker-executor.js.map +1 -0
  254. package/dist/executor/worker-threads-executor.d.ts +245 -0
  255. package/dist/executor/worker-threads-executor.d.ts.map +1 -0
  256. package/dist/executor/worker-threads-executor.js +332 -0
  257. package/dist/executor/worker-threads-executor.js.map +1 -0
  258. package/dist/index.d.ts +1 -0
  259. package/dist/index.d.ts.map +1 -1
  260. package/dist/index.js +4 -2
  261. package/dist/index.js.map +1 -1
  262. package/dist/renderers/latex-renderer.d.ts +60 -0
  263. package/dist/renderers/latex-renderer.d.ts.map +1 -1
  264. package/dist/renderers/latex-renderer.js +299 -0
  265. package/dist/renderers/latex-renderer.js.map +1 -1
  266. package/dist/renderers/types.d.ts +9 -0
  267. package/dist/renderers/types.d.ts.map +1 -1
  268. package/dist/renderers/types.js.map +1 -1
  269. package/dist/robustness/__tests__/perturbations.unit.test.d.ts +11 -0
  270. package/dist/robustness/__tests__/perturbations.unit.test.d.ts.map +1 -0
  271. package/dist/robustness/__tests__/perturbations.unit.test.js +284 -0
  272. package/dist/robustness/__tests__/perturbations.unit.test.js.map +1 -0
  273. package/dist/robustness/index.d.ts +5 -2
  274. package/dist/robustness/index.d.ts.map +1 -1
  275. package/dist/robustness/index.js +4 -2
  276. package/dist/robustness/index.js.map +1 -1
  277. package/dist/statistical/__tests__/mann-whitney-u.unit.test.d.ts +7 -0
  278. package/dist/statistical/__tests__/mann-whitney-u.unit.test.d.ts.map +1 -0
  279. package/dist/statistical/__tests__/mann-whitney-u.unit.test.js +185 -0
  280. package/dist/statistical/__tests__/mann-whitney-u.unit.test.js.map +1 -0
  281. package/dist/types/evaluator.d.ts +449 -0
  282. package/dist/types/evaluator.d.ts.map +1 -0
  283. package/dist/types/evaluator.js +9 -0
  284. package/dist/types/evaluator.js.map +1 -0
  285. package/dist/types/result.d.ts +2 -0
  286. package/dist/types/result.d.ts.map +1 -1
  287. package/package.json +8 -1
  288. package/dist/claims/evaluator.d.ts +0 -33
  289. package/dist/claims/evaluator.d.ts.map +0 -1
  290. package/dist/claims/evaluator.js +0 -174
  291. package/dist/claims/evaluator.js.map +0 -1
  292. package/dist/robustness/analyzer.d.ts +0 -61
  293. package/dist/robustness/analyzer.d.ts.map +0 -1
  294. package/dist/robustness/analyzer.js +0 -191
  295. package/dist/robustness/analyzer.js.map +0 -1
@@ -0,0 +1,277 @@
1
+ /**
2
+ * Run Command
3
+ *
4
+ * Executes experiments based on JSON configuration.
5
+ */
6
+ import { resolve } from "node:path";
7
+ import { aggregateResults, createAggregationOutput } from "../../aggregation/index.js";
8
+ import { Executor } from "../../executor/index.js";
9
+ import { loadAndValidateConfig } from "../config-loader.js";
10
+ import { loadCaseDefinition, loadMetricsExtractor, loadSutFactory } from "../module-loader.js";
11
+ import { generateOutputFilename, writeAggregates, writeResults } from "../output-writer.js";
12
+ /**
13
+ * Merge checkpoint shards from worker threads.
14
+ *
15
+ * After worker threads complete execution, each worker has its own checkpoint shard.
16
+ * This function merges all shards into a single checkpoint file.
17
+ *
18
+ * @param baseDir - Base directory for checkpoints
19
+ * @param logger - Command logger
20
+ */
21
+ async function mergeCheckpointShards(baseDir, logger) {
22
+ const { readdir, unlink } = await import("node:fs/promises");
23
+ const { FileStorage } = await import("../../executor/checkpoint-storage.js");
24
+ const checkpointDir = resolve(baseDir, "results/execute");
25
+ try {
26
+ const files = await readdir(checkpointDir);
27
+ const shards = files.filter((f) => f.startsWith("checkpoint-worker-") && f.endsWith(".json"));
28
+ if (shards.length === 0) {
29
+ // No shards found - nothing to merge
30
+ return;
31
+ }
32
+ logger.info(`Merging ${shards.length} checkpoint shards...`);
33
+ // Load and merge all shards
34
+ const mergedData = {
35
+ configHash: "merged",
36
+ createdAt: new Date().toISOString(),
37
+ updatedAt: new Date().toISOString(),
38
+ completedRunIds: [],
39
+ results: {},
40
+ totalPlanned: 0,
41
+ };
42
+ for (const shard of shards) {
43
+ const shardPath = resolve(checkpointDir, shard);
44
+ const storage = new FileStorage(shardPath);
45
+ const data = await storage.load();
46
+ if (data?.completedRunIds) {
47
+ // Merge run IDs into the array
48
+ for (const runId of data.completedRunIds) {
49
+ if (!mergedData.completedRunIds.includes(runId)) {
50
+ mergedData.completedRunIds.push(runId);
51
+ }
52
+ }
53
+ }
54
+ // Merge results if present
55
+ if (data?.results) {
56
+ Object.assign(mergedData.results, data.results);
57
+ }
58
+ // Update total planned from first shard
59
+ if (data?.totalPlanned && mergedData.totalPlanned === 0) {
60
+ mergedData.totalPlanned = data.totalPlanned;
61
+ }
62
+ }
63
+ // Write merged checkpoint
64
+ const mainStorage = new FileStorage(resolve(checkpointDir, "checkpoint.json"));
65
+ await mainStorage.save(mergedData);
66
+ logger.info("Checkpoint shards merged successfully");
67
+ // Clean up shard files
68
+ for (const shard of shards) {
69
+ const shardPath = resolve(checkpointDir, shard);
70
+ await unlink(shardPath).catch(() => {
71
+ // Ignore errors during cleanup
72
+ });
73
+ }
74
+ logger.debug(`Cleaned up ${shards.length} shard files`);
75
+ }
76
+ catch (error) {
77
+ // Checkpoint directory doesn't exist or other error - log and continue
78
+ logger.debug(`Checkpoint merge skipped: ${error instanceof Error ? error.message : String(error)}`);
79
+ }
80
+ }
81
+ /**
82
+ * Execute run command with injected dependencies.
83
+ *
84
+ * This function is exported for testing.
85
+ *
86
+ * @param configFile - Path to config file
87
+ * @param options - Command options
88
+ * @param dependencies - Injected dependencies
89
+ */
90
+ export async function executeRun(configFile, options, dependencies) {
91
+ const { logger, configLoader, moduleLoader, createExecutor, aggregator, outputWriter, processExit, } = dependencies;
92
+ try {
93
+ logger.header(`Experiment: ${options.dryRun ? "Dry Run" : "Execution"}`);
94
+ // Load and validate configuration
95
+ const loaded = await configLoader.loadAndValidateConfig(configFile);
96
+ const { config, baseDir } = loaded;
97
+ logger.info(`Configuration: ${loaded.configPath}`);
98
+ logger.info(`Experiment: ${config.experiment.name}`);
99
+ if (config.experiment.description) {
100
+ logger.info(`Description: ${config.experiment.description}`);
101
+ }
102
+ // Apply CLI overrides
103
+ const executorConfig = { ...config.executor };
104
+ if (options.jobs !== undefined) {
105
+ executorConfig.concurrency = options.jobs;
106
+ logger.debug(`Concurrency overridden to ${options.jobs}`);
107
+ }
108
+ // Handle unsafe in-process flag
109
+ if (options.unsafeInProcess) {
110
+ executorConfig.forceInProcess = true;
111
+ logger.warn("Running in-process without worker thread isolation (SUT crashes can crash CLI)");
112
+ }
113
+ // Load SUTs
114
+ logger.subheader("Loading SUTs...");
115
+ const sutDefinitions = await Promise.all(config.suts.map(async (sutConfig) => {
116
+ logger.debug(`Loading SUT: ${sutConfig.id} from ${sutConfig.module}`);
117
+ // Build binary config if this is a binary SUT
118
+ const binaryConfig = sutConfig.type === "binary" && sutConfig.binaryCommand
119
+ ? {
120
+ type: "binary",
121
+ command: sutConfig.binaryCommand,
122
+ args: sutConfig.binaryArgs,
123
+ inputFormat: sutConfig.binaryInputFormat,
124
+ outputFormat: sutConfig.binaryOutputFormat,
125
+ timeout: sutConfig.binaryTimeout,
126
+ successExitCode: undefined, // Could add to SutConfig if needed
127
+ }
128
+ : undefined;
129
+ return moduleLoader.loadSutFactory(sutConfig.module, sutConfig.exportName, baseDir, {
130
+ id: sutConfig.id,
131
+ name: sutConfig.registration.name,
132
+ version: sutConfig.registration.version,
133
+ role: sutConfig.registration.role,
134
+ config: sutConfig.config ?? {},
135
+ tags: sutConfig.registration.tags ?? [],
136
+ description: sutConfig.registration.description,
137
+ }, sutConfig.config, binaryConfig);
138
+ }));
139
+ logger.info(`Loaded ${sutDefinitions.length} SUTs`);
140
+ // Load cases
141
+ logger.subheader("Loading cases...");
142
+ const caseDefinitions = await Promise.all(config.cases.map(async (caseConfig) => {
143
+ logger.debug(`Loading case: ${caseConfig.id} from ${caseConfig.module}`);
144
+ return moduleLoader.loadCaseDefinition(caseConfig.module, caseConfig.exportName, baseDir);
145
+ }));
146
+ logger.info(`Loaded ${caseDefinitions.length} cases`);
147
+ // Load metrics extractor
148
+ logger.subheader("Loading metrics extractor...");
149
+ const metricsExtractor = await moduleLoader.loadMetricsExtractor(config.metricsExtractor.module, config.metricsExtractor.exportName, baseDir);
150
+ logger.info(`Metrics extractor loaded from ${config.metricsExtractor.module}`);
151
+ // Create executor
152
+ const executor = createExecutor(executorConfig);
153
+ // Plan runs
154
+ logger.subheader("Planning execution...");
155
+ const plannedRuns = executor.plan(sutDefinitions, caseDefinitions);
156
+ logger.info(`Planned ${plannedRuns.length} runs`);
157
+ // Dry run - just show plan and exit
158
+ if (options.dryRun) {
159
+ logger.info("");
160
+ logger.subheader("Dry run - not executing");
161
+ logger.info(`SUTs: ${sutDefinitions.map((s) => s.id).join(", ")}`);
162
+ logger.info(`Cases: ${caseDefinitions.map((c) => c.case.caseId).join(", ")}`);
163
+ logger.info(`Total runs: ${plannedRuns.length}`);
164
+ return;
165
+ }
166
+ // Execute
167
+ logger.subheader("Executing...");
168
+ logger.setProgress(true);
169
+ const startTime = Date.now();
170
+ const summary = await executor.execute(sutDefinitions, caseDefinitions, metricsExtractor, undefined);
171
+ const elapsed = Date.now() - startTime;
172
+ logger.setProgress(false);
173
+ logger.info("");
174
+ // Merge checkpoint shards from workers if not in unsafe in-process mode
175
+ if (!options.unsafeInProcess) {
176
+ await mergeCheckpointShards(baseDir, logger);
177
+ }
178
+ // Report results
179
+ logger.subheader("Execution Summary");
180
+ logger.info(`Total runs: ${summary.totalRuns}`);
181
+ logger.info(`Successful: ${summary.successfulRuns}`);
182
+ logger.info(`Failed: ${summary.failedRuns}`);
183
+ logger.info(`Elapsed time: ${(elapsed / 1000).toFixed(2)}s`);
184
+ if (summary.errors.length > 0) {
185
+ logger.warn(`Errors encountered: ${summary.errors.length}`);
186
+ for (const error of summary.errors.slice(0, 5)) {
187
+ logger.warn(` - ${error.runId}: ${error.error}`);
188
+ }
189
+ if (summary.errors.length > 5) {
190
+ logger.warn(` ... and ${summary.errors.length - 5} more`);
191
+ }
192
+ }
193
+ // Determine output settings
194
+ const outputPath = options.output ?? config.output.path ?? "./results";
195
+ const format = options.format ?? config.output.format ?? "json-pretty";
196
+ const shouldAggregate = options.noAggregate !== true && config.output.aggregate !== false;
197
+ // Write results
198
+ logger.subheader("Writing results...");
199
+ const resultsFilename = outputWriter.generateOutputFilename(config.experiment.name, "results");
200
+ const resultsPath = `${outputPath}/${resultsFilename}`;
201
+ await outputWriter.writeResults(summary.results, resultsPath, format);
202
+ logger.info(`Results written to: ${resultsPath}`);
203
+ // Aggregate if requested
204
+ if (shouldAggregate && summary.results.length > 0) {
205
+ logger.subheader("Aggregating results...");
206
+ const aggregates = aggregator.aggregateResults(summary.results, {
207
+ groupByCaseClass: true,
208
+ computeComparisons: true,
209
+ });
210
+ const aggregationOutput = aggregator.createAggregationOutput(aggregates, summary.results);
211
+ const aggregatesFilename = outputWriter.generateOutputFilename(config.experiment.name, "aggregates");
212
+ const aggregatesPath = `${outputPath}/${aggregatesFilename}`;
213
+ await outputWriter.writeAggregates(aggregationOutput, aggregatesPath, format);
214
+ logger.info(`Aggregates written to: ${aggregatesPath}`);
215
+ logger.info("");
216
+ logger.subheader("Aggregation Summary");
217
+ for (const agg of aggregates) {
218
+ const caseClassSuffix = agg.caseClass ? ` (${agg.caseClass})` : "";
219
+ logger.info(`${agg.sut}${caseClassSuffix}:`);
220
+ logger.info(` Runs: ${agg.group.runCount}`);
221
+ logger.info(` Cases: ${agg.group.caseCount}`);
222
+ for (const [metric, stats] of Object.entries(agg.metrics)) {
223
+ const std = stats.std ?? 0;
224
+ logger.info(` ${metric}: mean=${stats.mean.toFixed(2)}, std=${std.toFixed(2)}`);
225
+ }
226
+ }
227
+ }
228
+ logger.info("");
229
+ logger.info("Experiment completed successfully!");
230
+ }
231
+ catch (error) {
232
+ logger.setProgress(false);
233
+ if (error instanceof Error) {
234
+ logger.error(error.message);
235
+ if (options.verbose) {
236
+ console.error(error.stack);
237
+ }
238
+ }
239
+ else {
240
+ logger.error(String(error));
241
+ }
242
+ processExit(1);
243
+ }
244
+ }
245
+ /**
246
+ * Register the run command.
247
+ *
248
+ * @param program - Commander program instance
249
+ */
250
+ export function registerRunCommand(program) {
251
+ program
252
+ .command("run")
253
+ .description("Run experiments from a configuration file")
254
+ .argument("<config-file>", "Path to experiment configuration JSON file")
255
+ .option("-o, --output <path>", "Output directory")
256
+ .option("-f, --format <format>", "Output format (json or json-pretty)", "json-pretty")
257
+ .option("--no-aggregate", "Skip aggregation")
258
+ .option("-j, --jobs <number>", "Override concurrency", (value) => Number.parseInt(value, 10))
259
+ .option("-v, --verbose", "Verbose logging")
260
+ .option("-q, --quiet", "Suppress output")
261
+ .option("--dry-run", "Plan without running")
262
+ .option("--unsafe-in-process", "Run in-process without worker thread isolation (SUT crashes can crash CLI)")
263
+ .action(async (configFile, options) => {
264
+ const { createLogger } = await import("../logger.js");
265
+ const logger = createLogger(options);
266
+ await executeRun(configFile, options, {
267
+ logger,
268
+ configLoader: { loadAndValidateConfig },
269
+ moduleLoader: { loadSutFactory, loadCaseDefinition, loadMetricsExtractor },
270
+ createExecutor: (config) => new Executor(config),
271
+ aggregator: { aggregateResults, createAggregationOutput },
272
+ outputWriter: { generateOutputFilename, writeResults, writeAggregates },
273
+ processExit: (code) => process.exit(code),
274
+ });
275
+ });
276
+ }
277
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/cli/commands/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Validate Command
3
+ *
4
+ * Validates experiment configuration files without running experiments.
5
+ */
6
+ import type { Command } from "commander";
7
+ import type { IConfigLoader, ICommandLogger } from "../command-deps.js";
8
+ /**
9
+ * Execute validate command with injected dependencies.
10
+ *
11
+ * This function is exported for testing.
12
+ *
13
+ * @param configFile - Path to config file
14
+ * @param dependencies - Injected dependencies
15
+ */
16
+ export declare function executeValidate(configFile: string, dependencies: {
17
+ logger: ICommandLogger;
18
+ configLoader: IConfigLoader;
19
+ processExit: (code: number) => never;
20
+ }): Promise<void>;
21
+ /**
22
+ * Register the validate command.
23
+ *
24
+ * @param program - Commander program instance
25
+ */
26
+ export declare function registerValidateCommand(program: Command): void;
27
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/validate.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIxE;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACpC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,YAAY,EAAE,aAAa,CAAC;IAC5B,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;CACrC,GACC,OAAO,CAAC,IAAI,CAAC,CA0Df;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgB9D"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Validate Command
3
+ *
4
+ * Validates experiment configuration files without running experiments.
5
+ */
6
+ import { loadAndValidateConfig } from "../config-loader.js";
7
+ // Note: createLogger imported lazily in action handler to avoid circular dependency
8
+ /**
9
+ * Execute validate command with injected dependencies.
10
+ *
11
+ * This function is exported for testing.
12
+ *
13
+ * @param configFile - Path to config file
14
+ * @param dependencies - Injected dependencies
15
+ */
16
+ export async function executeValidate(configFile, dependencies) {
17
+ const { logger, configLoader, processExit } = dependencies;
18
+ try {
19
+ logger.header("Configuration Validation");
20
+ const loaded = await configLoader.loadAndValidateConfig(configFile);
21
+ logger.info(`Configuration file: ${loaded.configPath}`);
22
+ logger.info(`Base directory: ${loaded.baseDir}`);
23
+ logger.info(`Experiment: ${loaded.config.experiment.name}`);
24
+ if (loaded.config.experiment.description) {
25
+ logger.info(`Description: ${loaded.config.experiment.description}`);
26
+ }
27
+ if (loaded.config.experiment.version) {
28
+ logger.info(`Version: ${loaded.config.experiment.version}`);
29
+ }
30
+ logger.subheader("SUTs");
31
+ for (const sut of loaded.config.suts) {
32
+ logger.info(` - ${sut.id} (${sut.registration.name} v${sut.registration.version})`);
33
+ logger.info(` Module: ${sut.module} -> ${sut.exportName}`);
34
+ logger.info(` Role: ${sut.registration.role}`);
35
+ }
36
+ logger.subheader("Cases");
37
+ for (const testCase of loaded.config.cases) {
38
+ logger.info(` - ${testCase.id}`);
39
+ logger.info(` Module: ${testCase.module} -> ${testCase.exportName}`);
40
+ }
41
+ logger.subheader("Executor");
42
+ const executor = loaded.config.executor;
43
+ logger.info(` Repetitions: ${executor.repetitions ?? "default"}`);
44
+ logger.info(` Seed base: ${executor.seedBase ?? "default"}`);
45
+ logger.info(` Timeout: ${executor.timeoutMs ?? "default"}ms`);
46
+ logger.info(` Concurrency: ${executor.concurrency ?? "sequential"}`);
47
+ logger.info(` Continue on error: ${executor.continueOnError ?? "default"}`);
48
+ logger.info(` Collect provenance: ${executor.collectProvenance ?? "default"}`);
49
+ logger.subheader("Output");
50
+ const output = loaded.config.output;
51
+ logger.info(` Path: ${output.path ?? "./results"}`);
52
+ logger.info(` Format: ${output.format ?? "json-pretty"}`);
53
+ logger.info(` Aggregate: ${output.aggregate ?? "true"}`);
54
+ logger.info("");
55
+ logger.info("Configuration is valid!");
56
+ }
57
+ catch (error) {
58
+ if (error instanceof Error) {
59
+ logger.error(error.message);
60
+ }
61
+ else {
62
+ logger.error(String(error));
63
+ }
64
+ processExit(1);
65
+ }
66
+ }
67
+ /**
68
+ * Register the validate command.
69
+ *
70
+ * @param program - Commander program instance
71
+ */
72
+ export function registerValidateCommand(program) {
73
+ program
74
+ .command("validate")
75
+ .description("Validate an experiment configuration file")
76
+ .argument("<config-file>", "Path to experiment configuration JSON file")
77
+ .action(async (configFile) => {
78
+ // Import dependencies lazily to avoid circular imports
79
+ const { createLogger } = await import("../logger.js");
80
+ const logger = createLogger();
81
+ await executeValidate(configFile, {
82
+ logger,
83
+ configLoader: { loadAndValidateConfig },
84
+ processExit: (code) => process.exit(code),
85
+ });
86
+ });
87
+ }
88
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/cli/commands/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Config Loader
3
+ *
4
+ * Loads and validates experiment configuration from JSON files.
5
+ */
6
+ import type { ExperimentConfig, LoadedConfig, ValidationResult } from "./types.js";
7
+ /**
8
+ * Load and parse experiment configuration from a JSON file.
9
+ *
10
+ * @param configPath - Path to config file (can be absolute or relative)
11
+ * @returns Loaded configuration with base directory
12
+ * @throws Error if file cannot be read or parsed
13
+ */
14
+ export declare function loadConfig(configPath: string): Promise<LoadedConfig>;
15
+ /**
16
+ * Validate experiment configuration.
17
+ *
18
+ * @param config - Configuration to validate
19
+ * @returns Validation result with errors and warnings
20
+ */
21
+ export declare function validateConfig(config: ExperimentConfig): ValidationResult;
22
+ /**
23
+ * Load and validate experiment configuration.
24
+ *
25
+ * @param configPath - Path to config file
26
+ * @returns Loaded and validated configuration
27
+ * @throws Error if validation fails
28
+ */
29
+ export declare function loadAndValidateConfig(configPath: string): Promise<LoadedConfig>;
30
+ //# sourceMappingURL=config-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/cli/config-loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnF;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAgB1E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CA+HzE;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAkBrF"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Config Loader
3
+ *
4
+ * Loads and validates experiment configuration from JSON files.
5
+ */
6
+ import { readFile } from "node:fs/promises";
7
+ import { resolve, dirname } from "node:path";
8
+ /**
9
+ * Load and parse experiment configuration from a JSON file.
10
+ *
11
+ * @param configPath - Path to config file (can be absolute or relative)
12
+ * @returns Loaded configuration with base directory
13
+ * @throws Error if file cannot be read or parsed
14
+ */
15
+ export async function loadConfig(configPath) {
16
+ // Resolve absolute path
17
+ const absolutePath = resolve(configPath);
18
+ // Read and parse JSON
19
+ const content = await readFile(absolutePath, "utf-8");
20
+ const config = JSON.parse(content);
21
+ // Get base directory (for resolving module paths)
22
+ const baseDir = dirname(absolutePath);
23
+ return {
24
+ config,
25
+ baseDir,
26
+ configPath: absolutePath,
27
+ };
28
+ }
29
+ /**
30
+ * Validate experiment configuration.
31
+ *
32
+ * @param config - Configuration to validate
33
+ * @returns Validation result with errors and warnings
34
+ */
35
+ export function validateConfig(config) {
36
+ const errors = [];
37
+ const warnings = [];
38
+ // Validate experiment metadata
39
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
40
+ if (!config.experiment?.name) {
41
+ errors.push("experiment.name is required");
42
+ }
43
+ // Validate executor config
44
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
45
+ if (!config.executor) {
46
+ errors.push("executor configuration is required");
47
+ }
48
+ else {
49
+ if (config.executor.repetitions !== undefined && config.executor.repetitions < 1) {
50
+ errors.push("executor.repetitions must be at least 1");
51
+ }
52
+ if (config.executor.seedBase !== undefined && config.executor.seedBase < 0) {
53
+ errors.push("executor.seedBase must be non-negative");
54
+ }
55
+ if (config.executor.timeoutMs !== undefined && config.executor.timeoutMs < 0) {
56
+ errors.push("executor.timeoutMs must be non-negative");
57
+ }
58
+ if (config.executor.concurrency !== undefined && config.executor.concurrency < 1) {
59
+ errors.push("executor.concurrency must be at least 1");
60
+ }
61
+ }
62
+ // Validate SUTs
63
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
64
+ if (!config.suts || config.suts.length === 0) {
65
+ warnings.push("No SUTs configured - experiment will have nothing to execute");
66
+ }
67
+ else {
68
+ for (let i = 0; i < config.suts.length; i++) {
69
+ const sut = config.suts[i];
70
+ if (!sut.id) {
71
+ errors.push(`suts[${i}].id is required`);
72
+ }
73
+ if (!sut.module) {
74
+ errors.push(`suts[${i}].module is required`);
75
+ }
76
+ if (!sut.exportName) {
77
+ errors.push(`suts[${i}].exportName is required`);
78
+ }
79
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
80
+ if (!sut.registration?.name) {
81
+ errors.push(`suts[${i}].registration.name is required`);
82
+ }
83
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
84
+ if (!sut.registration?.version) {
85
+ errors.push(`suts[${i}].registration.version is required`);
86
+ }
87
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
88
+ if (!sut.registration?.role) {
89
+ errors.push(`suts[${i}].registration.role is required`);
90
+ }
91
+ else if (!["primary", "baseline", "oracle"].includes(sut.registration.role)) {
92
+ errors.push(`suts[${i}].registration.role must be one of: primary, baseline, oracle`);
93
+ }
94
+ }
95
+ // Check for duplicate SUT IDs
96
+ const sutIds = new Set();
97
+ for (const sut of config.suts) {
98
+ if (sut.id && sutIds.has(sut.id)) {
99
+ errors.push(`Duplicate SUT ID: ${sut.id}`);
100
+ }
101
+ sutIds.add(sut.id);
102
+ }
103
+ }
104
+ // Validate cases
105
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
106
+ if (!config.cases || config.cases.length === 0) {
107
+ warnings.push("No cases configured - experiment will have nothing to execute");
108
+ }
109
+ else {
110
+ for (let i = 0; i < config.cases.length; i++) {
111
+ const testCase = config.cases[i];
112
+ if (!testCase.id) {
113
+ errors.push(`cases[${i}].id is required`);
114
+ }
115
+ if (!testCase.module) {
116
+ errors.push(`cases[${i}].module is required`);
117
+ }
118
+ if (!testCase.exportName) {
119
+ errors.push(`cases[${i}].exportName is required`);
120
+ }
121
+ }
122
+ // Check for duplicate case IDs
123
+ const caseIds = new Set();
124
+ for (const testCase of config.cases) {
125
+ if (testCase.id && caseIds.has(testCase.id)) {
126
+ errors.push(`Duplicate case ID: ${testCase.id}`);
127
+ }
128
+ caseIds.add(testCase.id);
129
+ }
130
+ }
131
+ // Validate metrics extractor
132
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
133
+ if (!config.metricsExtractor) {
134
+ errors.push("metricsExtractor configuration is required");
135
+ }
136
+ else {
137
+ if (!config.metricsExtractor.module) {
138
+ errors.push("metricsExtractor.module is required");
139
+ }
140
+ if (!config.metricsExtractor.exportName) {
141
+ errors.push("metricsExtractor.exportName is required");
142
+ }
143
+ }
144
+ // Validate output config
145
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
146
+ if (!config.output) {
147
+ warnings.push("No output configuration specified - using defaults");
148
+ }
149
+ else {
150
+ if (config.output.format && !["json", "json-pretty"].includes(config.output.format)) {
151
+ errors.push('output.format must be one of: "json", "json-pretty"');
152
+ }
153
+ }
154
+ return {
155
+ valid: errors.length === 0,
156
+ errors,
157
+ warnings,
158
+ };
159
+ }
160
+ /**
161
+ * Load and validate experiment configuration.
162
+ *
163
+ * @param configPath - Path to config file
164
+ * @returns Loaded and validated configuration
165
+ * @throws Error if validation fails
166
+ */
167
+ export async function loadAndValidateConfig(configPath) {
168
+ const loaded = await loadConfig(configPath);
169
+ const validation = validateConfig(loaded.config);
170
+ if (!validation.valid) {
171
+ throw new Error(`Configuration validation failed:\n${validation.errors.map((e) => ` - ${e}`).join("\n")}`);
172
+ }
173
+ // Print warnings if any
174
+ if (validation.warnings.length > 0) {
175
+ for (const warning of validation.warnings) {
176
+ console.warn(`Warning: ${warning}`);
177
+ }
178
+ }
179
+ return loaded;
180
+ }
181
+ //# sourceMappingURL=config-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../src/cli/config-loader.ts"],"names":[],"mappings":"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"}