{"version":3,"sources":["../src/setup.ts","../src/story-api.ts"],"sourcesContent":["/**\n * Jest setup file for executable-stories.\n *\n * Add this to your Jest config's setupFilesAfterEnv:\n *\n * @example\n * ```js\n * // jest.config.js\n * export default {\n *   setupFilesAfterEnv: ['executable-stories-jest/setup'],\n *   reporters: [\n *     'default',\n *     ['executable-stories-jest/reporter', {\n *       formats: ['markdown', 'html'],\n *       outputDir: 'docs',\n *       outputName: 'user-stories',\n *     }],\n *   ],\n * };\n * ```\n */\n\nimport { afterAll } from \"@jest/globals\";\nimport { _internal } from \"./story-api\";\n\n// Register afterAll hook to flush stories at the file level\nafterAll(() => {\n  _internal.flushStories();\n});\n","/**\n * Jest story.* API for executable-stories.\n *\n * Uses native Jest describe/it/test with opt-in documentation:\n *\n * @example\n * ```ts\n * import { story } from 'executable-stories-jest';\n *\n * describe('Calculator', () => {\n *   it('adds two numbers', () => {\n *     story.init();\n *\n *     story.given('two numbers 5 and 3');\n *     const a = 5;\n *     const b = 3;\n *\n *     story.when('I add them together');\n *     const result = a + b;\n *\n *     story.then('the result is 8');\n *     expect(result).toBe(8);\n *   });\n * });\n * ```\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { createRequire } from 'node:module';\nimport { tryGetActiveOtelContext, resolveTraceUrl } from 'executable-stories-core/utils/otel-detect';\nimport { buildHtmlDocEntry } from 'executable-stories-core/utils/doc-builders';\nimport type {\n  DocEntry,\n  FeatureInput,\n  NormalizedTicket,\n  StepKeyword,\n  StoryDocs,\n  StoryMeta,\n  StoryOptions,\n  StoryStep,\n  ScopedAttachment,\n  AttachmentOptions,\n  TicketInput,\n  KvOptions,\n  JsonOptions,\n  StateOptions,\n  CodeOptions,\n  TableOptions,\n  LinkOptions,\n  SectionOptions,\n  MermaidOptions,\n  ScreenshotOptions,\n  VideoOptions,\n  HtmlOptions,\n  CustomOptions,\n} from './types';\n\n// Re-export types for consumers\nexport type {\n  StoryMeta,\n  StoryStep,\n  DocEntry,\n  StepKeyword,\n  StoryDocs,\n  StoryOptions,\n  AttachmentOptions,\n  TicketInput,\n  NormalizedTicket,\n} from './types';\n\n// ============================================================================\n// Story Context\n// ============================================================================\n\n/** Internal timer entry */\ninterface TimerEntry {\n  start: number;\n  stepIndex?: number;\n  stepId?: string;\n  consumed: boolean;\n}\n\ninterface StoryContext {\n  /** The story metadata being built */\n  meta: StoryMeta;\n  /** The current step (for attaching docs) */\n  currentStep: StoryStep | null;\n  /** Deterministic step counter (resets per test case) */\n  stepCounter: number;\n  /** Collected attachments with step scope */\n  attachments: ScopedAttachment[];\n  /** Active timers keyed by token */\n  activeTimers: Map<number, TimerEntry>;\n  /** Monotonic timer token counter */\n  timerCounter: number;\n  /** Test file path for registry lookups */\n  testPath: string;\n  /** Index into the storyRegistry array for this scenario */\n  scenarioIndex: number;\n  /**\n   * Marker step awaiting its assertion count.\n   *\n   * A marker states the claim and the assertion follows it, so the count is\n   * only known once the next step starts or the test ends.\n   */\n  pendingStep: StoryStep | null;\n  /** Assertion counter reading taken when {@link pendingStep} was created. */\n  pendingFrom?: number;\n}\n\n/**\n * Jest's live per-test assertion counter, or undefined when it cannot be read.\n *\n * Undefined is not zero: it means we could not observe, and the report must not\n * report an unobserved step as one that asserted nothing.\n */\nfunction readAssertionCount(): number | undefined {\n  try {\n    const state = (expect as unknown as {\n      getState?: () => { assertionCalls?: number };\n    }).getState?.();\n    return typeof state?.assertionCalls === \"number\" ? state.assertionCalls : undefined;\n  } catch {\n    return undefined;\n  }\n}\n\n/** Record how many assertions ran between `before` and now. */\nfunction recordAssertions(step: StoryStep, before: number | undefined): void {\n  if (before === undefined) return;\n  const after = readAssertionCount();\n  if (after === undefined || after < before) return;\n  step.assertions = after - before;\n}\n\n/**\n * Flush at the end of every test, registered from this module.\n *\n * tsup bundles a separate copy of this file into `dist/setup.js`, so a hook\n * registered from the setup entry would flush a different module instance with\n * its own empty context. Registering here keeps the hook and the state\n * together. Runs at import time, which in a Jest test file is inside the root\n * describe scope.\n */\ntry {\n  // The global, not a static @jest/globals import: this module is also bundled\n  // into the reporter entry, which runs outside any test context.\n  const registerAfterEach = (globalThis as {\n    afterEach?: (fn: () => void) => void;\n  }).afterEach;\n  registerAfterEach?.(() => {\n    flushPendingAssertions();\n  });\n} catch {\n  // Imported outside a Jest test context (the reporter, tooling): nothing to\n  // register against, and nothing to flush.\n}\n\n/**\n * Close off the marker step waiting on a count.\n *\n * Called from the `afterEach` registered above, so the last marker in a test\n * still collects the assertions written after it.\n */\nfunction flushPendingAssertions(): void {\n  const ctx = activeContext;\n  if (!ctx) return;\n  const step = ctx.pendingStep;\n  const before = ctx.pendingFrom;\n  ctx.pendingStep = null;\n  ctx.pendingFrom = undefined;\n  if (!step) return;\n  recordAssertions(step, before);\n}\n\n// ============================================================================\n// File-based story collection (works across Jest worker processes)\n// ============================================================================\n\n// Use globalThis to ensure the registry is shared across module instances\n// This is needed because Jest may load the setup file and test files as separate module instances\ndeclare global {\n  // eslint-disable-next-line no-var\n  var __jestExecutableStoriesRegistry: Map<string, StoryMeta[]> | undefined;\n  // eslint-disable-next-line no-var\n  var __jestExecutableStoriesExitHandler: boolean | undefined;\n}\n\n/** Feature declarations, keyed by test file path */\nconst featureRegistry = new Map<string, FeatureInput>();\n\n/** Stories collected during test execution, keyed by test file path */\nconst storyRegistry: Map<string, StoryMeta[]> = globalThis.__jestExecutableStoriesRegistry ??= new Map();\n\n/** Attachments collected per story, keyed by test file path → scenario index → attachments */\nconst attachmentRegistry = new Map<string, Map<number, ScopedAttachment[]>>();\n\n/** OTel spans collected per story, keyed by test file path → scenario index → spans */\nconst otelSpansRegistry = new Map<string, Map<number, ReadonlyArray<Record<string, unknown>>>>();\n\n/** Track if we've registered the process exit handler */\nlet exitHandlerRegistered = globalThis.__jestExecutableStoriesExitHandler ?? false;\n\n/** Get the output directory for story JSON files */\nfunction getOutputDir(): string {\n  const baseDir = process.env.JEST_STORY_DOCS_DIR ?? \".jest-executable-stories\";\n  return path.resolve(process.cwd(), baseDir);\n}\n\n/** Flush all collected stories to JSON files */\nfunction flushStories(): void {\n  if (storyRegistry.size === 0) return;\n\n  const workerId = process.env.JEST_WORKER_ID ?? \"0\";\n  const outputDir = path.join(getOutputDir(), `worker-${workerId}`);\n  fs.mkdirSync(outputDir, { recursive: true });\n\n  for (const [testFilePath, scenarios] of storyRegistry) {\n    if (!scenarios.length) continue;\n    const hash = createHash(\"sha1\").update(testFilePath).digest(\"hex\").slice(0, 12);\n    const baseName = testFilePath === \"unknown\" ? \"unknown\" : path.basename(testFilePath);\n    const outFile = path.join(outputDir, `${baseName}.${hash}.json`);\n\n    // Include attachments and otelSpans per scenario (keyed by index, not name)\n    const fileAttachments = attachmentRegistry.get(testFilePath);\n    const fileOtelSpans = otelSpansRegistry.get(testFilePath);\n    const scenariosWithAttachments = scenarios.map((s, i) => ({\n      ...s,\n      _attachments: fileAttachments?.get(i) ?? [],\n      ...(fileOtelSpans?.get(i) ? { _otelSpans: fileOtelSpans.get(i) } : {}),\n    }));\n\n    const declared = featureRegistry.get(testFilePath);\n    const payload = {\n      testFilePath,\n      ...(declared ? { feature: declared } : {}),\n      scenarios: scenariosWithAttachments,\n    };\n    fs.writeFileSync(outFile, JSON.stringify(payload, null, 2) + \"\\n\", \"utf8\");\n  }\n  storyRegistry.clear();\n  attachmentRegistry.clear();\n  otelSpansRegistry.clear();\n  featureRegistry.clear();\n}\n\n/** Register process exit handler to flush stories (once per worker) */\nfunction registerExitHandler(): void {\n  if (exitHandlerRegistered) return;\n  exitHandlerRegistered = true;\n  globalThis.__jestExecutableStoriesExitHandler = true;\n  // Use 'exit' event - always fired when Node.js is about to exit\n  // Note: Only sync operations work here, which is fine for fs.writeFileSync\n  process.on(\"exit\", () => {\n    flushStories();\n  });\n}\n\n// ============================================================================\n// Jest-specific context\n// ============================================================================\n\n/** Active story context - set by story.init() */\nlet activeContext: StoryContext | null = null;\n\n/** Counter to track source order of stories (increments on each story.init call) */\nlet sourceOrderCounter = 0;\n\n/**\n * Get the current story context. Throws if story.init() wasn't called.\n */\nfunction getContext(): StoryContext {\n  if (!activeContext) {\n    throw new Error(\n      \"story.init() must be called first. Use: it('name', () => { story.init(); ... });\"\n    );\n  }\n  return activeContext;\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Normalize ticket option to array of NormalizedTicket objects.\n */\nfunction normalizeTickets(\n  ticket: TicketInput | TicketInput[] | undefined,\n): NormalizedTicket[] | undefined {\n  if (!ticket) return undefined;\n  const arr = Array.isArray(ticket) ? ticket : [ticket];\n  return arr.map((t) => (typeof t === 'string' ? { id: t } : t));\n}\n\n/**\n * Extract the suite path from Jest's currentTestName.\n * Jest's currentTestName is formatted as: \"describe1 > describe2 > test name\"\n */\nfunction extractSuitePath(currentTestName: string): { suitePath?: string[]; testName: string } {\n  const parts = currentTestName.split(\" > \");\n  if (parts.length <= 1) {\n    return { testName: currentTestName };\n  }\n  const testName = parts[parts.length - 1];\n  const suitePath = parts.slice(0, -1);\n  return { suitePath, testName };\n}\n\n/**\n * Convert StoryDocs inline options to DocEntry array.\n */\nfunction convertStoryDocsToEntries(docs: StoryDocs): DocEntry[] {\n  const entries: DocEntry[] = [];\n\n  if (docs.note) {\n    entries.push({ kind: \"note\", text: docs.note, phase: \"runtime\" });\n  }\n  if (docs.tag) {\n    const names = Array.isArray(docs.tag) ? docs.tag : [docs.tag];\n    entries.push({ kind: \"tag\", names, phase: \"runtime\" });\n  }\n  if (docs.kv) {\n    for (const [label, value] of Object.entries(docs.kv)) {\n      entries.push({ kind: \"kv\", label, value, phase: \"runtime\" });\n    }\n  }\n  if (docs.code) {\n    entries.push({ kind: \"code\", label: docs.code.label, content: docs.code.content, lang: docs.code.lang, phase: \"runtime\" });\n  }\n  if (docs.json) {\n    entries.push({ kind: \"code\", label: docs.json.label, content: JSON.stringify(docs.json.value, null, 2), lang: \"json\", phase: \"runtime\" });\n  }\n  if (docs.state) {\n    warnIfLargeState(docs.state.label, docs.state.value);\n    entries.push({ kind: \"state\", label: docs.state.label, value: docs.state.value, phase: \"runtime\" });\n  }\n  if (docs.table) {\n    entries.push({ kind: \"table\", label: docs.table.label, columns: docs.table.columns, rows: docs.table.rows, phase: \"runtime\" });\n  }\n  if (docs.link) {\n    entries.push({ kind: \"link\", label: docs.link.label, url: docs.link.url, phase: \"runtime\" });\n  }\n  if (docs.section) {\n    entries.push({ kind: \"section\", title: docs.section.title, markdown: docs.section.markdown, phase: \"runtime\" });\n  }\n  if (docs.mermaid) {\n    entries.push({ kind: \"mermaid\", code: docs.mermaid.code, title: docs.mermaid.title, phase: \"runtime\" });\n  }\n  if (docs.screenshot) {\n    entries.push({ kind: \"screenshot\", path: docs.screenshot.path, alt: docs.screenshot.alt, phase: \"runtime\" });\n  }\n  if (docs.video) {\n    entries.push({ kind: \"video\", path: docs.video.path, caption: docs.video.caption, poster: docs.video.poster, phase: \"runtime\" });\n  }\n  if (docs.html) {\n    entries.push(buildHtmlDocEntry(docs.html));\n  }\n  if (docs.custom) {\n    entries.push({ kind: \"custom\", type: docs.custom.type, data: docs.custom.data, phase: \"runtime\" });\n  }\n\n  return entries;\n}\n\n/** Warn (non-fatal) when a state snapshot is suspiciously large. */\nfunction warnIfLargeState(label: string | undefined, value: unknown): void {\n  try {\n    const length = JSON.stringify(value)?.length ?? 0;\n    if (length > 100_000) {\n      console.warn(\n        `[executable-stories] state \"${label ?? 'state'}\" is ${Math.round(length / 1024)}KB — consider capturing a projection of the entity instead of the whole thing`,\n      );\n    }\n  } catch {\n    /* non-serializable value; still recorded */\n  }\n}\n\n// ============================================================================\n// Helper to attach doc entry to current step or story-level\n// ============================================================================\n\nfunction attachDoc(entry: DocEntry, children?: DocEntry[]): DocEntry {\n  const ctx = getContext();\n  if (children && children.length > 0) {\n    entry.children = children;\n    const childSet = new Set<DocEntry>(children);\n    const filterDocs = (docs: DocEntry[]) => docs.filter((d) => !childSet.has(d));\n    // Remove children from ALL containers (story-level + every step)\n    ctx.meta.docs = filterDocs(ctx.meta.docs ?? []);\n    for (const step of ctx.meta.steps) {\n      if (step.docs) step.docs = filterDocs(step.docs);\n    }\n  }\n  if (ctx.currentStep) {\n    ctx.currentStep.docs ??= [];\n    ctx.currentStep.docs.push(entry);\n  } else {\n    ctx.meta.docs ??= [];\n    ctx.meta.docs.push(entry);\n  }\n  return entry;\n}\n\n// ============================================================================\n// Step Markers\n// ============================================================================\n\nfunction createStepMarker(keyword: StepKeyword) {\n  function stepMarker(text: string, docs?: StoryDocs): void;\n  function stepMarker(text: string, children: DocEntry[]): void;\n  function stepMarker<T>(text: string, body: () => T): T;\n  function stepMarker<T>(text: string, docsOrBody?: StoryDocs | DocEntry[] | (() => T)): T | void {\n    const ctx = getContext();\n    const isCallback = typeof docsOrBody === 'function';\n    const isChildrenArray = Array.isArray(docsOrBody);\n\n    const resolvedKeyword: StepKeyword =\n      (keyword === 'Given' || keyword === 'When' || keyword === 'Then') &&\n      ctx.meta.steps.some((s) => s.keyword === keyword)\n        ? 'And'\n        : keyword;\n\n    let stepDocs: DocEntry[] = [];\n    if (!isCallback && !isChildrenArray && docsOrBody) {\n      stepDocs = convertStoryDocsToEntries(docsOrBody as StoryDocs);\n    }\n\n    const step: StoryStep = {\n      id: `step-${ctx.stepCounter++}`,\n      startedAtMs: Date.now(),\n      keyword: resolvedKeyword,\n      text,\n      docs: stepDocs,\n      ...(isCallback ? { wrapped: true } : {}),\n    };\n\n    // Close the previous marker before this step's own assertions begin.\n    flushPendingAssertions();\n\n    ctx.meta.steps.push(step);\n    ctx.currentStep = step;\n    if (!isCallback) {\n      ctx.pendingStep = step;\n      ctx.pendingFrom = readAssertionCount();\n    }\n\n    // Handle DocEntry[] children: attach as step docs and deduplicate from story-level\n    if (isChildrenArray) {\n      const children = docsOrBody as DocEntry[];\n      if (children.length > 0) {\n        const childSet = new Set<DocEntry>(children);\n        // Deduplicate from story-level docs\n        ctx.meta.docs = (ctx.meta.docs ?? []).filter((d) => !childSet.has(d));\n        // Deduplicate from step docs of earlier steps\n        for (const prevStep of ctx.meta.steps) {\n          if (prevStep !== step && prevStep.docs) {\n            prevStep.docs = prevStep.docs.filter((d) => !childSet.has(d));\n          }\n        }\n        step.docs = [...(step.docs ?? []), ...children];\n      }\n      return;\n    }\n\n    if (!isCallback) return;\n\n    const body = docsOrBody as () => T;\n    const start = performance.now();\n    const assertionsBefore = readAssertionCount();\n\n    try {\n      const result = body();\n      if (result instanceof Promise) {\n        return result.then(\n          (val) => { step.durationMs = performance.now() - start; recordAssertions(step, assertionsBefore); return val; },\n          (err) => { step.durationMs = performance.now() - start; recordAssertions(step, assertionsBefore); throw err; },\n        ) as T;\n      }\n      step.durationMs = performance.now() - start;\n      recordAssertions(step, assertionsBefore);\n      return result;\n    } catch (err) {\n      step.durationMs = performance.now() - start;\n      recordAssertions(step, assertionsBefore);\n      throw err;\n    }\n  }\n  return stepMarker;\n}\n\n// ============================================================================\n// story.init() - Jest-specific\n// ============================================================================\n\n/**\n * Initialize a story for the current test.\n * Must be called at the start of each test that wants documentation.\n *\n * @param options - Optional story configuration (tags, ticket, meta)\n *\n * @example\n * ```ts\n * it('adds two numbers', () => {\n *   story.init();\n *   // ... rest of test\n * });\n * ```\n */\n/**\n * Declare what the file's scenarios are for, before any of them run.\n *\n * Scenarios say what the system does. This says why the feature exists and who\n * it serves, so a reader meets the intent before the examples. Call it once per\n * test file, at module scope or at the top of the outermost `describe`.\n *\n * @example\n * ```ts\n * story.feature({\n *   kind: 'ability',\n *   title: 'Employees can secure their passwords',\n *   narrative: 'Weak passwords are how most accounts get taken over.',\n * });\n * ```\n */\nfunction feature(input: FeatureInput): void {\n  // Jest only knows the current file once a test is running, so resolve it the\n  // same way `init` does and fall back to the module that called us.\n  const testPath = expect.getState().testPath || callerFile() || \"unknown\";\n  featureRegistry.set(testPath, input);\n  registerExitHandler();\n}\n\n/**\n * Best-effort source file of whoever called into this module.\n *\n * `story.feature(...)` usually runs at module scope, where Jest has not yet set\n * `testPath`. The stack is the only place the file name is available then.\n */\nfunction callerFile(): string | undefined {\n  const stack = new Error().stack;\n  if (!stack) return undefined;\n\n  for (const line of stack.split(\"\\n\").slice(1)) {\n    const match = /\\((?:file:\\/\\/)?([^()]+?):\\d+:\\d+\\)/.exec(line)\n      ?? /at (?:file:\\/\\/)?([^()\\s]+?):\\d+:\\d+/.exec(line);\n    const file = match?.[1];\n    if (file && !file.includes(\"executable-stories-jest\") && !file.startsWith(\"node:\")) {\n      return file;\n    }\n  }\n\n  return undefined;\n}\n\nfunction init(options?: StoryOptions): void {\n  // Get current test info from Jest globals\n  const state = expect.getState();\n  const currentTestName = state.currentTestName || \"Unknown test\";\n  const testPath = state.testPath || \"unknown\";\n\n  const { suitePath, testName } = extractSuitePath(currentTestName);\n\n  const meta: StoryMeta = {\n    scenario: testName,\n    steps: [],\n    suitePath,\n    tags: options?.tags,\n    tickets: normalizeTickets(options?.ticket),\n    covers: options?.covers,\n    meta: options?.meta,\n    sourceOrder: sourceOrderCounter++,\n    startedAtMs: Date.now(),\n  };\n\n  // OTel bridge: detect active span, flow data bidirectionally\n  const otelCtx = tryGetActiveOtelContext();\n  if (otelCtx) {\n    // OTel -> Story: capture traceId in structured meta\n    meta.meta = { ...meta.meta, otel: { traceId: otelCtx.traceId, spanId: otelCtx.spanId } };\n\n    // OTel -> Story: inject human-readable doc entries\n    meta.docs = meta.docs ?? [];\n    meta.docs.push({ kind: 'kv', label: 'Trace ID', value: otelCtx.traceId, phase: 'runtime' });\n\n    const template = options?.traceUrlTemplate ?? process.env.OTEL_TRACE_URL_TEMPLATE;\n    const url = resolveTraceUrl(template, otelCtx.traceId);\n    if (url) {\n      meta.docs.push({ kind: 'link', label: 'View Trace', url, phase: 'runtime' });\n    }\n\n    // Story -> OTel: enrich active span with story attributes\n    try {\n      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n      const reqUrl = import.meta.url\n        ?? (typeof __filename !== 'undefined' ? `file://${__filename}` : undefined);\n      const req = createRequire(reqUrl!);\n      const api = req('@opentelemetry/api');\n      const span = api.trace?.getActiveSpan?.();\n      if (span) {\n        span.setAttribute('story.scenario', testName);\n        if (options?.tags?.length) span.setAttribute('story.tags', options.tags);\n        if (options?.ticket) {\n          const tickets = Array.isArray(options.ticket) ? options.ticket : [options.ticket];\n          span.setAttribute('story.tickets', tickets.map((t) => typeof t === 'string' ? t : t.id));\n        }\n      }\n    } catch { /* OTel not available */ }\n  }\n\n  // Store in registry for this file\n  const existing = storyRegistry.get(testPath);\n  let scenarioIndex: number;\n  if (existing) {\n    scenarioIndex = existing.length;\n    existing.push(meta);\n  } else {\n    scenarioIndex = 0;\n    storyRegistry.set(testPath, [meta]);\n  }\n\n  // Register exit handler to flush stories when worker exits\n  registerExitHandler();\n\n  // Set active context\n  activeContext = {\n    meta,\n    currentStep: null,\n    stepCounter: 0,\n    attachments: [],\n    activeTimers: new Map(),\n    timerCounter: 0,\n    testPath,\n    scenarioIndex,\n    pendingStep: null,\n  };\n\n  // Link attachments to the registry for this test file + scenario index\n  if (!attachmentRegistry.has(testPath)) {\n    attachmentRegistry.set(testPath, new Map());\n  }\n  attachmentRegistry.get(testPath)!.set(scenarioIndex, activeContext.attachments);\n}\n\n// ============================================================================\n// Step Wrappers: story.fn() and story.expect()\n// ============================================================================\n\n/**\n * Wrap a function body as a step. Records the step with timing and `wrapped: true`.\n * Supports both sync and async functions. Returns whatever the function returns.\n *\n * @param keyword - The BDD keyword (Given, When, Then, And, But)\n * @param text - Step description\n * @param body - The function to execute\n * @returns The return value of body (or a Promise of it if body is async)\n *\n * @example\n * ```ts\n * const data = story.fn('Given', 'setup data', () => ({ a: 5, b: 3 }));\n * const result = await story.fn('When', 'call API', async () => fetch('/api'));\n * ```\n */\nfunction fn<T>(keyword: StepKeyword, text: string, body: () => T): T {\n  const ctx = getContext();\n  const resolvedKeyword: StepKeyword =\n    (keyword === 'Given' || keyword === 'When' || keyword === 'Then') &&\n    ctx.meta.steps.some((s) => s.keyword === keyword)\n      ? 'And'\n      : keyword;\n\n  const step: StoryStep = {\n    id: `step-${ctx.stepCounter++}`,\n    startedAtMs: Date.now(),\n    keyword: resolvedKeyword,\n    text,\n    docs: [],\n    wrapped: true,\n  };\n\n  ctx.meta.steps.push(step);\n  ctx.currentStep = step;\n\n  const start = performance.now();\n\n  try {\n    const result = body();\n\n    // Handle async functions\n    if (result instanceof Promise) {\n      return result.then(\n        (val) => {\n          step.durationMs = performance.now() - start;\n          return val;\n        },\n        (err) => {\n          step.durationMs = performance.now() - start;\n          throw err;\n        },\n      ) as T;\n    }\n\n    step.durationMs = performance.now() - start;\n    return result;\n  } catch (err) {\n    step.durationMs = performance.now() - start;\n    throw err;\n  }\n}\n\n/**\n * Wrap an assertion as a Then step. Shorthand for `story.fn('Then', text, body)`.\n *\n * @param text - Step description\n * @param body - The assertion function to execute\n *\n * @example\n * ```ts\n * story.expect('the result is 8', () => { expect(result).toBe(8); });\n * ```\n */\nfunction storyExpect<T>(text: string, body: () => T): T {\n  return fn('Then', text, body);\n}\n\n// ============================================================================\n// Export story object\n// ============================================================================\n\n/**\n * The main story API object for Jest.\n *\n * @example\n * ```ts\n * import { story } from 'executable-stories-jest';\n *\n * describe('Calculator', () => {\n *   it('adds two numbers', () => {\n *     story.init();\n *\n *     story.given('two numbers 5 and 3');\n *     const a = 5, b = 3;\n *\n *     story.when('I add them together');\n *     const result = a + b;\n *\n *     story.then('the result is 8');\n *     expect(result).toBe(8);\n *   });\n * });\n * ```\n */\nexport const story = {\n  // Jest-specific init\n  init,\n  feature,\n\n  // BDD step markers\n  given: createStepMarker(\"Given\"),\n  when: createStepMarker(\"When\"),\n  then: createStepMarker(\"Then\"),\n  and: createStepMarker(\"And\"),\n  but: createStepMarker(\"But\"),\n\n  // AAA pattern aliases\n  arrange: createStepMarker(\"Given\"),\n  act: createStepMarker(\"When\"),\n  assert: createStepMarker(\"Then\"),\n\n  // Additional aliases\n  setup: createStepMarker(\"Given\"),\n  context: createStepMarker(\"Given\"),\n  execute: createStepMarker(\"When\"),\n  action: createStepMarker(\"When\"),\n  verify: createStepMarker(\"Then\"),\n\n  // Standalone doc methods\n  note(text: string, children?: DocEntry[]): DocEntry {\n    return attachDoc({ kind: \"note\", text, phase: \"runtime\" }, children);\n  },\n\n  tag(name: string | string[], children?: DocEntry[]): DocEntry {\n    const names = Array.isArray(name) ? name : [name];\n    return attachDoc({ kind: \"tag\", names, phase: \"runtime\" }, children);\n  },\n\n  kv(options: KvOptions, children?: DocEntry[]): DocEntry {\n    return attachDoc({ kind: \"kv\", label: options.label, value: options.value, phase: \"runtime\" }, children);\n  },\n\n  json(options: JsonOptions, children?: DocEntry[]): DocEntry {\n    const content = JSON.stringify(options.value, null, 2);\n    return attachDoc({ kind: \"code\", label: options.label, content, lang: \"json\", phase: \"runtime\" }, children);\n  },\n\n  /**\n   * Capture what the world looks like at this step — a data snapshot that\n   * becomes a storyboard frame in generated docs. Consecutive snapshots with\n   * the same label get diffed downstream. The value must be JSON-serializable.\n   * @example\n   * ```ts\n   * story.given('an empty basket');\n   * story.state({ label: 'Basket', value: { items: [], total: 0 } });\n   * story.when('a widget is added');\n   * story.state({ label: 'Basket', value: { items: ['widget'], total: 49.99 } });\n   * ```\n   */\n  state(options: StateOptions, children?: DocEntry[]): DocEntry {\n    warnIfLargeState(options.label, options.value);\n    return attachDoc({ kind: \"state\", label: options.label, value: options.value, phase: \"runtime\" }, children);\n  },\n\n  code(options: CodeOptions, children?: DocEntry[]): DocEntry {\n    return attachDoc({ kind: \"code\", label: options.label, content: options.content, lang: options.lang, phase: \"runtime\" }, children);\n  },\n\n  table(options: TableOptions, children?: DocEntry[]): DocEntry {\n    return attachDoc({ kind: \"table\", label: options.label, columns: options.columns, rows: options.rows, phase: \"runtime\" }, children);\n  },\n\n  link(options: LinkOptions, children?: DocEntry[]): DocEntry {\n    return attachDoc({ kind: \"link\", label: options.label, url: options.url, phase: \"runtime\" }, children);\n  },\n\n  section(options: SectionOptions, children?: DocEntry[]): DocEntry {\n    return attachDoc({ kind: \"section\", title: options.title, markdown: options.markdown, phase: \"runtime\" }, children);\n  },\n\n  mermaid(options: MermaidOptions, children?: DocEntry[]): DocEntry {\n    return attachDoc({ kind: \"mermaid\", code: options.code, title: options.title, phase: \"runtime\" }, children);\n  },\n\n  screenshot(options: ScreenshotOptions, children?: DocEntry[]): DocEntry {\n    return attachDoc({ kind: \"screenshot\", path: options.path, alt: options.alt, phase: \"runtime\" }, children);\n  },\n\n  video(options: VideoOptions, children?: DocEntry[]): DocEntry {\n    return attachDoc({ kind: \"video\", path: options.path, caption: options.caption, poster: options.poster, phase: \"runtime\" }, children);\n  },\n\n  html(options: HtmlOptions, children?: DocEntry[]): DocEntry {\n    return attachDoc(buildHtmlDocEntry(options), children);\n  },\n\n  custom(options: CustomOptions, children?: DocEntry[]): DocEntry {\n    return attachDoc({ kind: \"custom\", type: options.type, data: options.data, phase: \"runtime\" }, children);\n  },\n\n  // Attachments\n  attach(options: AttachmentOptions): void {\n    const ctx = getContext();\n    const stepIndex = ctx.currentStep\n      ? ctx.meta.steps.indexOf(ctx.currentStep)\n      : undefined;\n    ctx.attachments.push({\n      ...options,\n      stepIndex: stepIndex !== undefined && stepIndex >= 0 ? stepIndex : undefined,\n      stepId: ctx.currentStep?.id,\n    });\n  },\n\n  // OTel span attachment\n  attachSpans(spans: ReadonlyArray<Record<string, unknown>>): void {\n    const ctx = getContext();\n    if (!otelSpansRegistry.has(ctx.testPath)) {\n      otelSpansRegistry.set(ctx.testPath, new Map());\n    }\n    otelSpansRegistry.get(ctx.testPath)!.set(ctx.scenarioIndex, spans);\n  },\n\n  // Step wrappers\n  fn,\n  expect: storyExpect,\n\n  // Step timing\n  startTimer(): number {\n    const ctx = getContext();\n    const token = ctx.timerCounter++;\n    const stepIndex = ctx.currentStep\n      ? ctx.meta.steps.indexOf(ctx.currentStep)\n      : undefined;\n    ctx.activeTimers.set(token, {\n      start: performance.now(),\n      stepIndex: stepIndex !== undefined && stepIndex >= 0 ? stepIndex : undefined,\n      stepId: ctx.currentStep?.id,\n      consumed: false,\n    });\n    return token;\n  },\n\n  endTimer(token: number): void {\n    const ctx = getContext();\n    const entry = ctx.activeTimers.get(token);\n    if (!entry || entry.consumed) return;\n\n    entry.consumed = true;\n    const durationMs = performance.now() - entry.start;\n\n    let step: StoryStep | undefined;\n    if (entry.stepId) {\n      step = ctx.meta.steps.find((s) => s.id === entry.stepId);\n    }\n    if (!step && entry.stepIndex !== undefined) {\n      step = ctx.meta.steps[entry.stepIndex];\n    }\n\n    if (step) {\n      step.durationMs = durationMs;\n    }\n  },\n};\n\nexport type Story = typeof story;\n\n// ============================================================================\n// Internal exports for setup file\n// ============================================================================\n\n/**\n * Internal API for the setup file and tests. Not for public use.\n * @internal\n */\nexport const _internal = {\n  flushStories,\n  flushPendingAssertions,\n  /** Clear active context (for tests that assert getContext() throws). */\n  clearContext(): void {\n    activeContext = null;\n  },\n};\n"],"mappings":"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","names":[]}