Version not found. Please check the version and try again.

executable-stories-react 0.11.0 → 0.12.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.
package/dist/ssr.js CHANGED
@@ -290,23 +290,48 @@ function useReport() {
290
290
  // src/components/ReportScenario.tsx
291
291
  import { formatDuration as formatDuration3 } from "executable-stories-core/utils/duration";
292
292
 
293
+ // src/lib/step-params.tsx
294
+ import { jsx as jsx3 } from "react/jsx-runtime";
295
+ var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
296
+ function highlightStepParams(text) {
297
+ const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
298
+ if (matches.length === 0) return text;
299
+ const nodes = [];
300
+ let lastIndex = 0;
301
+ matches.forEach((match, i) => {
302
+ const start = match.index;
303
+ const end = start + match[0].length;
304
+ if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
305
+ nodes.push(
306
+ /* @__PURE__ */ jsx3("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
307
+ );
308
+ lastIndex = end;
309
+ });
310
+ if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
311
+ return nodes;
312
+ }
313
+
293
314
  // src/components/ReportSteps.tsx
315
+ import {
316
+ assertionState,
317
+ assertiveSteps
318
+ } from "executable-stories-core/utils/assertive-steps";
294
319
  import { formatDuration } from "executable-stories-core/utils/duration";
295
320
 
296
321
  // src/components/doc/DocNote.tsx
297
- import { jsx as jsx3 } from "react/jsx-runtime";
322
+ import { jsx as jsx4 } from "react/jsx-runtime";
298
323
  function DocNote({ entry }) {
299
- return /* @__PURE__ */ jsx3("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
324
+ return /* @__PURE__ */ jsx4("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
300
325
  }
301
326
 
302
327
  // src/components/doc/DocTag.tsx
303
- import { jsx as jsx4 } from "react/jsx-runtime";
328
+ import { jsx as jsx5 } from "react/jsx-runtime";
304
329
  function DocTag({ entry }) {
305
- return /* @__PURE__ */ jsx4("ul", { className: "my-2 flex flex-wrap gap-1.5", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(Badge, { variant: "tag", children: n }) }, n)) });
330
+ return /* @__PURE__ */ jsx5("ul", { className: "my-2 flex flex-wrap gap-1.5", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ jsx5("li", { children: /* @__PURE__ */ jsx5(Badge, { variant: "tag", children: n }) }, n)) });
306
331
  }
307
332
 
308
333
  // src/components/doc/DocKv.tsx
