executable-stories-formatters 0.7.10 → 0.7.12

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
@@ -2988,6 +2992,27 @@ body {
2988
2992
  border: 1px solid var(--border);
2989
2993
  }
2990
2994
 
2995
+ .attachment-unavailable {
2996
+ padding: 0.75rem 1rem;
2997
+ border: 1px dashed var(--border);
2998
+ border-radius: calc(var(--radius) - 2px);
2999
+ background: var(--muted, transparent);
3000
+ color: var(--muted-foreground);
3001
+ font-size: 0.8125rem;
3002
+ }
3003
+
3004
+ .attachment-unavailable-label {
3005
+ font-weight: 600;
3006
+ margin-bottom: 0.25rem;
3007
+ }
3008
+
3009
+ .attachment-unavailable-path {
3010
+ font-family: var(--font-mono, ui-monospace, monospace);
3011
+ font-size: 0.75rem;
3012
+ word-break: break-all;
3013
+ opacity: 0.8;
3014
+ }
3015
+
2991
3016
  /* ============================================================================
2992
3017
  Chevron Icon - smooth rotation
2993
3018
  ============================================================================ */
@@ -3553,6 +3578,27 @@ body {
3553
3578
  font-style: italic;
3554
3579
  }
3555
3580
 
3581
+ .doc-screenshot-missing {
3582
+ padding: 0.75rem 1rem;
3583
+ border: 1px dashed var(--border);
3584
+ border-radius: calc(var(--radius) - 2px);
3585
+ background: var(--muted, transparent);
3586
+ color: var(--muted-foreground);
3587
+ font-size: 0.8125rem;
3588
+ }
3589
+
3590
+ .doc-screenshot-missing-label {
3591
+ font-weight: 600;
3592
+ margin-bottom: 0.25rem;
3593
+ }
3594
+
3595
+ .doc-screenshot-missing-path {
3596
+ font-family: var(--font-mono, ui-monospace, monospace);
3597
+ font-size: 0.75rem;
3598
+ word-break: break-all;
3599
+ opacity: 0.8;
3600
+ }
3601
+
3556
3602
  /* ============================================================================
3557
3603
  Documentation Entries - Custom
3558
3604
  ============================================================================ */
