executable-stories-formatters 0.7.9 → 0.7.11

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.
package/dist/cli.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { parseArgs } from "util";
5
- import * as fs7 from "fs";
6
- import * as path7 from "path";
5
+ import * as fs8 from "fs";
6
+ import * as path8 from "path";
7
7
 
8
8
  // src/validation/schema-validator.ts
9
9
  import Ajv from "ajv/dist/2020.js";
@@ -492,17 +492,17 @@ function validateRawRun(data) {
492
492
  return { valid: true, errors: [] };
493
493
  }
494
494
  const errors = (validate.errors ?? []).map((err) => {
495
- const path8 = err.instancePath || "/";
495
+ const path9 = err.instancePath || "/";
496
496
  const message = err.message ?? "unknown error";
497
497
  if (err.keyword === "additionalProperties") {
498
498
  const extra = err.params.additionalProperty;
499
- return `${path8}: ${message} \u2014 '${extra}'`;
499
+ return `${path9}: ${message} \u2014 '${extra}'`;
500
500
  }
501
501
  if (err.keyword === "enum") {
502
502
  const allowed = err.params.allowedValues;
503
- return `${path8}: ${message} \u2014 allowed: ${JSON.stringify(allowed)}`;
503
+ return `${path9}: ${message} \u2014 allowed: ${JSON.stringify(allowed)}`;
504
504
  }
505
- return `${path8}: ${message}`;
505
+ return `${path9}: ${message}`;
506
506
  });
507
507
  return { valid: false, errors };
508
508
  }
@@ -966,7 +966,7 @@ ${result.errors.join("\n")}`);
966
966
 
967
967
  // src/index.ts
968
968
  import "fs";
969
- import * as path5 from "path";
969
+ import * as path6 from "path";
970
970
  import * as fsPromises from "fs/promises";
971
971
 
972
972
  // src/converters/acl/lines.ts
@@ -1330,6 +1330,10 @@ ${doc.markdown}`,
1330
1330
  }
1331
1331
  };
1332
1332
 
1333
+ // src/formatters/html/renderers/index.ts
1334
+ import * as fs2 from "fs";
1335
+ import * as path2 from "path";
1336
+
1333
1337
  // src/formatters/html/template.ts
1334
1338
  var JS_THEME = `
1335
1339
  // Theme management
@@ -13637,7 +13641,9 @@ function renderDocMermaid(entry, deps) {
13637
13641
  }
13638
13642
  function renderDocScreenshot(entry, deps) {
13639
13643
  const alt = entry.alt ?? "Screenshot";
13640
- const src = entry.path;
13644
+ const embedEnabled = deps.embedScreenshots ?? true;
13645
+ const isRemote = /^(?:https?:|data:)/i.test(entry.path);
13646
+ const src = embedEnabled && !isRemote && deps.readScreenshot ? deps.readScreenshot(entry.path) ?? entry.path : entry.path;
13641
13647
  return `<div class="doc-screenshot">
13642
13648
  <img src="${deps.escapeHtml(src)}" alt="${deps.escapeHtml(alt)}" class="doc-screenshot-img" />
13643
13649
  ${entry.alt ? `<div class="doc-screenshot-caption">${deps.escapeHtml(entry.alt)}</div>` : ""}
@@ -14219,6 +14225,28 @@ function renderToc(args, deps) {
14219
14225
  }
14220
14226
 
14221
14227
  // src/formatters/html/renderers/index.ts