309
- import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
334
+ import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
310
335
  function formatValue(value) {
311
336
  if (value === null) return "null";
312
337
  if (typeof value === "string") return value;
@@ -319,8 +344,8 @@ function formatValue(value) {
319
344
  }
320
345
  function DocKv({ entry }) {
321
346
  return /* @__PURE__ */ jsxs2("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
322
- /* @__PURE__ */ jsx5("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
323
- /* @__PURE__ */ jsx5("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
347
+ /* @__PURE__ */ jsx6("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
348
+ /* @__PURE__ */ jsx6("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
324
349
  ] });
325
350
  }
326
351
 
@@ -338,37 +363,37 @@ function useBuiltinRenderers() {
338
363
  }
339
364
 
340
365
  // src/components/doc/DocCode.tsx
341
- import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
366
+ import { Fragment, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
342
367
  function CodeFigure({ label, children }) {
343
368
  return /* @__PURE__ */ jsxs3("figure", { className: "my-3", children: [
344
- label ? /* @__PURE__ */ jsx6("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
345
- /* @__PURE__ */ jsx6("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
369
+ label ? /* @__PURE__ */ jsx7("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
370
+ /* @__PURE__ */ jsx7("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
346
371
  ] });
347
372
  }
348
373
  function DocCode({ entry }) {
349
374
  const renderers = useBuiltinRenderers();
350
375
  if (renderers.code) {
351
- return /* @__PURE__ */ jsx6(Fragment, { children: renderers.code(entry) });
376
+ return /* @__PURE__ */ jsx7(Fragment, { children: renderers.code(entry) });
352
377
  }
353
- return /* @__PURE__ */ jsx6(CodeFigure, { label: entry.label, children: /* @__PURE__ */ jsx6("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
378
+ return /* @__PURE__ */ jsx7(CodeFigure, { label: entry.label, children: /* @__PURE__ */ jsx7("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
354
379
  }
355
380
 
356
381
  // src/components/doc/DocTable.tsx
357
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
382
+ import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
358
383
  function DocTable({ entry }) {
359
384
  return /* @__PURE__ */ jsxs4("figure", { className: "my-3", children: [
360
- entry.label ? /* @__PURE__ */ jsx7("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
361
- /* @__PURE__ */ jsx7("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ jsxs4("table", { className: "w-full border-collapse text-sm", children: [
362
- /* @__PURE__ */ jsx7("thead", { children: /* @__PURE__ */ jsx7("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ jsx7("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
363
- /* @__PURE__ */ jsx7("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ jsx7("tr", { children: row.map((cell, j) => /* @__PURE__ */ jsx7("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell }, j)) }, i)) })
385
+ entry.label ? /* @__PURE__ */ jsx8("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
386
+ /* @__PURE__ */ jsx8("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ jsxs4("table", { className: "w-full border-collapse text-sm", children: [
387
+ /* @__PURE__ */ jsx8("thead", { children: /* @__PURE__ */ jsx8("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ jsx8("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
388
+ /* @__PURE__ */ jsx8("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ jsx8("tr", { children: row.map((cell, j) => /* @__PURE__ */ jsx8("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell }, j)) }, i)) })
364
389
  ] }) })
365
390
  ] });
366
391
  }
367
392
 
368
393
  // src/components/doc/DocLink.tsx
369
- import { jsx as jsx8 } from "react/jsx-runtime";
394
+ import { jsx as jsx9 } from "react/jsx-runtime";
370
395
  function DocLink({ entry }) {
371
- return /* @__PURE__ */ jsx8(
396
+ return /* @__PURE__ */ jsx9(
372
397
  "a",
373
398
  {
374
399
  className: "text-sm font-medium text-primary underline underline-offset-2 hover:no-underline",
@@ -402,16 +427,16 @@ function neutralizeUrl(value) {
402
427
  }
403
428
 
404
429
  // src/components/doc/DocSection.tsx
405
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
430
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
406
431
  function DocSection({ entry }) {
407
432
  const renderers = useBuiltinRenderers();
408
433
  if (renderers.section) {
409
- return /* @__PURE__ */ jsx9(Fragment2, { children: renderers.section(entry) });
434
+ return /* @__PURE__ */ jsx10(Fragment2, { children: renderers.section(entry) });
410
435
  }
411
436
  const html = safeMarkdownHtml(entry.markdown);
412
437
  return /* @__PURE__ */ jsxs5("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
413
- entry.title ? /* @__PURE__ */ jsx9("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
414
- /* @__PURE__ */ jsx9(
438
+ entry.title ? /* @__PURE__ */ jsx10("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
439
+ /* @__PURE__ */ jsx10(
415
440
  "div",
416
441
  {
417
442
  className: "es-doc-prose prose prose-sm max-w-none",
@@ -422,11 +447,11 @@ function DocSection({ entry }) {
422
447
  }
423
448
 
424
449
  // src/components/doc/DocMermaid.tsx
425
- import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
450
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
426
451
  function MermaidSource({ entry, ref }) {
427
452
  return /* @__PURE__ */ jsxs6("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
428
- entry.title ? /* @__PURE__ */ jsx10("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
429
- /* @__PURE__ */ jsx10(
453
+ entry.title ? /* @__PURE__ */ jsx11("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
454
+ /* @__PURE__ */ jsx11(
430
455
  "pre",
431
456
  {
432
457
  "data-mermaid": true,
@@ -440,24 +465,24 @@ function MermaidSource({ entry, ref }) {
440
465
  function DocMermaid({ entry }) {
441
466
  const renderers = useBuiltinRenderers();
442
467
  if (renderers.mermaid) {
443
- return /* @__PURE__ */ jsx10(Fragment3, { children: renderers.mermaid(entry) });
468
+ return /* @__PURE__ */ jsx11(Fragment3, { children: renderers.mermaid(entry) });
444
469
  }
445
- return /* @__PURE__ */ jsx10(MermaidSource, { entry });
470
+ return /* @__PURE__ */ jsx11(MermaidSource, { entry });
446
471
  }
447
472
 
448
473
  // src/components/doc/DocScreenshot.tsx
449
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
474
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
450
475
  function DocScreenshot({ entry }) {
451
476
  const src = isLocalFsPath(entry.path) ? void 0 : safeImageUrl(entry.path);
452
477
  if (!src) {
453
478
  return /* @__PURE__ */ jsxs7("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
454
- /* @__PURE__ */ jsx11("div", { className: "font-medium", children: "Screenshot unavailable" }),
455
- /* @__PURE__ */ jsx11("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
456
- entry.alt ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
479
+ /* @__PURE__ */ jsx12("div", { className: "font-medium", children: "Screenshot unavailable" }),
480
+ /* @__PURE__ */ jsx12("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
481
+ entry.alt ? /* @__PURE__ */ jsx12("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
457
482
  ] });
458
483
  }
459
484
  return /* @__PURE__ */ jsxs7("figure", { className: "my-3", children: [
460
- /* @__PURE__ */ jsx11(
485
+ /* @__PURE__ */ jsx12(
461
486
  "img",
462
487
  {
463
488
  src,
@@ -466,24 +491,24 @@ function DocScreenshot({ entry }) {
466
491
  className: "max-w-full rounded-md border border-border"
467
492
  }
468
493
  ),
469
- entry.alt ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
494
+ entry.alt ? /* @__PURE__ */ jsx12("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
470
495
  ] });
471
496
  }
472
497
 
473
498
  // src/components/doc/DocVideo.tsx
474
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
499
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
475
500
  function DocVideo({ entry }) {
476
501
  if (isLocalFsPath(entry.path)) {
477
502
  return /* @__PURE__ */ jsxs8("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
478
- /* @__PURE__ */ jsx12("div", { className: "font-medium", children: "Video unavailable" }),
479
- /* @__PURE__ */ jsx12("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
480
- entry.caption ? /* @__PURE__ */ jsx12("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
503
+ /* @__PURE__ */ jsx13("div", { className: "font-medium", children: "Video unavailable" }),
504
+ /* @__PURE__ */ jsx13("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
505
+ entry.caption ? /* @__PURE__ */ jsx13("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
481
506
  ] });
482
507
  }
483
508
  const src = safeUrl(entry.path);
484
509
  const poster = safeUrl(entry.poster);
485
510
  return /* @__PURE__ */ jsxs8("figure", { className: "my-3", children: [
486
- /* @__PURE__ */ jsx12(
511
+ /* @__PURE__ */ jsx13(
487
512
  "video",
488
513
  {
489
514
  controls: true,
@@ -493,12 +518,12 @@ function DocVideo({ entry }) {
493
518
  className: "max-w-full rounded-md border border-border"
494
519
  }
495
520
  ),
496
- entry.caption ? /* @__PURE__ */ jsx12("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
521
+ entry.caption ? /* @__PURE__ */ jsx13("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
497
522
  ] });
498
523
  }
499
524
 
500
525
  // src/components/doc/DocHtml.tsx
501
- import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
526
+ import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
502
527
  function DocHtml({ entry }) {
503
528
  const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
504
529
  const title = entry.title ?? "Embedded HTML";
@@ -506,8 +531,8 @@ function DocHtml({ entry }) {
506
531
  const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
507
532
  return /* @__PURE__ */ jsxs9("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
508
533
  /* @__PURE__ */ jsxs9("figcaption", { className: "flex items-center justify-between border-b border-border bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground", children: [
509
- /* @__PURE__ */ jsx13("span", { children: entry.title ?? "HTML" }),
510
- safeHref ? /* @__PURE__ */ jsx13(
534
+ /* @__PURE__ */ jsx14("span", { children: entry.title ?? "HTML" }),
535
+ safeHref ? /* @__PURE__ */ jsx14(
511
536
  "a",
512
537
  {
513
538
  href: safeHref,
@@ -520,7 +545,7 @@ function DocHtml({ entry }) {
520
545
  }
521
546
  ) : null
522
547
  ] }),
523
- /* @__PURE__ */ jsx13(
548
+ /* @__PURE__ */ jsx14(
524
549
  "iframe",
525
550
  {
526
551
  ...frameProps,
@@ -535,7 +560,7 @@ function DocHtml({ entry }) {
535
560
  }
536
561
 
537
562
  // src/components/doc/DocState.tsx
538
- import { jsx as jsx14 } from "react/jsx-runtime";
563
+ import { jsx as jsx15 } from "react/jsx-runtime";
539
564
  function DocState({ entry }) {
540
565
  let json;
541
566
  try {
@@ -543,20 +568,20 @@ function DocState({ entry }) {
543
568
  } catch {
544
569
  json = String(entry.value);
545
570
  }
546
- return /* @__PURE__ */ jsx14(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ jsx14("code", { className: "font-mono text-xs language-json", children: json }) });
571
+ return /* @__PURE__ */ jsx15(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ jsx15("code", { className: "font-mono text-xs language-json", children: json }) });
547
572
  }
548
573
 
549
574
  // src/components/doc/DocCustom.tsx
550
- import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
575
+ import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
551
576
  function DocCustom({ entry }) {
552
577
  const renderers = useCustomRenderers();
553
578
  const renderer = renderers[entry.type];
554
579
  if (renderer) {
555
- return /* @__PURE__ */ jsx15(Fragment4, { children: renderer(entry) });
580
+ return /* @__PURE__ */ jsx16(Fragment4, { children: renderer(entry) });
556
581
  }
557
582
  return /* @__PURE__ */ jsxs10("div", { className: "my-2", "data-type": entry.type, children: [
558
- /* @__PURE__ */ jsx15("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
559
- /* @__PURE__ */ jsx15("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify2(entry.data) })
583
+ /* @__PURE__ */ jsx16("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
584
+ /* @__PURE__ */ jsx16("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify2(entry.data) })
560
585
  ] });
561
586
  }
562
587
  function safeStringify2(value) {
@@ -568,64 +593,43 @@ function safeStringify2(value) {
568
593
  }
569
594
 
570
595
  // src/components/doc/DocEntry.tsx
571
- import { jsx as jsx16 } from "react/jsx-runtime";
596
+ import { jsx as jsx17 } from "react/jsx-runtime";
572
597
  function DocEntry({ entry }) {
573
598
  switch (entry.kind) {
574
599
  case "note":
575
- return /* @__PURE__ */ jsx16(DocNote, { entry });
600
+ return /* @__PURE__ */ jsx17(DocNote, { entry });
576
601
  case "tag":
577
- return /* @__PURE__ */ jsx16(DocTag, { entry });
602
+ return /* @__PURE__ */ jsx17(DocTag, { entry });
578
603
  case "kv":
579
- return /* @__PURE__ */ jsx16(DocKv, { entry });
604
+ return /* @__PURE__ */ jsx17(DocKv, { entry });
580
605
  case "code":
581
- return /* @__PURE__ */ jsx16(DocCode, { entry });
606
+ return /* @__PURE__ */ jsx17(DocCode, { entry });
582
607
  case "table":
583
- return /* @__PURE__ */ jsx16(DocTable, { entry });
608
+ return /* @__PURE__ */ jsx17(DocTable, { entry });
584
609
  case "link":
585
- return /* @__PURE__ */ jsx16(DocLink, { entry });
610
+ return /* @__PURE__ */ jsx17(DocLink, { entry });
586
611
  case "section":
587
- return /* @__PURE__ */ jsx16(DocSection, { entry });
612
+ return /* @__PURE__ */ jsx17(DocSection, { entry });
588
613
  case "mermaid":
589
- return /* @__PURE__ */ jsx16(DocMermaid, { entry });
614
+ return /* @__PURE__ */ jsx17(DocMermaid, { entry });
590
615
  case "screenshot":
591
- return /* @__PURE__ */ jsx16(DocScreenshot, { entry });
616
+ return /* @__PURE__ */ jsx17(DocScreenshot, { entry });
592
617
  case "video":
593
- return /* @__PURE__ */ jsx16(DocVideo, { entry });
618
+ return /* @__PURE__ */ jsx17(DocVideo, { entry });
594
619
  case "html":
595
- return /* @__PURE__ */ jsx16(DocHtml, { entry });
620
+ return /* @__PURE__ */ jsx17(DocHtml, { entry });
596
621
  case "state":
597
- return /* @__PURE__ */ jsx16(DocState, { entry });
622
+ return /* @__PURE__ */ jsx17(DocState, { entry });
598
623
  case "custom":
599
- return /* @__PURE__ */ jsx16(DocCustom, { entry });
624
+ return /* @__PURE__ */ jsx17(DocCustom, { entry });
600
625
  }
601
626
  }
602
627
 
603
628
  // src/components/ReportDocEntries.tsx
604
- import { Fragment as Fragment5, jsx as jsx17 } from "react/jsx-runtime";
629
+ import { Fragment as Fragment5, jsx as jsx18 } from "react/jsx-runtime";
605
630
  function ReportDocEntries({ entries }) {
606
631
  if (entries.length === 0) return null;
607
- return /* @__PURE__ */ jsx17(Fragment5, { children: entries.map((entry, i) => /* @__PURE__ */ jsx17(DocEntry, { entry }, i)) });
608
- }
609
-
610
- // src/lib/step-params.tsx
611
- import { jsx as jsx18 } from "react/jsx-runtime";
612
- var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
613
- function highlightStepParams(text) {
614
- const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
615
- if (matches.length === 0) return text;
616
- const nodes = [];
617
- let lastIndex = 0;
618
- matches.forEach((match, i) => {
619
- const start = match.index;
620
- const end = start + match[0].length;
621
- if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
622
- nodes.push(
623
- /* @__PURE__ */ jsx18("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
624
- );
625
- lastIndex = end;
626
- });
627
- if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
628
- return nodes;
632
+ return /* @__PURE__ */ jsx18(Fragment5, { children: entries.map((entry, i) => /* @__PURE__ */ jsx18(DocEntry, { entry }, i)) });
629
633
  }
630
634
 
631
635
  // src/components/ReportSteps.tsx
@@ -644,9 +648,22 @@ var STEP_GLYPH_COLOR = {
644
648
  };
645
649
  function ReportSteps({ scenario }) {
646
650
  if (scenario.steps.length === 0) return null;
647
- return /* @__PURE__ */ jsx19("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ jsx19(ReportStepItem, { step }, step.id)) });
651
+ const unasserted = new Set(
652
+ assertionState(scenario.steps) === "unasserted" ? assertiveSteps(scenario.steps).map((step) => step.id) : []
653
+ );
654
+ return /* @__PURE__ */ jsx19("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ jsx19(
655
+ ReportStepItem,
656
+ {
657
+ step,
658
+ unasserted: unasserted.has(step.id)
659
+ },
660
+ step.id
661
+ )) });
648
662
  }
649
- function ReportStepItem({ step }) {
663
+ function ReportStepItem({
664
+ step,
665
+ unasserted = false
666
+ }) {
650
667
  const kw = step.keyword.toLowerCase();
651
668
  const continuation = kw === "and" || kw === "but";
652
669
  return /* @__PURE__ */ jsxs11(
@@ -654,10 +671,23 @@ function ReportStepItem({ step }) {
654
671
  {
655
672
  id: step.id,
656
673
  "data-status": step.status,
657
- className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
674
+ className: cn(
675
+ "py-1.5 text-[0.8125rem] leading-normal",
676
+ continuation && "pl-5"
677
+ ),
658
678
  children: [
659
679
  /* @__PURE__ */ jsxs11("div", { className: "flex items-baseline gap-2", children: [
660
- /* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn("w-4 shrink-0 text-center text-xs", STEP_GLYPH_COLOR[step.status]), children: STEP_GLYPH[step.status] ?? "\u25CB" }),
680
+ /* @__PURE__ */ jsx19(
681
+ "span",
682
+ {
683
+ "aria-hidden": true,
684
+ className: cn(
685
+ "w-4 shrink-0 text-center text-xs",
686
+ STEP_GLYPH_COLOR[step.status]
687
+ ),
688
+ children: STEP_GLYPH[step.status] ?? "\u25CB"
689
+ }
690
+ ),
661
691
  /* @__PURE__ */ jsx19(
662
692
  "span",
663
693
  {
@@ -669,6 +699,7 @@ function ReportStepItem({ step }) {
669
699
  }
670
700
  ),
671
701
  /* @__PURE__ */ jsx19("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
702
+ unasserted ? /* @__PURE__ */ jsx19("span", { className: "shrink-0 rounded-full border border-pend-border bg-pend-bg px-2 py-0.5 text-[0.6875rem] font-medium text-pend", children: "No assertion" }) : null,
672
703
  step.durationMs > 0 ? /* @__PURE__ */ jsx19("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
673
704
  ] }),
674
705
  step.errorMessage ? /* @__PURE__ */ jsx19(
@@ -1064,21 +1095,95 @@ function ScenarioRunHistory({ scenarioId }) {
1064
1095
  ] });
1065
1096
  }
1066
1097
 
1098
+ // src/components/ScenarioStaleness.tsx
1099
+ import { useEffect, useState } from "react";
1100
+
1101
+ // src/lib/provenance.ts
1102
+ function formatRelativeAge(thenMs, nowMs) {
1103
+ const deltaMs = Math.max(0, nowMs - thenMs);
1104
+ const minutes = Math.floor(deltaMs / 6e4);
1105
+ if (minutes < 1) return "just now";
1106
+ if (minutes < 60) return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
1107
+ const hours = Math.floor(minutes / 60);
1108
+ if (hours < 24) return `${hours} hour${hours === 1 ? "" : "s"} ago`;
1109
+ const days = Math.floor(hours / 24);
1110
+ return `${days} day${days === 1 ? "" : "s"} ago`;
1111
+ }
1112
+ function isReportStale(lastRunMs, staleAfterDays, nowMs) {
1113
+ if (lastRunMs === void 0 || staleAfterDays <= 0) return false;
1114
+ return nowMs - lastRunMs >= staleAfterDays * 864e5;
1115
+ }
1116
+ var CI_DISPLAY_NAMES = {
1117
+ github: "GitHub Actions",
1118
+ gitlab: "GitLab CI",
1119
+ circleci: "CircleCI",
1120
+ jenkins: "Jenkins",
1121
+ azure: "Azure DevOps",
1122
+ buildkite: "Buildkite",
1123
+ travis: "Travis CI",
1124
+ ci: "CI"
1125
+ };
1126
+ function ciDisplayName(name) {
1127
+ return CI_DISPLAY_NAMES[name] ?? name;
1128
+ }
1129
+ function commitUrl(ci, sha) {
1130
+ const commitSha = ci?.commitSha ?? sha;
1131
+ if (!ci?.url || !commitSha) return void 0;
1132
+ const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
1133
+ if (github) return `${github[1]}/commit/${commitSha}`;
1134
+ const gitlab = ci.url.match(/^(https?:\/\/.+)\/-\/pipelines\//);
1135
+ if (gitlab) return `${gitlab[1]}/-/commit/${commitSha}`;
1136
+ return void 0;
1137
+ }
1138
+ function prUrl(ci) {
1139
+ if (!ci?.url || !ci.prNumber) return void 0;
1140
+ const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
1141
+ if (github) return `${github[1]}/pull/${ci.prNumber}`;
1142
+ return void 0;
1143
+ }
1144
+
1145
+ // src/interactive/scenario-freshness-context.tsx
1146
+ import { createContext as createContext3, useContext as useContext4 } from "react";
1147
+ var ScenarioFreshnessContext = createContext3(null);
1148
+ var ScenarioFreshnessProvider = ScenarioFreshnessContext.Provider;
1149
+ function useScenarioFreshness() {
1150
+ return useContext4(ScenarioFreshnessContext);
1151
+ }
1152
+
1153
+ // src/components/ScenarioStaleness.tsx
1154
+ import { jsxs as jsxs16 } from "react/jsx-runtime";
1155
+ function ScenarioStaleness({ scenario }) {
1156
+ const freshness = useScenarioFreshness();
1157
+ const fixedNow = freshness?.nowMs;
1158
+ const [now, setNow] = useState(fixedNow);
1159
+ useEffect(() => {
1160
+ if (fixedNow === void 0) setNow(Date.now());
1161
+ }, [fixedNow]);
1162
+ if (!freshness || now === void 0) return null;
1163
+ const lastRunAtMs = scenario.lastRunAtMs;
1164
+ if (!lastRunAtMs || lastRunAtMs <= 0) return null;
1165
+ if (!isReportStale(lastRunAtMs, freshness.staleAfterDays, now)) return null;
1166
+ return /* @__PURE__ */ jsxs16(Badge, { variant: "outline", "aria-label": "Scenario freshness", children: [
1167
+ "Last ran ",
1168
+ formatRelativeAge(lastRunAtMs, now)
1169
+ ] });
1170
+ }
1171
+
1067
1172
  // src/interactive/collapse-context.ts
1068
- import { createContext as createContext3, useCallback, useContext as useContext4, useState } from "react";
1069
- var CollapseContext = createContext3(null);
1173
+ import { createContext as createContext4, useCallback, useContext as useContext5, useState as useState2 } from "react";
1174
+ var CollapseContext = createContext4(null);
1070
1175
  var CollapseProvider = CollapseContext.Provider;
1071
1176
  function useCollapse() {
1072
- return useContext4(CollapseContext);
1177
+ return useContext5(CollapseContext);
1073
1178
  }
1074
1179
 
1075
1180
  // src/interactive/scenario-actions.tsx
1076
- import { createContext as createContext4, useContext as useContext5 } from "react";
1181
+ import { createContext as createContext5, useContext as useContext6 } from "react";
1077
1182
  import { scenarioToMarkdown as coreScenarioToMarkdown } from "executable-stories-core/utils/scenario-markdown";
1078
- var ScenarioActionsContext = createContext4(null);
1183
+ var ScenarioActionsContext = createContext5(null);
1079
1184
  var ScenarioActionsProvider = ScenarioActionsContext.Provider;
1080
1185
  function useScenarioActions() {
1081
- return useContext5(ScenarioActionsContext);
1186
+ return useContext6(ScenarioActionsContext);
1082
1187
  }
1083
1188
 
1084
1189
  // src/components/ui/card.tsx
@@ -1125,7 +1230,7 @@ function CardContent({ className, ...props }) {
1125
1230
  import "react";
1126
1231
  import { Menu as MenuPrimitive } from "@base-ui/react/menu";
1127
1232
  import { CheckIcon } from "lucide-react";
1128
- import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
1233
+ import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
1129
1234
  var itemClasses = "relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_svg:not([class*='text-'])]:text-muted-foreground";
1130
1235
  function DropdownMenu(props) {
1131
1236
  return /* @__PURE__ */ jsx25(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
@@ -1176,7 +1281,7 @@ function DropdownMenuItem({
1176
1281
 
1177
1282
  // src/components/ReportScenario.tsx
1178
1283
  import { LinkIcon, FileTextIcon, SparklesIcon, MoreHorizontalIcon } from "lucide-react";
1179
- import { jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
1284
+ import { jsx as jsx26, jsxs as jsxs18 } from "react/jsx-runtime";
1180
1285
  var STATUS_META = {
1181
1286
  passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
1182
1287
  failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
@@ -1192,7 +1297,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
1192
1297
  const collapsible = collapse !== null && !hideTitle;
1193
1298
  const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
1194
1299
  const actions = useScenarioActions();
1195
- return /* @__PURE__ */ jsxs17(
1300
+ return /* @__PURE__ */ jsxs18(
1196
1301
  Card,
1197
1302
  {
1198
1303
  id: scenario.id,
@@ -1200,9 +1305,9 @@ function ReportScenario({ scenario, hideTitle = false }) {
1200
1305
  ...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
1201
1306
  className: "gap-0 py-0",
1202
1307
  children: [
1203
- /* @__PURE__ */ jsxs17(CardHeader, { className: "px-4 py-3", children: [
1204
- /* @__PURE__ */ jsxs17("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1205
- hideTitle ? null : /* @__PURE__ */ jsxs17("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1308
+ /* @__PURE__ */ jsxs18(CardHeader, { className: "px-4 py-3", children: [
1309
+ /* @__PURE__ */ jsxs18("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1310
+ hideTitle ? null : /* @__PURE__ */ jsxs18("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1206
1311
  collapsible ? /* @__PURE__ */ jsx26(
1207
1312
  "button",
1208
1313
  {
@@ -1218,11 +1323,12 @@ function ReportScenario({ scenario, hideTitle = false }) {
1218
1323
  /* @__PURE__ */ jsx26("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1219
1324
  /* @__PURE__ */ jsx26("span", { children: scenario.title })
1220
1325
  ] }),
1221
- /* @__PURE__ */ jsxs17("div", { className: "flex shrink-0 items-center gap-2", children: [
1326
+ /* @__PURE__ */ jsxs18("div", { className: "flex shrink-0 items-center gap-2", children: [
1222
1327
  /* @__PURE__ */ jsx26(ScenarioRunHistory, { scenarioId: scenario.id }),
1328
+ /* @__PURE__ */ jsx26(ScenarioStaleness, { scenario }),
1223
1329
  scenario.durationMs > 0 ? /* @__PURE__ */ jsx26("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration3(scenario.durationMs) }) : null,
1224
1330
  meta.showPill ? /* @__PURE__ */ jsx26(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ jsx26("span", { className: "sr-only", children: `Status: ${label}` }),
1225
- actions ? /* @__PURE__ */ jsxs17(DropdownMenu, { children: [
1331
+ actions ? /* @__PURE__ */ jsxs18(DropdownMenu, { children: [
1226
1332
  /* @__PURE__ */ jsx26(
1227
1333
  DropdownMenuTrigger,
1228
1334
  {
@@ -1231,16 +1337,16 @@ function ReportScenario({ scenario, hideTitle = false }) {
1231
1337
  children: /* @__PURE__ */ jsx26(MoreHorizontalIcon, { "aria-hidden": true })
1232
1338
  }
1233
1339
  ),
1234
- /* @__PURE__ */ jsxs17(DropdownMenuContent, { align: "end", children: [
1235
- /* @__PURE__ */ jsxs17(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1340
+ /* @__PURE__ */ jsxs18(DropdownMenuContent, { align: "end", children: [
1341
+ /* @__PURE__ */ jsxs18(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1236
1342
  /* @__PURE__ */ jsx26(LinkIcon, {}),
1237
1343
  " Copy link"
1238
1344
  ] }),
1239
- /* @__PURE__ */ jsxs17(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1345
+ /* @__PURE__ */ jsxs18(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1240
1346
  /* @__PURE__ */ jsx26(FileTextIcon, {}),
1241
1347
  " Copy as Markdown"
1242
1348
  ] }),
1243
- /* @__PURE__ */ jsxs17(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1349
+ /* @__PURE__ */ jsxs18(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1244
1350
  /* @__PURE__ */ jsx26(SparklesIcon, {}),
1245
1351
  " Explain with AI"
1246
1352
  ] })
@@ -1248,12 +1354,12 @@ function ReportScenario({ scenario, hideTitle = false }) {
1248
1354
  ] }) : null
1249
1355
  ] })
1250
1356
  ] }),
1251
- scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs17("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1357
+ scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs18("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1252
1358
  scenario.tags.map((t) => /* @__PURE__ */ jsx26("li", { children: /* @__PURE__ */ jsx26(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1253
1359
  (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ jsx26("li", { children: ticket.url ? /* @__PURE__ */ jsx26("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ jsx26(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ jsx26(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1254
1360
  ] }) : null
1255
1361
  ] }),
1256
- /* @__PURE__ */ jsxs17(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1362
+ /* @__PURE__ */ jsxs18(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1257
1363
  scenario.errorMessage ? /* @__PURE__ */ jsx26(
1258
1364
  "pre",
1259
1365
  {
@@ -1280,12 +1386,12 @@ function ReportScenarioList({ feature }) {
1280
1386
  }
1281
1387
 
1282
1388
  // src/components/ReportFeatureIntro.tsx
1283
- import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
1389
+ import { jsx as jsx28, jsxs as jsxs19 } from "react/jsx-runtime";
1284
1390
  function ReportFeatureIntro({ feature }) {
1285
1391
  const hasNarrative = Boolean(feature.narrative?.trim());
1286
1392
  const hasGlossary = Boolean(feature.glossary?.length);
1287
1393
  if (!hasNarrative && !hasGlossary) return null;
1288
- return /* @__PURE__ */ jsxs18("div", { className: "flex flex-col gap-3 border-l-2 border-border pl-4 text-sm", children: [
1394
+ return /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-3 border-l-2 border-border pl-4 text-sm", children: [
1289
1395
  hasNarrative ? /* @__PURE__ */ jsx28(
1290
1396
  "div",
1291
1397
  {
@@ -1293,7 +1399,7 @@ function ReportFeatureIntro({ feature }) {
1293
1399
  dangerouslySetInnerHTML: { __html: safeMarkdownHtml(dedent(feature.narrative)) }
1294
1400
  }
1295
1401
  ) : null,
1296
- hasGlossary ? /* @__PURE__ */ jsx28("dl", { className: "grid gap-1.5", "aria-label": "Glossary", children: feature.glossary.map((entry) => /* @__PURE__ */ jsxs18("div", { className: "flex flex-wrap gap-x-2 text-xs", children: [
1402
+ hasGlossary ? /* @__PURE__ */ jsx28("dl", { className: "grid gap-1.5", "aria-label": "Glossary", children: feature.glossary.map((entry) => /* @__PURE__ */ jsxs19("div", { className: "flex flex-wrap gap-x-2 text-xs", children: [
1297
1403
  /* @__PURE__ */ jsx28("dt", { className: "font-medium text-foreground", children: entry.term }),
1298
1404
  /* @__PURE__ */ jsx28("dd", { className: "flex-1 text-muted-foreground", children: entry.definition })
1299
1405
  ] }, entry.term)) }) : null
@@ -1307,7 +1413,7 @@ function featureKindLabel(kind) {
1307
1413
  }
1308
1414
 
1309
1415
  // src/components/ReportFeature.tsx
1310
- import { Fragment as Fragment9, jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
1416
+ import { Fragment as Fragment9, jsx as jsx29, jsxs as jsxs20 } from "react/jsx-runtime";
1311
1417
  function ReportFeature({ feature }) {
1312
1418
  const titleId = `${feature.id}-title`;
1313
1419
  const bodyId = `${feature.id}-body`;
@@ -1319,7 +1425,7 @@ function ReportFeature({ feature }) {
1319
1425
  return (
1320
1426
  // Suite = a section heading + a divider, NOT another bordered card. Drops a
1321
1427
  // layer of nesting so the scenario cards below read as the primary unit.
1322
- /* @__PURE__ */ jsxs19(
1428
+ /* @__PURE__ */ jsxs20(
1323
1429
  "section",
1324
1430
  {
1325
1431
  id: feature.id,
@@ -1327,9 +1433,9 @@ function ReportFeature({ feature }) {
1327
1433
  "aria-labelledby": titleId,
1328
1434
  className: "flex flex-col gap-3",
1329
1435
  children: [
1330
- /* @__PURE__ */ jsxs19("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1331
- /* @__PURE__ */ jsxs19("div", { className: "min-w-0", children: [
1332
- /* @__PURE__ */ jsx29("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs19(
1436
+ /* @__PURE__ */ jsxs20("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1437
+ /* @__PURE__ */ jsxs20("div", { className: "min-w-0", children: [
1438
+ /* @__PURE__ */ jsx29("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs20(
1333
1439
  "button",
1334
1440
  {
1335
1441
  type: "button",
@@ -1343,29 +1449,29 @@ function ReportFeature({ feature }) {
1343
1449
  ]
1344
1450
  }
1345
1451
  ) : feature.title }),
1346
- /* @__PURE__ */ jsxs19("p", { className: cn("mt-0.5 flex items-center gap-2 text-xs text-muted-foreground", collapse && "pl-5"), children: [
1452
+ /* @__PURE__ */ jsxs20("p", { className: cn("mt-0.5 flex items-center gap-2 text-xs text-muted-foreground", collapse && "pl-5"), children: [
1347
1453
  kindLabel ? /* @__PURE__ */ jsx29("span", { className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-medium text-foreground", children: kindLabel }) : null,
1348
1454
  /* @__PURE__ */ jsx29("span", { className: "truncate font-mono", children: feature.sourceFile })
1349
1455
  ] })
1350
1456
  ] }),
1351
- /* @__PURE__ */ jsxs19(
1457
+ /* @__PURE__ */ jsxs20(
1352
1458
  "p",
1353
1459
  {
1354
1460
  className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
1355
1461
  "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1356
1462
  children: [
1357
- /* @__PURE__ */ jsxs19("span", { children: [
1463
+ /* @__PURE__ */ jsxs20("span", { children: [
1358
1464
  s.passed,
1359
1465
  " passed"
1360
1466
  ] }),
1361
1467
  /* @__PURE__ */ jsx29("span", { "aria-hidden": true, children: "\xB7" }),
1362
- /* @__PURE__ */ jsxs19("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1468
+ /* @__PURE__ */ jsxs20("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1363
1469
  s.failed,
1364
1470
  " failed"
1365
1471
  ] }),
1366
- skipped > 0 ? /* @__PURE__ */ jsxs19(Fragment9, { children: [
1472
+ skipped > 0 ? /* @__PURE__ */ jsxs20(Fragment9, { children: [
1367
1473
  /* @__PURE__ */ jsx29("span", { "aria-hidden": true, children: "\xB7" }),
1368
- /* @__PURE__ */ jsxs19("span", { children: [
1474
+ /* @__PURE__ */ jsxs20("span", { children: [
1369
1475
  skipped,
1370
1476
  " skipped"
1371
1477
  ] })
@@ -1374,7 +1480,7 @@ function ReportFeature({ feature }) {
1374
1480
  }
1375
1481
  )
1376
1482
  ] }),
1377
- /* @__PURE__ */ jsxs19("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: [
1483
+ /* @__PURE__ */ jsxs20("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: [
1378
1484
  /* @__PURE__ */ jsx29(ReportFeatureIntro, { feature }),
1379
1485
  /* @__PURE__ */ jsx29(ReportScenarioList, { feature })
1380
1486
  ] })
@@ -1452,7 +1558,7 @@ function ReportEmpty({ message }) {
1452
1558
  }
1453
1559
 
1454
1560
  // src/components/ReportSummary.tsx
1455
- import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
1561
+ import { jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
1456
1562
  function ReportSummary({ className }) {
1457
1563
  const report = useReport();
1458
1564
  return /* @__PURE__ */ jsx33(
@@ -1494,13 +1600,13 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1494
1600
  "aria-label": ariaLabel,
1495
1601
  children: cards.map((c) => {
1496
1602
  const tone = TONE[c.status];
1497
- return /* @__PURE__ */ jsxs20(
1603
+ return /* @__PURE__ */ jsxs21(
1498
1604
  "div",
1499
1605
  {
1500
1606
  "data-status": c.status,
1501
1607
  className: "rounded-lg border bg-card px-4 py-3",
1502
1608
  children: [
1503
- /* @__PURE__ */ jsxs20("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1609
+ /* @__PURE__ */ jsxs21("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1504
1610
  /* @__PURE__ */ jsx33("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1505
1611
  c.label
1506
1612
  ] }),
@@ -1516,39 +1622,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1516
1622
 
1517
1623
  // src/components/ReportMeta.tsx
1518
1624
  import { formatDuration as formatDuration4 } from "executable-stories-core/utils/duration";
1519
-
1520
- // src/lib/provenance.ts
1521
- var CI_DISPLAY_NAMES = {
1522
- github: "GitHub Actions",
1523
- gitlab: "GitLab CI",
1524
- circleci: "CircleCI",
1525
- jenkins: "Jenkins",
1526
- azure: "Azure DevOps",
1527
- buildkite: "Buildkite",
1528
- travis: "Travis CI",
1529
- ci: "CI"
1530
- };
1531
- function ciDisplayName(name) {
1532
- return CI_DISPLAY_NAMES[name] ?? name;
1533
- }
1534
- function commitUrl(ci, sha) {
1535
- const commitSha = ci?.commitSha ?? sha;
1536
- if (!ci?.url || !commitSha) return void 0;
1537
- const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
1538
- if (github) return `${github[1]}/commit/${commitSha}`;
1539
- const gitlab = ci.url.match(/^(https?:\/\/.+)\/-\/pipelines\//);
1540
- if (gitlab) return `${gitlab[1]}/-/commit/${commitSha}`;
1541
- return void 0;
1542
- }
1543
- function prUrl(ci) {
1544
- if (!ci?.url || !ci.prNumber) return void 0;
1545
- const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
1546
- if (github) return `${github[1]}/pull/${ci.prNumber}`;
1547
- return void 0;
1548
- }
1549
-
1550
- // src/components/ReportMeta.tsx
1551
- import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
1625
+ import { jsx as jsx34, jsxs as jsxs22 } from "react/jsx-runtime";
1552
1626
  function ReportMeta() {
1553
1627
  const report = useReport();
1554
1628
  const items = [];
@@ -1577,7 +1651,7 @@ function ReportMeta() {
1577
1651
  if (report.ci) {
1578
1652
  items.push({
1579
1653
  label: "CI",
1580
- value: report.ci.url ? /* @__PURE__ */ jsxs21("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1654
+ value: report.ci.url ? /* @__PURE__ */ jsxs22("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1581
1655
  ciDisplayName(report.ci.name),
1582
1656
  report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
1583
1657
  ] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
@@ -1596,12 +1670,12 @@ function ReportMeta() {
1596
1670
  // Run metadata is useful but secondary to "what failed" — tuck it into a
1597
1671
  // collapsed disclosure so it doesn't push the result down. Native <details>
1598
1672
  // so it works with no JS (the SSR/no-hydration report still toggles).
1599
- /* @__PURE__ */ jsxs21("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1600
- /* @__PURE__ */ jsxs21("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
1673
+ /* @__PURE__ */ jsxs22("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1674
+ /* @__PURE__ */ jsxs22("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
1601
1675
  /* @__PURE__ */ jsx34("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1602
1676
  "Run details"
1603
1677
  ] }),
1604
- /* @__PURE__ */ jsx34("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ jsxs21("div", { className: "flex items-baseline gap-1.5", children: [
1678
+ /* @__PURE__ */ jsx34("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ jsxs22("div", { className: "flex items-baseline gap-1.5", children: [
1605
1679
  /* @__PURE__ */ jsx34("dt", { className: "font-medium", children: item.label }),
1606
1680
  /* @__PURE__ */ jsx34("dd", { className: "font-mono text-foreground", children: item.value })
1607
1681
  ] }, item.label)) })
@@ -1610,18 +1684,18 @@ function ReportMeta() {
1610
1684
  }
1611
1685
 
1612
1686
  // src/components/ReportSchemaError.tsx
1613
- import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
1687
+ import { jsx as jsx35, jsxs as jsxs23 } from "react/jsx-runtime";
1614
1688
  function ReportSchemaError({ error }) {
1615
- return /* @__PURE__ */ jsxs22("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1689
+ return /* @__PURE__ */ jsxs23("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1616
1690
  /* @__PURE__ */ jsx35("p", { children: /* @__PURE__ */ jsx35("strong", { children: "Report could not be displayed." }) }),
1617
1691
  /* @__PURE__ */ jsx35("p", { children: error.message }),
1618
- error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs22("p", { children: [
1692
+ error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs23("p", { children: [
1619
1693
  "The report bundle is newer than this version of ",
1620
1694
  /* @__PURE__ */ jsx35("code", { children: "executable-stories-react" }),
1621
1695
  ". Upgrade the package, or regenerate the report with an older formatters CLI."
1622
1696
  ] }) : null,
1623
- error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs22("details", { children: [
1624
- /* @__PURE__ */ jsxs22("summary", { children: [
1697
+ error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs23("details", { children: [
1698
+ /* @__PURE__ */ jsxs23("summary", { children: [
1625
1699
  error.issues.length,
1626
1700
  " validation issue",
1627
1701
  error.issues.length === 1 ? "" : "s"
@@ -1632,9 +1706,9 @@ function ReportSchemaError({ error }) {
1632
1706
  }
1633
1707
 
1634
1708
  // src/components/ReportShell.tsx
1635
- import { Fragment as Fragment10, jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
1709
+ import { Fragment as Fragment10, jsx as jsx36, jsxs as jsxs24 } from "react/jsx-runtime";
1636
1710
  function ReportTitleBlock({ title }) {
1637
- return /* @__PURE__ */ jsxs23(Fragment10, { children: [
1711
+ return /* @__PURE__ */ jsxs24(Fragment10, { children: [
1638
1712
  /* @__PURE__ */ jsx36("h1", { children: title ?? "Story Report" }),
1639
1713
  /* @__PURE__ */ jsx36(ReportSummary, {}),
1640
1714
  /* @__PURE__ */ jsx36(ReportMeta, {})
@@ -1650,7 +1724,7 @@ function ReportErrorShell({
1650
1724
  }
1651
1725
 
1652
1726
  // src/components/Report.tsx
1653
- import { jsx as jsx37, jsxs as jsxs24 } from "react/jsx-runtime";
1727
+ import { jsx as jsx37, jsxs as jsxs25 } from "react/jsx-runtime";
1654
1728
  function Report(props) {
1655
1729
  const { report, customRenderers, renderers, className, title, dataTheme } = props;
1656
1730
  const result = unwrapReport(report);
@@ -1659,7 +1733,7 @@ function Report(props) {
1659
1733
  }
1660
1734
  const data = result.data;
1661
1735
  const hasContent = data.features.length > 0;
1662
- return /* @__PURE__ */ jsx37(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ jsxs24("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
1736
+ return /* @__PURE__ */ jsx37(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ jsxs25("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
1663
1737
  /* @__PURE__ */ jsx37("header", { className: "es-report-header", children: /* @__PURE__ */ jsx37(ReportTitleBlock, { title }) }),
1664
1738
  hasContent ? /* @__PURE__ */ jsx37(ReportFeatureList, {}) : /* @__PURE__ */ jsx37(ReportEmpty, {})
1665
1739
  ] }) });