@@ -13527,11 +13573,18 @@ function renderTagBar(args, deps) {
13527
13573
  </div>`;
13528
13574
  }
13529
13575
 
13576
+ // src/notifiers/ansi-strip.ts
13577
+ function stripAnsi(text2) {
13578
+ return text2.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
13579
+ }
13580
+
13530
13581
  // src/formatters/html/renderers/error-box.ts
13531
13582
  function renderErrorBox(args, deps) {
13532
- const body = args.stack != null ? `${deps.escapeHtml(args.message)}
13583
+ const message = stripAnsi(args.message);
13584
+ const stack = args.stack != null ? stripAnsi(args.stack) : void 0;
13585
+ const body = stack != null ? `${deps.escapeHtml(message)}
13533
13586
 
13534
- ${deps.escapeHtml(args.stack)}` : deps.escapeHtml(args.message);
13587
+ ${deps.escapeHtml(stack)}` : deps.escapeHtml(message);
13535
13588
  return `<div class="error-box">${body}</div>`;
13536
13589
  }
13537
13590
 
@@ -13544,6 +13597,7 @@ function renderAttachments(args, deps) {
13544
13597
  const isImage = att.mediaType.startsWith("image/");
13545
13598
  const isVideo = att.mediaType.startsWith("video/");
13546
13599
  const isBase64 = att.contentEncoding === "BASE64";
13600
+ const isUnreachableFsPath = deps.embedScreenshots && !isBase64 && typeof att.body === "string" && /^(?:[/\\]|[A-Za-z]:[/\\])/.test(att.body);
13547
13601
  if (isImage && deps.embedScreenshots && isBase64) {
13548
13602
  return `
13549
13603
  <div class="attachment">
@@ -13551,12 +13605,19 @@ function renderAttachments(args, deps) {
13551
13605
  <img class="attachment-image" src="data:${att.mediaType};base64,${att.body}" alt="${deps.escapeHtml(att.name)}" />
13552
13606
  </div>`;
13553
13607
  }
13554
- if (isVideo && deps.embedScreenshots) {
13608
+ if (isVideo && deps.embedScreenshots && !isUnreachableFsPath) {
13555
13609
  const src = isBase64 ? `data:${att.mediaType};base64,${att.body}` : att.body;
13556
13610
  return `
13557
13611
  <div class="attachment">
13558
13612
  ${deps.escapeHtml(att.name)}
13559
13613
  <video class="attachment-video" controls src="${deps.escapeHtml(src)}"></video>
13614
+ </div>`;
13615
+ }
13616
+ if (isUnreachableFsPath) {
13617
+ return `
13618
+ <div class="attachment attachment-unavailable">
13619
+ <div class="attachment-unavailable-label">${deps.escapeHtml(att.name)} unavailable</div>
13620
+ <div class="attachment-unavailable-path">${deps.escapeHtml(att.body)}</div>
13560
13621
  </div>`;
13561
13622
  }
13562
13623
  const href = isBase64 ? `data:${att.mediaType};base64,${att.body}` : att.body;
@@ -13637,7 +13698,20 @@ function renderDocMermaid(entry, deps) {
13637
13698
  }
13638
13699
  function renderDocScreenshot(entry, deps) {
13639
13700
  const alt = entry.alt ?? "Screenshot";
13640
- const src = entry.path;
13701
+ const embedEnabled = deps.embedScreenshots ?? true;
13702
+ const isRemote = /^(?:https?:|data:)/i.test(entry.path);
13703
+ const embedAttempted = !isRemote && embedEnabled && !!deps.readScreenshot;
13704
+ const inlined = embedAttempted ? deps.readScreenshot(entry.path) : void 0;
13705
+ const isAbsoluteFsPath = !isRemote && /^(?:[/\\]|[A-Za-z]:[/\\])/.test(entry.path);
13706
+ if (embedAttempted && inlined === void 0 && isAbsoluteFsPath) {
13707
+ const captionHtml = entry.alt ? `<div class="doc-screenshot-caption">${deps.escapeHtml(entry.alt)}</div>` : "";
13708
+ return `<div class="doc-screenshot doc-screenshot-missing">
13709
+ <div class="doc-screenshot-missing-label">Screenshot unavailable</div>
13710
+ <div class="doc-screenshot-missing-path">${deps.escapeHtml(entry.path)}</div>
13711
+ ${captionHtml}
13712
+ </div>`;
13713
+ }
13714
+ const src = inlined ?? entry.path;
13641
13715
  return `<div class="doc-screenshot">
13642
13716
  <img src="${deps.escapeHtml(src)}" alt="${deps.escapeHtml(alt)}" class="doc-screenshot-img" />
13643
13717
  ${entry.alt ? `<div class="doc-screenshot-caption">${deps.escapeHtml(entry.alt)}</div>` : ""}
@@ -14219,6 +14293,28 @@ function renderToc(args, deps) {
14219
14293
  }
14220
14294
 
14221
14295
  // src/formatters/html/renderers/index.ts
14296
+ var SCREENSHOT_MIME_BY_EXT = {
14297
+ png: "image/png",
14298
+ jpg: "image/jpeg",
14299
+ jpeg: "image/jpeg",
14300
+ gif: "image/gif",
14301
+ webp: "image/webp",
14302
+ svg: "image/svg+xml",
14303
+ avif: "image/avif",
14304
+ bmp: "image/bmp"
14305
+ };
14306
+ function readScreenshotAsDataUri(filePath) {
14307
+ try {
14308
+ const ext = path2.extname(filePath).slice(1).toLowerCase();
14309
+ const mime = SCREENSHOT_MIME_BY_EXT[ext];
14310
+ if (!mime) return void 0;
14311
+ if (!fs2.existsSync(filePath)) return void 0;
14312
+ const buf = fs2.readFileSync(filePath);
14313
+ return `data:${mime};base64,${buf.toString("base64")}`;
14314
+ } catch {
14315
+ return void 0;
14316
+ }
14317
+ }
14222
14318
  function normalizeOptions(options = {}) {
14223
14319
  return {
14224
14320
  title: options.title ?? "Test Results",
@@ -14242,7 +14338,9 @@ function createHtmlFormatter(options = {}) {
14242
14338
  escapeHtml,
14243
14339
  syntaxHighlighting: opts.syntaxHighlighting,
14244
14340
  markdownEnabled: opts.markdownEnabled,
14245
- mermaidEnabled: opts.mermaidEnabled
14341
+ mermaidEnabled: opts.mermaidEnabled,
14342
+ embedScreenshots: opts.embedScreenshots,
14343
+ readScreenshot: (filePath) => readScreenshotAsDataUri(filePath)
14246
14344
  };
14247
14345
  const renderDocs = (docs, containerClass) => {
14248
14346
  if (!docs || docs.length === 0) return "";
@@ -15671,8 +15769,8 @@ function extractDocAttachments(step) {
15671
15769
  }
15672
15770
  return attachments;
15673
15771
  }
15674
- function guessMediaType(path8) {
15675
- const lower = path8.toLowerCase();
15772
+ function guessMediaType(path9) {
15773
+ const lower = path9.toLowerCase();
15676
15774
  if (lower.endsWith(".png")) return "image/png";
15677
15775
  if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
15678
15776
  if (lower.endsWith(".gif")) return "image/gif";
@@ -16746,8 +16844,8 @@ function selectTestCases(args, deps) {
16746
16844
  }
16747
16845
 
16748
16846
  // src/bundler/bundle-assets.ts
16749
- import * as fs3 from "fs";
16750
- import * as path3 from "path";
16847
+ import * as fs4 from "fs";
16848
+ import * as path4 from "path";
16751
16849
 
16752
16850
  // src/bundler/scan-html-assets.ts
16753
16851
  function scanHtmlAssets(html) {
@@ -16777,21 +16875,21 @@ function isLocalAssetRef(ref) {
16777
16875
  }
16778
16876
 
16779
16877
  // src/bundler/copy-asset.ts
16780
- import * as fs2 from "fs";
16781
- import * as path2 from "path";
16878
+ import * as fs3 from "fs";
16879
+ import * as path3 from "path";
16782
16880
  import * as crypto from "crypto";
16783
16881
  function copyAsset(sourcePath, assetsDir) {
16784
- if (!fs2.existsSync(assetsDir)) {
16785
- fs2.mkdirSync(assetsDir, { recursive: true });
16882
+ if (!fs3.existsSync(assetsDir)) {
16883
+ fs3.mkdirSync(assetsDir, { recursive: true });
16786
16884
  }
16787
- const content = fs2.readFileSync(sourcePath);
16885
+ const content = fs3.readFileSync(sourcePath);
16788
16886
  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));
16887
+ const ext = path3.extname(sourcePath);
16888
+ const baseName = sanitize(path3.basename(sourcePath, ext));
16791
16889
  const destName = `${baseName}-${hash}${ext}`;
16792
- const destPath = path2.join(assetsDir, destName);
16793
- if (!fs2.existsSync(destPath)) {
16794
- fs2.copyFileSync(sourcePath, destPath);
16890
+ const destPath = path3.join(assetsDir, destName);
16891
+ if (!fs3.existsSync(destPath)) {
16892
+ fs3.copyFileSync(sourcePath, destPath);
16795
16893
  }
16796
16894
  return `assets/${destName}`;
16797
16895
  }
@@ -16801,15 +16899,15 @@ function sanitize(name) {
16801
16899
 
16802
16900
  // src/bundler/bundle-assets.ts
16803
16901
  function bundleAssets(htmlPath, options = {}) {
16804
- const htmlDir = path3.dirname(htmlPath);
16805
- const assetsDir = path3.join(htmlDir, "assets");
16806
- let html = fs3.readFileSync(htmlPath, "utf8");
16902
+ const htmlDir = path4.dirname(htmlPath);
16903
+ const assetsDir = path4.join(htmlDir, "assets");
16904
+ let html = fs4.readFileSync(htmlPath, "utf8");
16807
16905
  const refs = scanHtmlAssets(html);
16808
16906
  let copiedCount = 0;
16809
16907
  const missing = [];
16810
16908
  for (const ref of refs) {
16811
- const absolutePath = path3.resolve(htmlDir, ref);
16812
- if (!fs3.existsSync(absolutePath)) {
16909
+ const absolutePath = path4.resolve(htmlDir, ref);
16910
+ if (!fs4.existsSync(absolutePath)) {
16813
16911
  missing.push(ref);
16814
16912
  continue;
16815
16913
  }
@@ -16822,7 +16920,7 @@ function bundleAssets(htmlPath, options = {}) {
16822
16920
  `Missing asset${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}`
16823
16921
  );
16824
16922
  }
16825
- fs3.writeFileSync(htmlPath, html, "utf8");
16923
+ fs4.writeFileSync(htmlPath, html, "utf8");
16826
16924
  return {
16827
16925
  copiedCount,
16828
16926
  missingCount: missing.length,
@@ -17305,15 +17403,15 @@ function groupBy7(items, keyFn) {
17305
17403
  }
17306
17404
 
17307
17405
  // src/formatters/astro-assets.ts
17308
- import * as fs4 from "fs";
17309
- import * as path4 from "path";
17406
+ import * as fs5 from "fs";
17407
+ import * as path5 from "path";
17310
17408
  var SKIP_PREFIXES = ["http://", "https://", "data:", "#"];
17311
17409
  function isLocalPath(src) {
17312
17410
  const trimmed = src.trim();
17313
17411
  if (SKIP_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {
17314
17412
  return false;
17315
17413
  }
17316
- return !path4.posix.isAbsolute(trimmed) && !path4.win32.isAbsolute(trimmed);
17414
+ return !path5.posix.isAbsolute(trimmed) && !path5.win32.isAbsolute(trimmed);
17317
17415
  }
17318
17416
  function stripCodeContent(markdown) {
17319
17417
  let result = markdown.replace(/^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?^[ \t]*\1\s*$/gm, "");
@@ -17407,8 +17505,8 @@ function copyMarkdownAssets(options) {
17407
17505
  const pathMap = /* @__PURE__ */ new Map();
17408
17506
  const missing = [];
17409
17507
  for (const ref of refs) {
17410
- const absPath = path4.resolve(markdownDir, ref);
17411
- if (!fs4.existsSync(absPath)) {
17508
+ const absPath = path5.resolve(markdownDir, ref);
17509
+ if (!fs5.existsSync(absPath)) {
17412
17510
  if (!allowMissing) {
17413
17511
  throw new Error(`Asset not found: ${absPath}`);
17414
17512
  }
@@ -18022,11 +18120,6 @@ function pickleStepArgumentToDocs(ps) {
18022
18120
  return docs;
18023
18121
  }
18024
18122
 
18025
- // src/notifiers/ansi-strip.ts
18026
- function stripAnsi(text2) {
18027
- return text2.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
18028
- }
18029
-
18030
18123
  // src/notifiers/slack.ts
18031
18124
  function truncate(text2, maxLen) {
18032
18125
  if (text2.length <= maxLen) return text2;
@@ -18716,11 +18809,11 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
18716
18809
  const ext = FORMAT_EXTENSIONS[format];
18717
18810
  const effectiveName = outputName + (outputNameSuffix ?? "");
18718
18811
  if (mode === "aggregated") {
18719
- return toPosix(path5.join(baseOutputDir, `${effectiveName}${ext}`));
18812
+ return toPosix(path6.join(baseOutputDir, `${effectiveName}${ext}`));
18720
18813
  }
18721
18814
  const normalizedSource = toPosix(sourceFile);
18722
- const dirOfSource = path5.posix.dirname(normalizedSource);
18723
- let baseName = path5.posix.basename(normalizedSource);
18815
+ const dirOfSource = path6.posix.dirname(normalizedSource);
18816
+ let baseName = path6.posix.basename(normalizedSource);
18724
18817
  for (const testExt of TEST_EXTENSIONS) {
18725
18818
  if (baseName.endsWith(testExt)) {
18726
18819
  baseName = baseName.slice(0, -testExt.length);
@@ -18729,9 +18822,9 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
18729
18822
  }
18730
18823
  const fileName = `${baseName}.${effectiveName}${ext}`;
18731
18824
  if (colocatedStyle === "adjacent") {
18732
- return toPosix(path5.posix.join(dirOfSource, fileName));
18825
+ return toPosix(path6.posix.join(dirOfSource, fileName));
18733
18826
  }
18734
- return toPosix(path5.posix.join(baseOutputDir, dirOfSource, fileName));
18827
+ return toPosix(path6.posix.join(baseOutputDir, dirOfSource, fileName));
18735
18828
  }
18736
18829
  function groupTestCasesByOutput(testCases, format, options, logger, outputNameSuffix) {
18737
18830
  const groups = /* @__PURE__ */ new Map();
@@ -18929,8 +19022,8 @@ var ReportGenerator = class {
18929
19022
  if (astroPaths) {
18930
19023
  for (const mdPath of astroPaths) {
18931
19024
  const content = await fsPromises.readFile(mdPath, "utf8");
18932
- const mdDir = path5.dirname(mdPath);
18933
- const assetsDir = path5.resolve(this.options.astro.assetsDir);
19025
+ const mdDir = path6.dirname(mdPath);
19026
+ const assetsDir = path6.resolve(this.options.astro.assetsDir);
18934
19027
  const result = copyMarkdownAssets({
18935
19028
  markdown: content,
18936
19029
  markdownDir: mdDir,
@@ -18961,9 +19054,9 @@ var ReportGenerator = class {
18961
19054
  if (groups.size === 0 && this.options.output.mode === "aggregated") {
18962
19055
  const ext = FORMAT_EXTENSIONS[format];
18963
19056
  const effectiveName = this.options.outputName + (outputNameSuffix ?? "");
18964
- const outputPath = toPosix(path5.join(this.options.outputDir, `${effectiveName}${ext}`));
19057
+ const outputPath = toPosix(path6.join(this.options.outputDir, `${effectiveName}${ext}`));
18965
19058
  const content = await this.formatContent(run, format);
18966
- const dir = path5.dirname(outputPath);
19059
+ const dir = path6.dirname(outputPath);
18967
19060
  await fsPromises.mkdir(dir, { recursive: true });
18968
19061
  await this.deps.writeFile(outputPath, content);
18969
19062
  return [outputPath];
@@ -18975,7 +19068,7 @@ var ReportGenerator = class {
18975
19068
  testCases
18976
19069
  };
18977
19070
  const content = await this.formatContent(groupRun, format);
18978
- const dir = path5.dirname(outputPath);
19071
+ const dir = path6.dirname(outputPath);
18979
19072
  await fsPromises.mkdir(dir, { recursive: true });
18980
19073
  await this.deps.writeFile(outputPath, content);
18981
19074
  writtenPaths.push(outputPath);
@@ -19095,7 +19188,7 @@ async function generateRunComparison(args) {
19095
19188
  await fsPromises.mkdir(outputDir, { recursive: true });
19096
19189
  for (const format of args.formats) {
19097
19190
  const ext = format === "html" ? ".html" : ".md";
19098
- const outputPath = toPosix(path5.join(outputDir, `${outputName}${ext}`));
19191
+ const outputPath = toPosix(path6.join(outputDir, `${outputName}${ext}`));
19099
19192
  const content = format === "html" ? new RunDiffHtmlFormatter({ title: args.title }).format(diff) : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);
19100
19193
  await fsPromises.writeFile(outputPath, content, "utf8");
19101
19194
  files.push(outputPath);
@@ -19104,23 +19197,23 @@ async function generateRunComparison(args) {
19104
19197
  }
19105
19198
 
19106
19199
  // src/init-astro.ts
19107
- import * as fs6 from "fs";
19108
- import * as path6 from "path";
19200
+ import * as fs7 from "fs";
19201
+ import * as path7 from "path";
19109
19202
  import { fileURLToPath } from "url";
19110
- var __dirname = path6.dirname(fileURLToPath(import.meta.url));
19203
+ var __dirname = path7.dirname(fileURLToPath(import.meta.url));
19111
19204
  function initAstro(options = {}) {
19112
19205
  const targetDir = options.targetDir ?? "./story-docs";
19113
19206
  const force = options.force ?? false;
19114
- if (fs6.existsSync(targetDir)) {
19115
- const entries = fs6.readdirSync(targetDir);
19207
+ if (fs7.existsSync(targetDir)) {
19208
+ const entries = fs7.readdirSync(targetDir);
19116
19209
  if (entries.length > 0 && !force) {
19117
19210
  throw new Error(
19118
19211
  `Directory "${targetDir}" already exists and is not empty. Use --force to overwrite.`
19119
19212
  );
19120
19213
  }
19121
19214
  }
19122
- const templateDir = path6.resolve(__dirname, "..", "templates", "astro-starlight");
19123
- if (!fs6.existsSync(templateDir)) {
19215
+ const templateDir = path7.resolve(__dirname, "..", "templates", "astro-starlight");
19216
+ if (!fs7.existsSync(templateDir)) {
19124
19217
  throw new Error(
19125
19218
  `Template directory not found at ${templateDir}. Ensure the package is installed correctly.`
19126
19219
  );
@@ -19129,25 +19222,25 @@ function initAstro(options = {}) {
19129
19222
  return { targetDir };
19130
19223
  }
19131
19224
  function copyDirRecursive(src, dest) {
19132
- fs6.mkdirSync(dest, { recursive: true });
19133
- const entries = fs6.readdirSync(src, { withFileTypes: true });
19225
+ fs7.mkdirSync(dest, { recursive: true });
19226
+ const entries = fs7.readdirSync(src, { withFileTypes: true });
19134
19227
  for (const entry of entries) {
19135
- const srcPath = path6.join(src, entry.name);
19136
- const destPath = path6.join(dest, entry.name);
19228
+ const srcPath = path7.join(src, entry.name);
19229
+ const destPath = path7.join(dest, entry.name);
19137
19230
  if (entry.isDirectory()) {
19138
19231
  copyDirRecursive(srcPath, destPath);
19139
19232
  } else {
19140
- fs6.copyFileSync(srcPath, destPath);
19233
+ fs7.copyFileSync(srcPath, destPath);
19141
19234
  }
19142
19235
  }
19143
19236
  }
19144
19237
 
19145
19238
  // src/config.ts
19146
- import { existsSync as existsSync6 } from "fs";
19239
+ import { existsSync as existsSync7 } from "fs";
19147
19240
  import { resolve as resolve6 } from "path";
19148
19241
  async function loadConfig(configPath) {
19149
19242
  const resolved = configPath ? resolve6(configPath) : resolve6(process.cwd(), "executable-stories.config.js");
19150
- if (!existsSync6(resolved)) return {};
19243
+ if (!existsSync7(resolved)) return {};
19151
19244
  const mod = await import(resolved);
19152
19245
  const config = mod.default;
19153
19246
  if (!config || typeof config !== "object" || Array.isArray(config)) {
@@ -19566,20 +19659,20 @@ async function readInput(args) {
19566
19659
  if (args.stdin) {
19567
19660
  return readStdin();
19568
19661
  }
19569
- const filePath = path7.resolve(args.inputFile);
19570
- if (!fs7.existsSync(filePath)) {
19662
+ const filePath = path8.resolve(args.inputFile);
19663
+ if (!fs8.existsSync(filePath)) {
19571
19664
  console.error(`Error: File not found: ${filePath}`);
19572
19665
  process.exit(EXIT_USAGE);
19573
19666
  }
19574
- return fs7.readFileSync(filePath, "utf8");
19667
+ return fs8.readFileSync(filePath, "utf8");
19575
19668
  }
19576
19669
  function readFileInput(filePath) {
19577
- const resolved = path7.resolve(filePath);
19578
- if (!fs7.existsSync(resolved)) {
19670
+ const resolved = path8.resolve(filePath);
19671
+ if (!fs8.existsSync(resolved)) {
19579
19672
  console.error(`Error: File not found: ${resolved}`);
19580
19673
  process.exit(EXIT_USAGE);
19581
19674
  }
19582
- return fs7.readFileSync(resolved, "utf8");
19675
+ return fs8.readFileSync(resolved, "utf8");
19583
19676
  }
19584
19677
  function readStdin() {
19585
19678
  return new Promise((resolve8, reject) => {
@@ -19712,14 +19805,14 @@ function tryNormalizeRunFromText(text2, args) {
19712
19805
  }
19713
19806
  }
19714
19807
  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)) {
19808
+ const baselineDir = path8.resolve(args.baselineDir ?? path8.dirname(currentFile));
19809
+ const currentResolved = path8.resolve(currentFile);
19810
+ if (!fs8.existsSync(baselineDir)) {
19718
19811
  console.error(`Error: baseline directory not found: ${baselineDir}`);
19719
19812
  process.exit(EXIT_USAGE);
19720
19813
  }
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(
19814
+ const entries = fs8.readdirSync(baselineDir, { withFileTypes: true });
19815
+ return entries.filter((entry) => entry.isFile()).map((entry) => path8.join(baselineDir, entry.name)).filter((candidate) => path8.resolve(candidate) !== currentResolved).filter(
19723
19816
  (candidate) => args.inputType === "ndjson" ? candidate.endsWith(".ndjson") : candidate.endsWith(".json")
19724
19817
  );
19725
19818
  }
@@ -19727,14 +19820,14 @@ function resolveBaselineAuto(currentFile, currentRun, args) {
19727
19820
  const candidates = listBaselineCandidates(currentFile, args);
19728
19821
  const comparable = [];
19729
19822
  for (const candidate of candidates) {
19730
- const run = tryNormalizeRunFromText(fs7.readFileSync(candidate, "utf8"), args);
19823
+ const run = tryNormalizeRunFromText(fs8.readFileSync(candidate, "utf8"), args);
19731
19824
  if (run) {
19732
19825
  comparable.push({ file: candidate, run });
19733
19826
  }
19734
19827
  }
19735
19828
  if (comparable.length === 0) {
19736
19829
  console.error(
19737
- `Error: no compatible baseline files found in ${path7.resolve(args.baselineDir ?? path7.dirname(currentFile))}.`
19830
+ `Error: no compatible baseline files found in ${path8.resolve(args.baselineDir ?? path8.dirname(currentFile))}.`
19738
19831
  );
19739
19832
  process.exit(EXIT_USAGE);
19740
19833
  }
@@ -19823,9 +19916,9 @@ async function main() {
19823
19916
  process.exit(EXIT_SCHEMA_VALIDATION);
19824
19917
  }
19825
19918
  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");
19919
+ const outPath = path8.resolve(args.emitCanonical);
19920
+ fs8.mkdirSync(path8.dirname(outPath), { recursive: true });
19921
+ fs8.writeFileSync(outPath, JSON.stringify(run, null, 2), "utf8");
19829
19922
  }
19830
19923
  try {
19831
19924
  const result = await generateReports(run, args);
@@ -19882,9 +19975,9 @@ ${msg}`);
19882
19975
  }
19883
19976
  const run = data;
19884
19977
  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");
19978
+ const outPath = path8.resolve(args.emitCanonical);
19979
+ fs8.mkdirSync(path8.dirname(outPath), { recursive: true });
19980
+ fs8.writeFileSync(outPath, JSON.stringify(run, null, 2), "utf8");
19888
19981
  }
19889
19982
  try {
19890
19983
  const result = await generateReports(run, args);
@@ -19940,9 +20033,9 @@ ${msg}`);
19940
20033
  process.exit(EXIT_CANONICAL_VALIDATION);
19941
20034
  }
19942
20035
  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");
20036
+ const outPath = path8.resolve(args.emitCanonical);
20037
+ fs8.mkdirSync(path8.dirname(outPath), { recursive: true });
20038
+ fs8.writeFileSync(outPath, JSON.stringify(canonical, null, 2), "utf8");
19946
20039
  }
19947
20040
  try {
19948
20041
  const result = await generateReports(canonical, args, droppedMissingStory);
@@ -19967,9 +20060,9 @@ function runCustomFormatters(run, customRequested, formatters, args) {
19967
20060
  const ext = formatter.fileExtension ?? formatName;
19968
20061
  const baseName = args.outputName ?? "report";
19969
20062
  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");
20063
+ const filepath = path8.join(outputDir, filename);
20064
+ fs8.mkdirSync(outputDir, { recursive: true });
20065
+ fs8.writeFileSync(filepath, content, "utf8");
19973
20066
  console.log(`Generated: ${filepath}`);
19974
20067
  } catch (err) {
19975
20068
  console.error(`Error running custom formatter "${formatName}": ${err instanceof Error ? err.message : String(err)}`);
@@ -20019,13 +20112,13 @@ async function dispatchNotifications(run, args) {
20019
20112
  }
20020
20113
  function runHistoryPipeline(run, args) {
20021
20114
  if (!args.historyFile) return;
20022
- const historyPath = path7.resolve(args.historyFile);
20115
+ const historyPath = path8.resolve(args.historyFile);
20023
20116
  const store = loadHistory(
20024
20117
  { filePath: historyPath },
20025
20118
  {
20026
20119
  readFile: (p) => {
20027
20120
  try {
20028
- return fs7.readFileSync(p, "utf8");
20121
+ return fs8.readFileSync(p, "utf8");
20029
20122
  } catch {
20030
20123
  return void 0;
20031
20124
  }
@@ -20038,11 +20131,11 @@ function runHistoryPipeline(run, args) {
20038
20131
  run,
20039
20132
  maxRuns: args.maxHistoryRuns
20040
20133
  });
20041
- const dir = path7.dirname(historyPath);
20042
- fs7.mkdirSync(dir, { recursive: true });
20134
+ const dir = path8.dirname(historyPath);
20135
+ fs8.mkdirSync(dir, { recursive: true });
20043
20136
  saveHistory(
20044
20137
  { filePath: historyPath, store: updated },
20045
- { writeFile: (p, content) => fs7.writeFileSync(p, content, "utf8") }
20138
+ { writeFile: (p, content) => fs8.writeFileSync(p, content, "utf8") }
20046
20139
  );
20047
20140
  let metricsCount = 0;
20048
20141
  for (const testId of Object.keys(updated.tests)) {
@@ -20140,9 +20233,9 @@ function printResult(result, args, startMs, droppedMissingStory = 0) {
20140
20233
  function printCompareResult(result, args, startMs) {
20141
20234
  const durationMs = Date.now() - startMs;
20142
20235
  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");
20236
+ const outputPath = path8.resolve(args.prSummaryFile);
20237
+ fs8.mkdirSync(path8.dirname(outputPath), { recursive: true });
20238
+ fs8.writeFileSync(outputPath, result.prSummary, "utf8");
20146
20239
  }
20147
20240
  if (args.jsonSummary) {
20148
20241
  console.log(
@@ -20213,7 +20306,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
20213
20306
  console.error("Error: missing ADF file argument. Run with --help for usage.");
20214
20307
  process.exit(EXIT_USAGE);
20215
20308
  }
20216
- if (!fs7.existsSync(inputFile)) {
20309
+ if (!fs8.existsSync(inputFile)) {
20217
20310
  console.error(`Error: file not found: ${inputFile}`);
20218
20311
  process.exit(EXIT_USAGE);
20219
20312
  }
@@ -20241,7 +20334,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
20241
20334
  console.error("Error: --title is required when creating a new page");
20242
20335
  process.exit(EXIT_USAGE);
20243
20336
  }
20244
- const adf = fs7.readFileSync(path7.resolve(inputFile), "utf8");
20337
+ const adf = fs8.readFileSync(path8.resolve(inputFile), "utf8");
20245
20338
  if (dryRun) {
20246
20339
  console.log(
20247
20340
  JSON.stringify(
@@ -20320,7 +20413,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
20320
20413
  console.error("Error: missing ADF file argument. Run with --help for usage.");
20321
20414
  process.exit(EXIT_USAGE);
20322
20415
  }
20323
- if (!fs7.existsSync(inputFile)) {
20416
+ if (!fs8.existsSync(inputFile)) {
20324
20417
  console.error(`Error: file not found: ${inputFile}`);
20325
20418
  process.exit(EXIT_USAGE);
20326
20419
  }
@@ -20347,7 +20440,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
20347
20440
  process.exit(EXIT_USAGE);
20348
20441
  }
20349
20442
  const mode = modeRaw;
20350
- const adf = fs7.readFileSync(path7.resolve(inputFile), "utf8");
20443
+ const adf = fs8.readFileSync(path8.resolve(inputFile), "utf8");
20351
20444
  if (dryRun) {
20352
20445
  console.log(
20353
20446
  JSON.stringify(