14228
+ var SCREENSHOT_MIME_BY_EXT = {
14229
+ png: "image/png",
14230
+ jpg: "image/jpeg",
14231
+ jpeg: "image/jpeg",
14232
+ gif: "image/gif",
14233
+ webp: "image/webp",
14234
+ svg: "image/svg+xml",
14235
+ avif: "image/avif",
14236
+ bmp: "image/bmp"
14237
+ };
14238
+ function readScreenshotAsDataUri(filePath) {
14239
+ try {
14240
+ const ext = path2.extname(filePath).slice(1).toLowerCase();
14241
+ const mime = SCREENSHOT_MIME_BY_EXT[ext];
14242
+ if (!mime) return void 0;
14243
+ if (!fs2.existsSync(filePath)) return void 0;
14244
+ const buf = fs2.readFileSync(filePath);
14245
+ return `data:${mime};base64,${buf.toString("base64")}`;
14246
+ } catch {
14247
+ return void 0;
14248
+ }
14249
+ }
14222
14250
  function normalizeOptions(options = {}) {
14223
14251
  return {
14224
14252
  title: options.title ?? "Test Results",
@@ -14242,7 +14270,9 @@ function createHtmlFormatter(options = {}) {
14242
14270
  escapeHtml,
14243
14271
  syntaxHighlighting: opts.syntaxHighlighting,
14244
14272
  markdownEnabled: opts.markdownEnabled,
14245
- mermaidEnabled: opts.mermaidEnabled
14273
+ mermaidEnabled: opts.mermaidEnabled,
14274
+ embedScreenshots: opts.embedScreenshots,
14275
+ readScreenshot: (filePath) => readScreenshotAsDataUri(filePath)
14246
14276
  };
14247
14277
  const renderDocs = (docs, containerClass) => {
14248
14278
  if (!docs || docs.length === 0) return "";
@@ -15671,8 +15701,8 @@ function extractDocAttachments(step) {
15671
15701
  }
15672
15702
  return attachments;
15673
15703
  }
15674
- function guessMediaType(path8) {
15675
- const lower = path8.toLowerCase();
15704
+ function guessMediaType(path9) {
15705
+ const lower = path9.toLowerCase();
15676
15706
  if (lower.endsWith(".png")) return "image/png";
15677
15707
  if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
15678
15708
  if (lower.endsWith(".gif")) return "image/gif";
@@ -16746,8 +16776,8 @@ function selectTestCases(args, deps) {
16746
16776
  }
16747
16777
 
16748
16778
  // src/bundler/bundle-assets.ts
16749
- import * as fs3 from "fs";
16750
- import * as path3 from "path";
16779
+ import * as fs4 from "fs";
16780
+ import * as path4 from "path";
16751
16781
 
16752
16782
  // src/bundler/scan-html-assets.ts
16753
16783
  function scanHtmlAssets(html) {
@@ -16777,21 +16807,21 @@ function isLocalAssetRef(ref) {
16777
16807
  }
16778
16808
 
16779
16809
  // src/bundler/copy-asset.ts
16780
- import * as fs2 from "fs";
16781
- import * as path2 from "path";
16810
+ import * as fs3 from "fs";
16811
+ import * as path3 from "path";
16782
16812
  import * as crypto from "crypto";
16783
16813
  function copyAsset(sourcePath, assetsDir) {
16784
- if (!fs2.existsSync(assetsDir)) {
16785
- fs2.mkdirSync(assetsDir, { recursive: true });
16814
+ if (!fs3.existsSync(assetsDir)) {
16815
+ fs3.mkdirSync(assetsDir, { recursive: true });
16786
16816
  }
16787
- const content = fs2.readFileSync(sourcePath);
16817
+ const content = fs3.readFileSync(sourcePath);
16788
16818
  const hash = crypto.createHash("sha256").update(content).digest("hex").slice(0, 8);
16789
- const ext = path2.extname(sourcePath);
16790
- const baseName = sanitize(path2.basename(sourcePath, ext));
16819
+ const ext = path3.extname(sourcePath);
16820
+ const baseName = sanitize(path3.basename(sourcePath, ext));
16791
16821
  const destName = `${baseName}-${hash}${ext}`;
16792
- const destPath = path2.join(assetsDir, destName);
16793
- if (!fs2.existsSync(destPath)) {
16794
- fs2.copyFileSync(sourcePath, destPath);
16822
+ const destPath = path3.join(assetsDir, destName);
16823
+ if (!fs3.existsSync(destPath)) {
16824
+ fs3.copyFileSync(sourcePath, destPath);
16795
16825
  }
16796
16826
  return `assets/${destName}`;
16797
16827
  }
@@ -16801,15 +16831,15 @@ function sanitize(name) {
16801
16831
 
16802
16832
  // src/bundler/bundle-assets.ts
16803
16833
  function bundleAssets(htmlPath, options = {}) {
16804
- const htmlDir = path3.dirname(htmlPath);
16805
- const assetsDir = path3.join(htmlDir, "assets");
16806
- let html = fs3.readFileSync(htmlPath, "utf8");
16834
+ const htmlDir = path4.dirname(htmlPath);
16835
+ const assetsDir = path4.join(htmlDir, "assets");
16836
+ let html = fs4.readFileSync(htmlPath, "utf8");
16807
16837
  const refs = scanHtmlAssets(html);
16808
16838
  let copiedCount = 0;
16809
16839
  const missing = [];
16810
16840
  for (const ref of refs) {
16811
- const absolutePath = path3.resolve(htmlDir, ref);
16812
- if (!fs3.existsSync(absolutePath)) {
16841
+ const absolutePath = path4.resolve(htmlDir, ref);
16842
+ if (!fs4.existsSync(absolutePath)) {
16813
16843
  missing.push(ref);
16814
16844
  continue;
16815
16845
  }
@@ -16822,7 +16852,7 @@ function bundleAssets(htmlPath, options = {}) {
16822
16852
  `Missing asset${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}`
16823
16853
  );
16824
16854
  }
16825
- fs3.writeFileSync(htmlPath, html, "utf8");
16855
+ fs4.writeFileSync(htmlPath, html, "utf8");
16826
16856
  return {
16827
16857
  copiedCount,
16828
16858
  missingCount: missing.length,
@@ -17305,15 +17335,15 @@ function groupBy7(items, keyFn) {
17305
17335
  }
17306
17336
 
17307
17337
  // src/formatters/astro-assets.ts
17308
- import * as fs4 from "fs";
17309
- import * as path4 from "path";
17338
+ import * as fs5 from "fs";
17339
+ import * as path5 from "path";
17310
17340
  var SKIP_PREFIXES = ["http://", "https://", "data:", "#"];
17311
17341
  function isLocalPath(src) {
17312
17342
  const trimmed = src.trim();
17313
17343
  if (SKIP_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {
17314
17344
  return false;
17315
17345
  }
17316
- return !path4.posix.isAbsolute(trimmed) && !path4.win32.isAbsolute(trimmed);
17346
+ return !path5.posix.isAbsolute(trimmed) && !path5.win32.isAbsolute(trimmed);
17317
17347
  }
17318
17348
  function stripCodeContent(markdown) {
17319
17349
  let result = markdown.replace(/^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?^[ \t]*\1\s*$/gm, "");
@@ -17407,8 +17437,8 @@ function copyMarkdownAssets(options) {
17407
17437
  const pathMap = /* @__PURE__ */ new Map();
17408
17438
  const missing = [];
17409
17439
  for (const ref of refs) {
17410
- const absPath = path4.resolve(markdownDir, ref);
17411
- if (!fs4.existsSync(absPath)) {
17440
+ const absPath = path5.resolve(markdownDir, ref);
17441
+ if (!fs5.existsSync(absPath)) {
17412
17442
  if (!allowMissing) {
17413
17443
  throw new Error(`Asset not found: ${absPath}`);
17414
17444
  }
@@ -18716,11 +18746,11 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
18716
18746
  const ext = FORMAT_EXTENSIONS[format];
18717
18747
  const effectiveName = outputName + (outputNameSuffix ?? "");
18718
18748
  if (mode === "aggregated") {
18719
- return toPosix(path5.join(baseOutputDir, `${effectiveName}${ext}`));
18749
+ return toPosix(path6.join(baseOutputDir, `${effectiveName}${ext}`));
18720
18750
  }
18721
18751
  const normalizedSource = toPosix(sourceFile);
18722
- const dirOfSource = path5.posix.dirname(normalizedSource);
18723
- let baseName = path5.posix.basename(normalizedSource);
18752
+ const dirOfSource = path6.posix.dirname(normalizedSource);
18753
+ let baseName = path6.posix.basename(normalizedSource);
18724
18754
  for (const testExt of TEST_EXTENSIONS) {
18725
18755
  if (baseName.endsWith(testExt)) {
18726
18756
  baseName = baseName.slice(0, -testExt.length);
@@ -18729,9 +18759,9 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
18729
18759
  }
18730
18760
  const fileName = `${baseName}.${effectiveName}${ext}`;
18731
18761
  if (colocatedStyle === "adjacent") {
18732
- return toPosix(path5.posix.join(dirOfSource, fileName));
18762
+ return toPosix(path6.posix.join(dirOfSource, fileName));
18733
18763
  }
18734
- return toPosix(path5.posix.join(baseOutputDir, dirOfSource, fileName));
18764
+ return toPosix(path6.posix.join(baseOutputDir, dirOfSource, fileName));
18735
18765
  }
18736
18766
  function groupTestCasesByOutput(testCases, format, options, logger, outputNameSuffix) {
18737
18767
  const groups = /* @__PURE__ */ new Map();
@@ -18929,8 +18959,8 @@ var ReportGenerator = class {
18929
18959
  if (astroPaths) {
18930
18960
  for (const mdPath of astroPaths) {
18931
18961
  const content = await fsPromises.readFile(mdPath, "utf8");
18932
- const mdDir = path5.dirname(mdPath);
18933
- const assetsDir = path5.resolve(this.options.astro.assetsDir);
18962
+ const mdDir = path6.dirname(mdPath);
18963
+ const assetsDir = path6.resolve(this.options.astro.assetsDir);
18934
18964
  const result = copyMarkdownAssets({
18935
18965
  markdown: content,
18936
18966
  markdownDir: mdDir,
@@ -18961,9 +18991,9 @@ var ReportGenerator = class {
18961
18991
  if (groups.size === 0 && this.options.output.mode === "aggregated") {
18962
18992
  const ext = FORMAT_EXTENSIONS[format];
18963
18993
  const effectiveName = this.options.outputName + (outputNameSuffix ?? "");
18964
- const outputPath = toPosix(path5.join(this.options.outputDir, `${effectiveName}${ext}`));
18994
+ const outputPath = toPosix(path6.join(this.options.outputDir, `${effectiveName}${ext}`));
18965
18995
  const content = await this.formatContent(run, format);
18966
- const dir = path5.dirname(outputPath);
18996
+ const dir = path6.dirname(outputPath);
18967
18997
  await fsPromises.mkdir(dir, { recursive: true });
18968
18998
  await this.deps.writeFile(outputPath, content);
18969
18999
  return [outputPath];
@@ -18975,7 +19005,7 @@ var ReportGenerator = class {
18975
19005
  testCases
18976
19006
  };
18977
19007
  const content = await this.formatContent(groupRun, format);
18978
- const dir = path5.dirname(outputPath);
19008
+ const dir = path6.dirname(outputPath);
18979
19009
  await fsPromises.mkdir(dir, { recursive: true });
18980
19010
  await this.deps.writeFile(outputPath, content);
18981
19011
  writtenPaths.push(outputPath);
@@ -19095,7 +19125,7 @@ async function generateRunComparison(args) {
19095
19125
  await fsPromises.mkdir(outputDir, { recursive: true });
19096
19126
  for (const format of args.formats) {
19097
19127
  const ext = format === "html" ? ".html" : ".md";
19098
- const outputPath = toPosix(path5.join(outputDir, `${outputName}${ext}`));
19128
+ const outputPath = toPosix(path6.join(outputDir, `${outputName}${ext}`));
19099
19129
  const content = format === "html" ? new RunDiffHtmlFormatter({ title: args.title }).format(diff) : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);
19100
19130
  await fsPromises.writeFile(outputPath, content, "utf8");
19101
19131
  files.push(outputPath);
@@ -19104,23 +19134,23 @@ async function generateRunComparison(args) {
19104
19134
  }
19105
19135
 
19106
19136
  // src/init-astro.ts
19107
- import * as fs6 from "fs";
19108
- import * as path6 from "path";
19137
+ import * as fs7 from "fs";
19138
+ import * as path7 from "path";
19109
19139
  import { fileURLToPath } from "url";
19110
- var __dirname = path6.dirname(fileURLToPath(import.meta.url));
19140
+ var __dirname = path7.dirname(fileURLToPath(import.meta.url));
19111
19141
  function initAstro(options = {}) {
19112
19142
  const targetDir = options.targetDir ?? "./story-docs";
19113
19143
  const force = options.force ?? false;
19114
- if (fs6.existsSync(targetDir)) {
19115
- const entries = fs6.readdirSync(targetDir);
19144
+ if (fs7.existsSync(targetDir)) {
19145
+ const entries = fs7.readdirSync(targetDir);
19116
19146
  if (entries.length > 0 && !force) {
19117
19147
  throw new Error(
19118
19148
  `Directory "${targetDir}" already exists and is not empty. Use --force to overwrite.`
19119
19149
  );
19120
19150
  }
19121
19151
  }
19122
- const templateDir = path6.resolve(__dirname, "..", "templates", "astro-starlight");
19123
- if (!fs6.existsSync(templateDir)) {
19152
+ const templateDir = path7.resolve(__dirname, "..", "templates", "astro-starlight");
19153
+ if (!fs7.existsSync(templateDir)) {
19124
19154
  throw new Error(
19125
19155
  `Template directory not found at ${templateDir}. Ensure the package is installed correctly.`
19126
19156
  );
@@ -19129,25 +19159,25 @@ function initAstro(options = {}) {
19129
19159
  return { targetDir };
19130
19160
  }
19131
19161
  function copyDirRecursive(src, dest) {
19132
- fs6.mkdirSync(dest, { recursive: true });
19133
- const entries = fs6.readdirSync(src, { withFileTypes: true });
19162
+ fs7.mkdirSync(dest, { recursive: true });
19163
+ const entries = fs7.readdirSync(src, { withFileTypes: true });
19134
19164
  for (const entry of entries) {
19135
- const srcPath = path6.join(src, entry.name);
19136
- const destPath = path6.join(dest, entry.name);
19165
+ const srcPath = path7.join(src, entry.name);
19166
+ const destPath = path7.join(dest, entry.name);
19137
19167
  if (entry.isDirectory()) {
19138
19168
  copyDirRecursive(srcPath, destPath);
19139
19169
  } else {
19140
- fs6.copyFileSync(srcPath, destPath);
19170
+ fs7.copyFileSync(srcPath, destPath);
19141
19171
  }
19142
19172
  }
19143
19173
  }
19144
19174
 
19145
19175
  // src/config.ts
19146
- import { existsSync as existsSync6 } from "fs";
19176
+ import { existsSync as existsSync7 } from "fs";
19147
19177
  import { resolve as resolve6 } from "path";
19148
19178
  async function loadConfig(configPath) {
19149
19179
  const resolved = configPath ? resolve6(configPath) : resolve6(process.cwd(), "executable-stories.config.js");
19150
- if (!existsSync6(resolved)) return {};
19180
+ if (!existsSync7(resolved)) return {};
19151
19181
  const mod = await import(resolved);
19152
19182
  const config = mod.default;
19153
19183
  if (!config || typeof config !== "object" || Array.isArray(config)) {
@@ -19566,20 +19596,20 @@ async function readInput(args) {
19566
19596
  if (args.stdin) {
19567
19597
  return readStdin();
19568
19598
  }
19569
- const filePath = path7.resolve(args.inputFile);
19570
- if (!fs7.existsSync(filePath)) {
19599
+ const filePath = path8.resolve(args.inputFile);
19600
+ if (!fs8.existsSync(filePath)) {
19571
19601
  console.error(`Error: File not found: ${filePath}`);
19572
19602
  process.exit(EXIT_USAGE);
19573
19603
  }
19574
- return fs7.readFileSync(filePath, "utf8");
19604
+ return fs8.readFileSync(filePath, "utf8");
19575
19605
  }
19576
19606
  function readFileInput(filePath) {
19577
- const resolved = path7.resolve(filePath);
19578
- if (!fs7.existsSync(resolved)) {
19607
+ const resolved = path8.resolve(filePath);
19608
+ if (!fs8.existsSync(resolved)) {
19579
19609
  console.error(`Error: File not found: ${resolved}`);
19580
19610
  process.exit(EXIT_USAGE);
19581
19611
  }
19582
- return fs7.readFileSync(resolved, "utf8");
19612
+ return fs8.readFileSync(resolved, "utf8");
19583
19613
  }
19584
19614
  function readStdin() {
19585
19615
  return new Promise((resolve8, reject) => {
@@ -19712,14 +19742,14 @@ function tryNormalizeRunFromText(text2, args) {
19712
19742
  }
19713
19743
  }
19714
19744
  function listBaselineCandidates(currentFile, args) {
19715
- const baselineDir = path7.resolve(args.baselineDir ?? path7.dirname(currentFile));
19716
- const currentResolved = path7.resolve(currentFile);
19717
- if (!fs7.existsSync(baselineDir)) {
19745
+ const baselineDir = path8.resolve(args.baselineDir ?? path8.dirname(currentFile));
19746
+ const currentResolved = path8.resolve(currentFile);
19747
+ if (!fs8.existsSync(baselineDir)) {
19718
19748
  console.error(`Error: baseline directory not found: ${baselineDir}`);
19719
19749
  process.exit(EXIT_USAGE);
19720
19750
  }
19721
- const entries = fs7.readdirSync(baselineDir, { withFileTypes: true });
19722
- return entries.filter((entry) => entry.isFile()).map((entry) => path7.join(baselineDir, entry.name)).filter((candidate) => path7.resolve(candidate) !== currentResolved).filter(
19751
+ const entries = fs8.readdirSync(baselineDir, { withFileTypes: true });
19752
+ return entries.filter((entry) => entry.isFile()).map((entry) => path8.join(baselineDir, entry.name)).filter((candidate) => path8.resolve(candidate) !== currentResolved).filter(
19723
19753
  (candidate) => args.inputType === "ndjson" ? candidate.endsWith(".ndjson") : candidate.endsWith(".json")
19724
19754
  );
19725
19755
  }
@@ -19727,14 +19757,14 @@ function resolveBaselineAuto(currentFile, currentRun, args) {
19727
19757
  const candidates = listBaselineCandidates(currentFile, args);
19728
19758
  const comparable = [];
19729
19759
  for (const candidate of candidates) {
19730
- const run = tryNormalizeRunFromText(fs7.readFileSync(candidate, "utf8"), args);
19760
+ const run = tryNormalizeRunFromText(fs8.readFileSync(candidate, "utf8"), args);
19731
19761
  if (run) {
19732
19762
  comparable.push({ file: candidate, run });
19733
19763
  }
19734
19764
  }
19735
19765
  if (comparable.length === 0) {
19736
19766
  console.error(
19737
- `Error: no compatible baseline files found in ${path7.resolve(args.baselineDir ?? path7.dirname(currentFile))}.`
19767
+ `Error: no compatible baseline files found in ${path8.resolve(args.baselineDir ?? path8.dirname(currentFile))}.`
19738
19768
  );
19739
19769
  process.exit(EXIT_USAGE);
19740
19770
  }
@@ -19823,9 +19853,9 @@ async function main() {
19823
19853
  process.exit(EXIT_SCHEMA_VALIDATION);
19824
19854
  }
19825
19855
  if (args.emitCanonical) {
19826
- const outPath = path7.resolve(args.emitCanonical);
19827
- fs7.mkdirSync(path7.dirname(outPath), { recursive: true });
19828
- fs7.writeFileSync(outPath, JSON.stringify(run, null, 2), "utf8");
19856
+ const outPath = path8.resolve(args.emitCanonical);
19857
+ fs8.mkdirSync(path8.dirname(outPath), { recursive: true });
19858
+ fs8.writeFileSync(outPath, JSON.stringify(run, null, 2), "utf8");
19829
19859
  }
19830
19860
  try {
19831
19861
  const result = await generateReports(run, args);
@@ -19882,9 +19912,9 @@ ${msg}`);
19882
19912
  }
19883
19913
  const run = data;
19884
19914
  if (args.emitCanonical) {
19885
- const outPath = path7.resolve(args.emitCanonical);
19886
- fs7.mkdirSync(path7.dirname(outPath), { recursive: true });
19887
- fs7.writeFileSync(outPath, JSON.stringify(run, null, 2), "utf8");
19915
+ const outPath = path8.resolve(args.emitCanonical);
19916
+ fs8.mkdirSync(path8.dirname(outPath), { recursive: true });
19917
+ fs8.writeFileSync(outPath, JSON.stringify(run, null, 2), "utf8");
19888
19918
  }
19889
19919
  try {
19890
19920
  const result = await generateReports(run, args);
@@ -19940,9 +19970,9 @@ ${msg}`);
19940
19970
  process.exit(EXIT_CANONICAL_VALIDATION);
19941
19971
  }
19942
19972
  if (args.emitCanonical) {
19943
- const outPath = path7.resolve(args.emitCanonical);
19944
- fs7.mkdirSync(path7.dirname(outPath), { recursive: true });
19945
- fs7.writeFileSync(outPath, JSON.stringify(canonical, null, 2), "utf8");
19973
+ const outPath = path8.resolve(args.emitCanonical);
19974
+ fs8.mkdirSync(path8.dirname(outPath), { recursive: true });
19975
+ fs8.writeFileSync(outPath, JSON.stringify(canonical, null, 2), "utf8");
19946
19976
  }
19947
19977
  try {
19948
19978
  const result = await generateReports(canonical, args, droppedMissingStory);
@@ -19967,9 +19997,9 @@ function runCustomFormatters(run, customRequested, formatters, args) {
19967
19997
  const ext = formatter.fileExtension ?? formatName;
19968
19998
  const baseName = args.outputName ?? "report";
19969
19999
  const filename = args.outputNameTimestamp ? `${baseName}-${Math.floor(run.startedAtMs / 1e3)}.${ext}` : `${baseName}.${ext}`;
19970
- const filepath = path7.join(outputDir, filename);
19971
- fs7.mkdirSync(outputDir, { recursive: true });
19972
- fs7.writeFileSync(filepath, content, "utf8");
20000
+ const filepath = path8.join(outputDir, filename);
20001
+ fs8.mkdirSync(outputDir, { recursive: true });
20002
+ fs8.writeFileSync(filepath, content, "utf8");
19973
20003
  console.log(`Generated: ${filepath}`);
19974
20004
  } catch (err) {
19975
20005
  console.error(`Error running custom formatter "${formatName}": ${err instanceof Error ? err.message : String(err)}`);
@@ -20019,13 +20049,13 @@ async function dispatchNotifications(run, args) {
20019
20049
  }
20020
20050
  function runHistoryPipeline(run, args) {
20021
20051
  if (!args.historyFile) return;
20022
- const historyPath = path7.resolve(args.historyFile);
20052
+ const historyPath = path8.resolve(args.historyFile);
20023
20053
  const store = loadHistory(
20024
20054
  { filePath: historyPath },
20025
20055
  {
20026
20056
  readFile: (p) => {
20027
20057
  try {
20028
- return fs7.readFileSync(p, "utf8");
20058
+ return fs8.readFileSync(p, "utf8");
20029
20059
  } catch {
20030
20060
  return void 0;
20031
20061
  }
@@ -20038,11 +20068,11 @@ function runHistoryPipeline(run, args) {
20038
20068
  run,
20039
20069
  maxRuns: args.maxHistoryRuns
20040
20070
  });
20041
- const dir = path7.dirname(historyPath);
20042
- fs7.mkdirSync(dir, { recursive: true });
20071
+ const dir = path8.dirname(historyPath);
20072
+ fs8.mkdirSync(dir, { recursive: true });
20043
20073
  saveHistory(
20044
20074
  { filePath: historyPath, store: updated },
20045
- { writeFile: (p, content) => fs7.writeFileSync(p, content, "utf8") }
20075
+ { writeFile: (p, content) => fs8.writeFileSync(p, content, "utf8") }
20046
20076
  );
20047
20077
  let metricsCount = 0;
20048
20078
  for (const testId of Object.keys(updated.tests)) {
@@ -20140,9 +20170,9 @@ function printResult(result, args, startMs, droppedMissingStory = 0) {
20140
20170
  function printCompareResult(result, args, startMs) {
20141
20171
  const durationMs = Date.now() - startMs;
20142
20172
  if (result.prSummary && args.prSummaryFile) {
20143
- const outputPath = path7.resolve(args.prSummaryFile);
20144
- fs7.mkdirSync(path7.dirname(outputPath), { recursive: true });
20145
- fs7.writeFileSync(outputPath, result.prSummary, "utf8");
20173
+ const outputPath = path8.resolve(args.prSummaryFile);
20174
+ fs8.mkdirSync(path8.dirname(outputPath), { recursive: true });
20175
+ fs8.writeFileSync(outputPath, result.prSummary, "utf8");
20146
20176
  }
20147
20177
  if (args.jsonSummary) {
20148
20178
  console.log(
@@ -20213,7 +20243,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
20213
20243
  console.error("Error: missing ADF file argument. Run with --help for usage.");
20214
20244
  process.exit(EXIT_USAGE);
20215
20245
  }
20216
- if (!fs7.existsSync(inputFile)) {
20246
+ if (!fs8.existsSync(inputFile)) {
20217
20247
  console.error(`Error: file not found: ${inputFile}`);
20218
20248
  process.exit(EXIT_USAGE);
20219
20249
  }
@@ -20241,7 +20271,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
20241
20271
  console.error("Error: --title is required when creating a new page");
20242
20272
  process.exit(EXIT_USAGE);
20243
20273
  }
20244
- const adf = fs7.readFileSync(path7.resolve(inputFile), "utf8");
20274
+ const adf = fs8.readFileSync(path8.resolve(inputFile), "utf8");
20245
20275
  if (dryRun) {
20246
20276
  console.log(
20247
20277
  JSON.stringify(
@@ -20320,7 +20350,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
20320
20350
  console.error("Error: missing ADF file argument. Run with --help for usage.");
20321
20351
  process.exit(EXIT_USAGE);
20322
20352
  }
20323
- if (!fs7.existsSync(inputFile)) {
20353
+ if (!fs8.existsSync(inputFile)) {
20324
20354
  console.error(`Error: file not found: ${inputFile}`);
20325
20355
  process.exit(EXIT_USAGE);
20326
20356
  }
@@ -20347,7 +20377,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
20347
20377
  process.exit(EXIT_USAGE);
20348
20378
  }
20349
20379
  const mode = modeRaw;
20350
- const adf = fs7.readFileSync(path7.resolve(inputFile), "utf8");
20380
+ const adf = fs8.readFileSync(path8.resolve(inputFile), "utf8");
20351
20381
  if (dryRun) {
20352
20382
  console.log(
20353
20383
  JSON.stringify(