awaitly-visualizer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/index.browser.cjs +1677 -0
  2. package/dist/index.browser.cjs.map +1 -0
  3. package/dist/index.browser.d.cts +166 -0
  4. package/dist/index.browser.d.ts +166 -0
  5. package/dist/index.browser.js +1677 -0
  6. package/dist/index.browser.js.map +1 -0
  7. package/dist/index.cjs +1680 -0
  8. package/dist/index.cjs.map +1 -0
  9. package/dist/index.d.cts +242 -0
  10. package/dist/index.d.ts +242 -0
  11. package/dist/index.js +1680 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/kroki/fetch.cjs +3 -0
  14. package/dist/kroki/fetch.cjs.map +1 -0
  15. package/dist/kroki/fetch.d.cts +86 -0
  16. package/dist/kroki/fetch.d.ts +86 -0
  17. package/dist/kroki/fetch.js +3 -0
  18. package/dist/kroki/fetch.js.map +1 -0
  19. package/dist/notifiers/discord.cjs +3 -0
  20. package/dist/notifiers/discord.cjs.map +1 -0
  21. package/dist/notifiers/discord.d.cts +70 -0
  22. package/dist/notifiers/discord.d.ts +70 -0
  23. package/dist/notifiers/discord.js +3 -0
  24. package/dist/notifiers/discord.js.map +1 -0
  25. package/dist/notifiers/slack.cjs +38 -0
  26. package/dist/notifiers/slack.cjs.map +1 -0
  27. package/dist/notifiers/slack.d.cts +95 -0
  28. package/dist/notifiers/slack.d.ts +95 -0
  29. package/dist/notifiers/slack.js +38 -0
  30. package/dist/notifiers/slack.js.map +1 -0
  31. package/dist/notifiers/webhook.cjs +3 -0
  32. package/dist/notifiers/webhook.cjs.map +1 -0
  33. package/dist/notifiers/webhook.d.cts +115 -0
  34. package/dist/notifiers/webhook.d.ts +115 -0
  35. package/dist/notifiers/webhook.js +3 -0
  36. package/dist/notifiers/webhook.js.map +1 -0
  37. package/dist/performance-analyzer-B5VF5b1F.d.ts +663 -0
  38. package/dist/performance-analyzer-BNwE4AiO.d.cts +663 -0
  39. package/dist/types-BIZSmXif.d.ts +350 -0
  40. package/dist/types-BnWc9Wlr.d.cts +350 -0
  41. package/dist/url-PkfQz4V5.d.cts +750 -0
  42. package/dist/url-PkfQz4V5.d.ts +750 -0
  43. package/package.json +105 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.browser.ts","../src/utils/timing.ts","../src/parallel-detector.ts","../src/ir-builder.ts","../src/renderers/ascii.ts","../src/types.ts","../src/renderers/colors.ts","../src/renderers/mermaid.ts","../src/performance-analyzer.ts","../src/renderers/flowchart.ts","../src/renderers/logger.ts","../src/renderers/html-styles.ts","../src/renderers/html-client.ts","../src/renderers/html.ts","../src/decision-tracker.ts","../src/time-travel.ts","../src/kroki/encoder.ts","../src/kroki/url.ts","../src/kroki/mermaid-ink.ts"],"sourcesContent":["/**\n * Browser-safe Workflow Visualization Module\n *\n * Excludes Node.js-specific features:\n * - createLiveVisualizer (requires process.stdout)\n * - fetchKroki* functions (requires Node.js fetch with Buffer)\n *\n * @example\n * ```typescript\n * import { createVisualizer } from 'awaitly-visualizer';\n *\n * const viz = createVisualizer({ workflowName: 'checkout' });\n * const workflow = createWorkflow(deps, { onEvent: viz.handleEvent });\n *\n * await workflow(async (step) => {\n * await step(() => validateCart(cart), 'Validate cart');\n * await step(() => processPayment(payment), 'Process payment');\n * });\n *\n * console.log(viz.render());\n * ```\n */\n\nimport type { WorkflowEvent } from \"awaitly/workflow\";\nimport type {\n OutputFormat,\n RenderOptions,\n ScopeEndEvent,\n ScopeStartEvent,\n DecisionStartEvent,\n DecisionBranchEvent,\n DecisionEndEvent,\n VisualizerOptions,\n WorkflowIR,\n} from \"./types\";\nimport { createIRBuilder } from \"./ir-builder\";\nimport { asciiRenderer, mermaidRenderer, loggerRenderer, flowchartRenderer, defaultColorScheme } from \"./renderers\";\n\n// =============================================================================\n// Re-exports (browser-safe modules)\n// =============================================================================\n\nexport * from \"./types\";\nexport { createIRBuilder, type IRBuilderOptions } from \"./ir-builder\";\nexport { asciiRenderer, mermaidRenderer, loggerRenderer, flowchartRenderer, defaultColorScheme } from \"./renderers\";\nexport type { LoggerOutput, LoggerRenderOptions, StepLog, HookLog, WorkflowSummary } from \"./renderers\";\nexport { htmlRenderer, renderToHTML } from \"./renderers/html\";\nexport { detectParallelGroups, createParallelDetector, type ParallelDetectorOptions } from \"./parallel-detector\";\nexport { trackDecision, trackIf, trackSwitch, type DecisionTracker, type IfTracker, type SwitchTracker } from \"./decision-tracker\";\n\n// Time-travel debugging\nexport {\n createTimeTravelController,\n type TimeTravelController,\n type TimeTravelOptions,\n} from \"./time-travel\";\n\n// Performance analysis\nexport {\n createPerformanceAnalyzer,\n getHeatLevel,\n type PerformanceAnalyzer,\n type WorkflowRun,\n} from \"./performance-analyzer\";\n\n// Kroki URL generation (browser-safe)\nexport {\n toKrokiUrl,\n toKrokiSvgUrl,\n toKrokiPngUrl,\n createUrlGenerator,\n type KrokiFormat,\n type UrlGeneratorOptions,\n} from \"./kroki/url\";\n\n// Mermaid.ink URL generation (browser-safe)\nexport {\n toMermaidInkUrl,\n toMermaidInkSvgUrl,\n toMermaidInkPngUrl,\n toMermaidInkJpegUrl,\n toMermaidInkWebpUrl,\n toMermaidInkPdfUrl,\n createMermaidInkGenerator,\n encodeForMermaidInk,\n buildMermaidInkUrl,\n type MermaidInkFormat,\n type MermaidInkImageType,\n type MermaidInkTheme,\n type MermaidInkPaperSize,\n type MermaidInkOptions,\n type MermaidInkGenerator,\n} from \"./kroki/mermaid-ink\";\n\n// Re-export notifier provider types for convenience\nexport type {\n DiagramProvider,\n ProviderOptions,\n KrokiProviderOptions,\n MermaidInkProviderOptions,\n} from \"./notifiers/types\";\n\n// =============================================================================\n// Node-only types (allows type-only imports in browser)\n// =============================================================================\n\nexport type { LiveVisualizer } from \"./live-visualizer\";\n\n// =============================================================================\n// Node-only stubs (throw helpful errors in browser)\n// =============================================================================\n\n/**\n * Creates a live terminal visualizer.\n *\n * @throws Error - Not available in browser environments\n */\nexport const createLiveVisualizer = (): never => {\n throw new Error(\n \"createLiveVisualizer is not available in browser. \" +\n \"It requires Node.js process.stdout.\"\n );\n};\n\n// =============================================================================\n// Visualizer Interface\n// =============================================================================\n\n/**\n * Workflow visualizer that processes events and renders output.\n */\nexport interface WorkflowVisualizer {\n /** Process a workflow event */\n handleEvent: (event: WorkflowEvent<unknown>) => void;\n\n /** Process a scope event (parallel/race) */\n handleScopeEvent: (event: ScopeStartEvent | ScopeEndEvent) => void;\n\n /** Process a decision event (conditional branches) */\n handleDecisionEvent: (event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent) => void;\n\n /** Get current IR state */\n getIR: () => WorkflowIR;\n\n /** Render current state using the default renderer */\n render: () => string;\n\n /** Render to a specific format */\n renderAs: (format: OutputFormat) => string;\n\n /** Reset state for a new workflow */\n reset: () => void;\n\n /** Subscribe to IR updates (for live visualization) */\n onUpdate: (callback: (ir: WorkflowIR) => void) => () => void;\n}\n\n// =============================================================================\n// Create Visualizer (browser-safe version)\n// =============================================================================\n\n/**\n * Create a workflow visualizer.\n *\n * @example\n * ```typescript\n * const viz = createVisualizer({ workflowName: 'my-workflow' });\n *\n * const workflow = createWorkflow(deps, {\n * onEvent: viz.handleEvent,\n * });\n *\n * await workflow(async (step) => { ... });\n *\n * console.log(viz.render());\n * ```\n */\nexport function createVisualizer(\n options: VisualizerOptions = {}\n): WorkflowVisualizer {\n const {\n workflowName,\n detectParallel = true,\n showTimings = true,\n showKeys = false,\n colors: customColors,\n } = options;\n\n const builder = createIRBuilder({ detectParallel });\n const updateCallbacks: Set<(ir: WorkflowIR) => void> = new Set();\n\n // Renderers\n const ascii = asciiRenderer();\n const mermaid = mermaidRenderer();\n const logger = loggerRenderer();\n const flowchart = flowchartRenderer();\n\n // Build render options (browser-safe: use fixed width instead of process.stdout.columns)\n const renderOptions: RenderOptions = {\n showTimings,\n showKeys,\n terminalWidth: 80,\n colors: { ...defaultColorScheme, ...customColors },\n };\n\n function notifyUpdate(): void {\n if (updateCallbacks.size > 0) {\n const ir = builder.getIR();\n for (const callback of updateCallbacks) {\n callback(ir);\n }\n }\n }\n\n function handleEvent(event: WorkflowEvent<unknown>): void {\n // Route scope events to handleScopeEvent for proper IR building\n if (event.type === \"scope_start\" || event.type === \"scope_end\") {\n handleScopeEvent(event as ScopeStartEvent | ScopeEndEvent);\n return;\n }\n\n builder.handleEvent(event);\n\n // Set workflow name if provided\n if (event.type === \"workflow_start\" && workflowName) {\n // Note: We'd need to extend the builder to support setting name\n // For now, the name is passed in render options\n }\n\n notifyUpdate();\n }\n\n function handleScopeEvent(event: ScopeStartEvent | ScopeEndEvent): void {\n builder.handleScopeEvent(event);\n notifyUpdate();\n }\n\n function handleDecisionEvent(\n event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent\n ): void {\n builder.handleDecisionEvent(event);\n notifyUpdate();\n }\n\n function getIR(): WorkflowIR {\n const ir = builder.getIR();\n // Apply workflow name if provided\n if (workflowName && !ir.root.name) {\n ir.root.name = workflowName;\n }\n return ir;\n }\n\n function render(): string {\n const ir = getIR();\n return ascii.render(ir, renderOptions);\n }\n\n function renderAs(format: OutputFormat): string {\n const ir = getIR();\n\n switch (format) {\n case \"ascii\":\n return ascii.render(ir, renderOptions);\n\n case \"mermaid\":\n return mermaid.render(ir, renderOptions);\n\n case \"json\": {\n const toSerialize = ir.hooks\n ? {\n ...ir,\n hooks: {\n ...ir.hooks,\n onAfterStep:\n ir.hooks.onAfterStep instanceof Map\n ? Object.fromEntries(ir.hooks.onAfterStep)\n : ir.hooks.onAfterStep ?? {},\n },\n }\n : ir;\n const replacer = (_key: string, value: unknown): unknown =>\n typeof value === \"bigint\" ? value.toString() : value;\n return JSON.stringify(toSerialize, replacer, 2);\n }\n\n case \"logger\":\n return logger.render(ir, renderOptions);\n\n case \"flowchart\":\n return flowchart.render(ir, renderOptions);\n\n default:\n throw new Error(`Unknown format: ${format}`);\n }\n }\n\n function reset(): void {\n builder.reset();\n notifyUpdate();\n }\n\n function onUpdate(callback: (ir: WorkflowIR) => void): () => void {\n updateCallbacks.add(callback);\n return () => updateCallbacks.delete(callback);\n }\n\n return {\n handleEvent,\n handleScopeEvent,\n handleDecisionEvent,\n getIR,\n render,\n renderAs,\n reset,\n onUpdate,\n };\n}\n\n// =============================================================================\n// Convenience Functions\n// =============================================================================\n\n/**\n * Combine multiple event handlers into one.\n * Use when you need visualization + logging + custom handlers.\n *\n * @example\n * ```typescript\n * const viz = createVisualizer({ workflowName: 'checkout' });\n * const workflow = createWorkflow(deps, {\n * onEvent: combineEventHandlers(\n * viz.handleEvent,\n * (e) => console.log(e.type),\n * (e) => metrics.track(e),\n * ),\n * });\n * ```\n */\nexport function combineEventHandlers<E = unknown, C = void>(\n ...handlers: Array<(event: WorkflowEvent<E, C>, ctx?: C) => void>\n): (event: WorkflowEvent<E, C>, ctx: C) => void {\n return (event, ctx) => {\n for (const handler of handlers) {\n handler(event, ctx);\n }\n };\n}\n\n/**\n * Union type for all collectable/visualizable events (workflow + decision).\n */\nexport type CollectableEvent =\n | WorkflowEvent<unknown>\n | DecisionStartEvent\n | DecisionBranchEvent\n | DecisionEndEvent;\n\n/**\n * Visualize collected events (post-execution).\n *\n * Supports both workflow events (from onEvent) and decision events\n * (from trackDecision/trackIf/trackSwitch).\n *\n * @example\n * ```typescript\n * const events: CollectableEvent[] = [];\n * const workflow = createWorkflow(deps, {\n * onEvent: (e) => events.push(e),\n * });\n *\n * await workflow(async (step) => {\n * const decision = trackIf('check', condition, {\n * emit: (e) => events.push(e),\n * });\n * // ...\n * });\n *\n * console.log(visualizeEvents(events));\n * ```\n */\nexport function visualizeEvents(\n events: CollectableEvent[],\n options: VisualizerOptions = {}\n): string {\n const viz = createVisualizer(options);\n\n for (const event of events) {\n if (event.type.startsWith(\"decision_\")) {\n viz.handleDecisionEvent(event as DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent);\n } else {\n viz.handleEvent(event as WorkflowEvent<unknown>);\n }\n }\n\n return viz.render();\n}\n\n/**\n * Create an event collector for later visualization.\n *\n * Supports both workflow events (from onEvent) and decision events\n * (from trackDecision/trackIf/trackSwitch).\n *\n * @example\n * ```typescript\n * const collector = createEventCollector();\n *\n * const workflow = createWorkflow(deps, {\n * onEvent: collector.handleEvent,\n * });\n *\n * await workflow(async (step) => {\n * // Decision events can also be collected\n * const decision = trackIf('check', condition, {\n * emit: collector.handleDecisionEvent,\n * });\n * // ...\n * });\n *\n * console.log(collector.visualize());\n * ```\n */\nexport function createEventCollector(options: VisualizerOptions = {}) {\n const events: CollectableEvent[] = [];\n\n return {\n /** Handle a workflow event */\n handleEvent: (event: WorkflowEvent<unknown>) => {\n events.push(event);\n },\n\n /** Handle a decision event */\n handleDecisionEvent: (event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent) => {\n events.push(event);\n },\n\n /** Get all collected events */\n getEvents: () => [...events],\n\n /** Get workflow events only */\n getWorkflowEvents: () => events.filter((e): e is WorkflowEvent<unknown> =>\n !e.type.startsWith(\"decision_\")\n ),\n\n /** Get decision events only */\n getDecisionEvents: () => events.filter((e): e is DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent =>\n e.type.startsWith(\"decision_\")\n ),\n\n /** Clear collected events */\n clear: () => {\n events.length = 0;\n },\n\n /** Visualize collected events */\n visualize: () => {\n const viz = createVisualizer(options);\n for (const event of events) {\n if (event.type.startsWith(\"decision_\")) {\n viz.handleDecisionEvent(event as DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent);\n } else {\n viz.handleEvent(event as WorkflowEvent<unknown>);\n }\n }\n return viz.render();\n },\n\n /** Visualize in a specific format */\n visualizeAs: (format: OutputFormat) => {\n const viz = createVisualizer(options);\n for (const event of events) {\n if (event.type.startsWith(\"decision_\")) {\n viz.handleDecisionEvent(event as DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent);\n } else {\n viz.handleEvent(event as WorkflowEvent<unknown>);\n }\n }\n return viz.renderAs(format);\n },\n };\n}\n","/**\n * Timing utilities for workflow visualization.\n */\n\n/**\n * Format duration in milliseconds to a human-readable string.\n *\n * @example\n * formatDuration(23) // \"23ms\"\n * formatDuration(1500) // \"1.5s\"\n * formatDuration(65000) // \"1m 5s\"\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)}ms`;\n }\n\n if (ms < 60000) {\n const seconds = ms / 1000;\n // Show one decimal for seconds\n return `${seconds.toFixed(1).replace(/\\.0$/, \"\")}s`;\n }\n\n let minutes = Math.floor(ms / 60000);\n let seconds = Math.round((ms % 60000) / 1000);\n if (seconds >= 60) {\n minutes += 1;\n seconds = 0;\n }\n\n if (seconds === 0) {\n return `${minutes}m`;\n }\n\n return `${minutes}m ${seconds}s`;\n}\n\n/**\n * Generate a unique ID for nodes.\n */\nexport function generateId(): string {\n return `node_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n","/**\n * Parallel Detection - Heuristic detection of parallel execution from timing.\n *\n * When steps overlap in time (one starts before another ends), they are\n * likely running in parallel. This module detects such patterns and\n * groups overlapping steps into ParallelNode structures.\n */\n\nimport type { FlowNode, ParallelNode, StepNode } from \"./types\";\n\n/**\n * Options for parallel detection.\n */\nexport interface ParallelDetectorOptions {\n /**\n * Minimum overlap in milliseconds to consider steps parallel.\n * Default: 0 (any overlap counts)\n */\n minOverlapMs?: number;\n\n /**\n * Maximum gap in milliseconds to still consider steps as part of same parallel group.\n * Default: 5 (steps starting within 5ms are grouped)\n */\n maxGapMs?: number;\n}\n\n/**\n * Step timing information for overlap detection.\n */\ninterface StepTiming {\n node: StepNode;\n startTs: number;\n endTs: number;\n}\n\n/**\n * Check if nodes contain real scope nodes (from scope_start/scope_end events).\n * When real scope nodes exist, heuristic detection should be skipped to avoid\n * duplicating or conflicting with the explicit structure.\n */\nfunction hasRealScopeNodes(nodes: FlowNode[]): boolean {\n for (const node of nodes) {\n // Real scope nodes are parallel/race/sequence that came from scope events\n // (not from heuristic detection, which uses ids starting with \"detected_\")\n if (\n (node.type === \"parallel\" || node.type === \"race\" || node.type === \"sequence\") &&\n !node.id.startsWith(\"detected_\")\n ) {\n return true;\n }\n // Also check for decision nodes if present\n if (node.type === \"decision\") {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Group overlapping steps into parallel nodes.\n *\n * Algorithm:\n * 1. Sort steps by start time\n * 2. For each step, check if it overlaps with existing parallel groups\n * 3. If it overlaps, add to group; otherwise start new sequence\n * 4. Merge overlapping groups when step bridges them\n *\n * Note: If real scope nodes (from scope_start/scope_end) are present,\n * heuristic detection is skipped to avoid conflicts.\n */\nexport function detectParallelGroups(\n nodes: FlowNode[],\n options: ParallelDetectorOptions = {}\n): FlowNode[] {\n // If real scope nodes exist, skip heuristic detection\n // The explicit scope events provide accurate structure\n if (hasRealScopeNodes(nodes)) {\n return nodes;\n }\n\n const { minOverlapMs = 0, maxGapMs = 5 } = options;\n\n // Extract step nodes with timing info, preserving indices for position restoration\n const stepsWithTiming: (StepTiming & { originalIndex: number })[] = [];\n const nonStepNodes: { node: FlowNode; originalIndex: number }[] = [];\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n if (node.type === \"step\" && node.startTs !== undefined) {\n stepsWithTiming.push({\n node,\n startTs: node.startTs,\n endTs: node.endTs ?? node.startTs + (node.durationMs ?? 0),\n originalIndex: i,\n });\n } else {\n // Keep non-step nodes with their original position\n nonStepNodes.push({ node, originalIndex: i });\n }\n }\n\n if (stepsWithTiming.length <= 1) {\n return nodes; // Nothing to group\n }\n\n // Sort by start time\n stepsWithTiming.sort((a, b) => a.startTs - b.startTs);\n\n // Group overlapping steps\n type StepTimingWithIndex = StepTiming & { originalIndex: number };\n const groups: StepTimingWithIndex[][] = [];\n let currentGroup: StepTimingWithIndex[] = [stepsWithTiming[0]];\n\n for (let i = 1; i < stepsWithTiming.length; i++) {\n const step = stepsWithTiming[i];\n const groupStart = Math.min(...currentGroup.map((s) => s.startTs));\n const groupEnd = Math.max(...currentGroup.map((s) => s.endTs));\n\n // Two ways steps can be parallel:\n // 1. They started together (within maxGapMs) - handles timing jitter\n // 2. They genuinely overlap (step starts before group ends)\n const startedTogether = step.startTs <= groupStart + maxGapMs;\n const hasTrueOverlap = step.startTs < groupEnd;\n\n if (!startedTogether && !hasTrueOverlap) {\n // Sequential: step started after group ended AND not with the group\n groups.push(currentGroup);\n currentGroup = [step];\n continue;\n }\n\n // Check minOverlapMs threshold for overlap duration\n // For steps that started together, overlap is measured from step start to group end\n // For steps with true overlap, it's from step start to min(step end, group end)\n const overlapDuration = hasTrueOverlap\n ? Math.min(step.endTs, groupEnd) - step.startTs\n : 0;\n\n // Started together bypasses minOverlapMs (they're parallel by definition)\n // True overlap must meet the minOverlapMs threshold\n if (startedTogether || overlapDuration >= minOverlapMs) {\n currentGroup.push(step);\n } else {\n // Overlap too small - treat as sequential\n groups.push(currentGroup);\n currentGroup = [step];\n }\n }\n groups.push(currentGroup);\n\n // Convert groups to nodes with position tracking\n const groupedNodes: { node: FlowNode; position: number }[] = [];\n\n for (const group of groups) {\n // Use the minimum original index as the position for the group\n const position = Math.min(...group.map((s) => s.originalIndex));\n\n if (group.length === 1) {\n // Single step - no parallel grouping needed\n groupedNodes.push({ node: group[0].node, position });\n } else {\n // Multiple overlapping steps - create parallel node\n const children = group.map((s) => s.node);\n const startTs = Math.min(...group.map((s) => s.startTs));\n const endTs = Math.max(...group.map((s) => s.endTs));\n\n const parallelNode: ParallelNode = {\n type: \"parallel\",\n id: `detected_parallel_${startTs}`,\n name: `${children.length} parallel steps`,\n state: deriveGroupState(children),\n mode: \"all\",\n children,\n startTs,\n endTs,\n durationMs: endTs - startTs,\n };\n\n groupedNodes.push({ node: parallelNode, position });\n }\n }\n\n // Add non-step nodes with their original positions\n for (const { node, originalIndex } of nonStepNodes) {\n groupedNodes.push({ node, position: originalIndex });\n }\n\n // Sort by original position to preserve ordering\n groupedNodes.sort((a, b) => a.position - b.position);\n\n return groupedNodes.map((g) => g.node);\n}\n\n/**\n * Derive the state of a group from its children.\n */\nfunction deriveGroupState(\n children: FlowNode[]\n): \"pending\" | \"running\" | \"success\" | \"error\" | \"aborted\" | \"cached\" {\n const hasError = children.some((c) => c.state === \"error\");\n if (hasError) return \"error\";\n\n const hasRunning = children.some((c) => c.state === \"running\");\n if (hasRunning) return \"running\";\n\n const hasPending = children.some((c) => c.state === \"pending\");\n if (hasPending) return \"pending\";\n\n const allSuccess = children.every(\n (c) => c.state === \"success\" || c.state === \"cached\"\n );\n if (allSuccess) return \"success\";\n\n return \"success\";\n}\n\n/**\n * Create a parallel detector that processes nodes.\n */\nexport function createParallelDetector(options: ParallelDetectorOptions = {}) {\n return {\n /**\n * Process nodes and group overlapping ones into parallel nodes.\n */\n detect: (nodes: FlowNode[]) => detectParallelGroups(nodes, options),\n };\n}\n","/**\n * IR Builder - Converts workflow events to Intermediate Representation.\n *\n * The builder maintains state as events arrive, constructing a tree\n * representation of the workflow execution that can be rendered.\n */\n\nimport type { WorkflowEvent } from \"awaitly/workflow\";\nimport type {\n FlowNode,\n ScopeEndEvent,\n ScopeStartEvent,\n ScopeType,\n StepNode,\n StepState,\n WorkflowIR,\n WorkflowNode,\n ParallelNode,\n RaceNode,\n DecisionNode,\n DecisionStartEvent,\n DecisionBranchEvent,\n DecisionEndEvent,\n DecisionBranch,\n IRSnapshot,\n ActiveStepSnapshot,\n WorkflowHooks,\n HookExecution,\n StreamNode,\n} from \"./types\";\nimport { generateId } from \"./utils/timing\";\nimport { detectParallelGroups, type ParallelDetectorOptions } from \"./parallel-detector\";\n\n// =============================================================================\n// Builder Options\n// =============================================================================\n\n/**\n * Options for the IR builder.\n */\nexport interface IRBuilderOptions {\n /**\n * Enable heuristic parallel detection based on timing.\n * When true, overlapping steps are grouped into ParallelNodes.\n * Default: true\n */\n detectParallel?: boolean;\n\n /**\n * Options for parallel detection.\n */\n parallelDetection?: ParallelDetectorOptions;\n\n /**\n * Enable snapshot recording for time-travel debugging.\n * When true, the builder captures IR state after each event.\n * Default: false\n */\n enableSnapshots?: boolean;\n\n /**\n * Maximum number of snapshots to keep (ring buffer behavior).\n * When exceeded, oldest snapshots are discarded.\n * Default: 1000\n */\n maxSnapshots?: number;\n}\n\n// =============================================================================\n// Builder State\n// =============================================================================\n\ninterface ActiveStep {\n id: string;\n name?: string;\n key?: string;\n startTs: number;\n retryCount: number;\n timedOut: boolean;\n timeoutMs?: number;\n}\n\ninterface ActiveScope {\n id: string;\n name?: string;\n type: ScopeType;\n startTs: number;\n children: FlowNode[];\n}\n\ninterface ActiveDecision {\n id: string;\n name?: string;\n condition?: string;\n decisionValue?: unknown;\n startTs: number;\n branches: Map<string, DecisionBranch>;\n branchTaken?: string | boolean;\n /** Nodes that arrived before any branch was created */\n pendingChildren: FlowNode[];\n}\n\ninterface ActiveStream {\n id: string;\n namespace: string;\n startTs: number;\n writeCount: number;\n readCount: number;\n streamState: \"active\" | \"closed\" | \"error\";\n backpressureOccurred: boolean;\n finalPosition: number;\n}\n\n// =============================================================================\n// IR Builder\n// =============================================================================\n\n/**\n * Creates an IR builder that processes workflow events.\n */\nexport function createIRBuilder(options: IRBuilderOptions = {}) {\n const {\n detectParallel = true,\n parallelDetection,\n enableSnapshots: initialEnableSnapshots = false,\n maxSnapshots = 1000,\n } = options;\n\n // Mutable so time-travel stopRecording() can pause snapshot accumulation\n let enableSnapshots = initialEnableSnapshots;\n\n // Current workflow state\n // Generate a stable default ID for use before workflow_start event\n const defaultWorkflowId = generateId();\n let workflowId: string | undefined;\n let workflowStartTs: number | undefined;\n let workflowEndTs: number | undefined;\n let workflowState: StepState = \"pending\";\n let workflowError: unknown;\n let workflowDurationMs: number | undefined;\n\n // Active steps (currently running)\n const activeSteps = new Map<string, ActiveStep>();\n\n // Active scopes (parallel/race blocks)\n const scopeStack: ActiveScope[] = [];\n\n // Active decisions (conditional branches)\n const decisionStack: ActiveDecision[] = [];\n\n // Active streams (streaming operations)\n const activeStreams = new Map<string, ActiveStream>();\n\n // Completed nodes at the current scope level\n let currentNodes: FlowNode[] = [];\n\n // Metadata\n let createdAt = Date.now();\n let lastUpdatedAt = createdAt;\n\n // Hook executions\n let hookState: WorkflowHooks = {\n onAfterStep: new Map(),\n };\n /** WorkflowId that the current pre-start hooks (shouldRun, onBeforeStart) belong to */\n let preStartHookWorkflowId: string | undefined;\n\n // Snapshot state for time-travel debugging\n const snapshots: IRSnapshot[] = [];\n let eventIndex = 0;\n\n /**\n * Get the step ID from an event.\n * Uses stepId if available (new events), then falls back to stepKey or name,\n * and finally generates a random ID for backwards compatibility.\n */\n function getStepId(event: { stepId?: string; stepKey?: string; name?: string }): string {\n return event.stepId ?? event.stepKey ?? event.name ?? generateId();\n }\n\n /**\n * Add a completed node to the current scope or decision branch.\n * Scope stack takes priority over decision stack because scopes are\n * the innermost container - when a scope ends, its node will be added\n * to the decision branch via this same function.\n */\n function addNode(node: FlowNode): void {\n // If we're in a scope, add to the innermost scope\n // (scope takes priority - when scope ends, the scope node will be added to decision)\n if (scopeStack.length > 0) {\n scopeStack[scopeStack.length - 1].children.push(node);\n lastUpdatedAt = Date.now();\n return;\n }\n\n // If we're in a decision (but not in a scope), add to the taken branch\n if (decisionStack.length > 0) {\n const decision = decisionStack[decisionStack.length - 1];\n // Find the taken branch\n for (const branch of decision.branches.values()) {\n if (branch.taken) {\n branch.children.push(node);\n lastUpdatedAt = Date.now();\n return;\n }\n }\n // No branch is taken yet (or first branch was taken: false) - buffer for later\n decision.pendingChildren.push(node);\n lastUpdatedAt = Date.now();\n return;\n }\n\n // Add to the root level\n currentNodes.push(node);\n lastUpdatedAt = Date.now();\n }\n\n /**\n * Capture a snapshot of the current IR state (for time-travel debugging).\n * Called after each event is processed.\n */\n function captureSnapshot(event: WorkflowEvent<unknown>): void {\n if (!enableSnapshots) return;\n\n // Deep clone the current IR state\n const ir = getIR();\n\n // Clone active steps for debugging\n const activeStepsCopy = new Map<string, ActiveStepSnapshot>();\n for (const [id, step] of activeSteps) {\n activeStepsCopy.set(id, {\n id: step.id,\n name: step.name,\n key: step.key,\n startTs: step.startTs,\n retryCount: step.retryCount,\n timedOut: step.timedOut,\n timeoutMs: step.timeoutMs,\n });\n }\n\n const snapshot: IRSnapshot = {\n id: `snapshot_${eventIndex}`,\n eventIndex,\n event: structuredClone(event),\n ir: structuredClone(ir),\n timestamp: Date.now(),\n activeSteps: activeStepsCopy,\n };\n\n snapshots.push(snapshot);\n\n // Ring buffer: remove oldest if we exceed max\n if (snapshots.length > maxSnapshots) {\n snapshots.shift();\n }\n\n eventIndex++;\n }\n\n /**\n * Handle a workflow event and update the IR.\n */\n function handleEvent(event: WorkflowEvent<unknown>): void {\n switch (event.type) {\n case \"workflow_start\": {\n // New run: clear previous nodes and end-state so visualizations don't accumulate\n currentNodes = [];\n workflowEndTs = undefined;\n workflowDurationMs = undefined;\n workflowError = undefined;\n activeSteps.clear();\n scopeStack.length = 0;\n decisionStack.length = 0;\n activeStreams.clear();\n\n workflowId = event.workflowId;\n workflowStartTs = event.ts;\n workflowState = \"running\";\n createdAt = Date.now();\n lastUpdatedAt = createdAt;\n // Clear pre-start hooks from a previous run so a new run without hook events doesn't inherit them\n if (preStartHookWorkflowId !== undefined && preStartHookWorkflowId !== event.workflowId) {\n hookState.shouldRun = undefined;\n hookState.onBeforeStart = undefined;\n }\n preStartHookWorkflowId = event.workflowId;\n hookState.onAfterStep = new Map();\n break;\n }\n\n case \"workflow_success\":\n workflowState = \"success\";\n workflowEndTs = event.ts;\n workflowDurationMs = event.durationMs;\n lastUpdatedAt = Date.now();\n break;\n\n case \"workflow_error\":\n workflowState = \"error\";\n workflowEndTs = event.ts;\n workflowError = event.error;\n workflowDurationMs = event.durationMs;\n lastUpdatedAt = Date.now();\n break;\n\n case \"workflow_cancelled\":\n workflowState = \"aborted\";\n workflowEndTs = event.ts;\n workflowDurationMs = event.durationMs;\n lastUpdatedAt = Date.now();\n break;\n\n case \"step_start\": {\n const id = getStepId(event);\n activeSteps.set(id, {\n id,\n name: event.name,\n key: event.stepKey,\n startTs: event.ts,\n retryCount: 0,\n timedOut: false,\n });\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"step_success\": {\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n const node: StepNode = {\n type: \"step\",\n id: active.id,\n name: active.name,\n key: active.key,\n state: \"success\",\n startTs: active.startTs,\n endTs: event.ts,\n durationMs: event.durationMs,\n ...(active.retryCount > 0 && { retryCount: active.retryCount }),\n ...(active.timedOut && { timedOut: true, timeoutMs: active.timeoutMs }),\n };\n addNode(node);\n activeSteps.delete(id);\n }\n break;\n }\n\n case \"step_error\": {\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n const node: StepNode = {\n type: \"step\",\n id: active.id,\n name: active.name,\n key: active.key,\n state: \"error\",\n startTs: active.startTs,\n endTs: event.ts,\n durationMs: event.durationMs,\n error: event.error,\n ...(active.retryCount > 0 && { retryCount: active.retryCount }),\n ...(active.timedOut && { timedOut: true, timeoutMs: active.timeoutMs }),\n };\n addNode(node);\n activeSteps.delete(id);\n }\n break;\n }\n\n case \"step_aborted\": {\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n const node: StepNode = {\n type: \"step\",\n id: active.id,\n name: active.name,\n key: active.key,\n state: \"aborted\",\n startTs: active.startTs,\n endTs: event.ts,\n durationMs: event.durationMs,\n ...(active.retryCount > 0 && { retryCount: active.retryCount }),\n ...(active.timedOut && { timedOut: true, timeoutMs: active.timeoutMs }),\n };\n addNode(node);\n activeSteps.delete(id);\n }\n break;\n }\n\n case \"step_cache_hit\": {\n const id = getStepId(event);\n const node: StepNode = {\n type: \"step\",\n id,\n name: event.name,\n key: event.stepKey,\n state: \"cached\",\n startTs: event.ts,\n endTs: event.ts,\n durationMs: 0,\n };\n addNode(node);\n break;\n }\n\n case \"step_cache_miss\":\n // Cache miss just means the step will execute normally\n // We'll get a step_start event next\n break;\n\n case \"step_complete\":\n // step_complete is for state persistence, not visualization\n // We already handled the step via step_success/step_error\n break;\n\n case \"step_timeout\": {\n // Timeout is an intermediate event - step may retry or will get step_error\n // Track timeout info on the active step\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n active.timedOut = true;\n active.timeoutMs = event.timeoutMs;\n }\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"step_retry\": {\n // Retry is an intermediate event - increment retry counter\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n active.retryCount = (event.attempt ?? 1) - 1; // attempt is 1-indexed, retryCount is 0-indexed\n }\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"step_retries_exhausted\":\n // All retries exhausted - step_error will follow\n // The error state will be set by step_error handler\n lastUpdatedAt = Date.now();\n break;\n\n case \"step_skipped\": {\n const id = getStepId(event);\n const node: StepNode = {\n type: \"step\",\n id,\n name: event.name,\n key: event.stepKey,\n state: \"skipped\",\n startTs: event.ts,\n endTs: event.ts,\n durationMs: 0,\n };\n addNode(node);\n break;\n }\n\n // Hook events\n case \"hook_should_run\": {\n const hookExec: HookExecution = {\n type: \"shouldRun\",\n state: \"success\",\n ts: event.ts,\n durationMs: event.durationMs,\n context: {\n result: event.result,\n skipped: event.skipped,\n },\n };\n hookState.shouldRun = hookExec;\n preStartHookWorkflowId = event.workflowId;\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"hook_should_run_error\": {\n const hookExec: HookExecution = {\n type: \"shouldRun\",\n state: \"error\",\n ts: event.ts,\n durationMs: event.durationMs,\n error: event.error,\n };\n hookState.shouldRun = hookExec;\n preStartHookWorkflowId = event.workflowId;\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"hook_before_start\": {\n const hookExec: HookExecution = {\n type: \"onBeforeStart\",\n state: \"success\",\n ts: event.ts,\n durationMs: event.durationMs,\n context: {\n result: event.result,\n skipped: event.skipped,\n },\n };\n hookState.onBeforeStart = hookExec;\n preStartHookWorkflowId = event.workflowId;\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"hook_before_start_error\": {\n const hookExec: HookExecution = {\n type: \"onBeforeStart\",\n state: \"error\",\n ts: event.ts,\n durationMs: event.durationMs,\n error: event.error,\n };\n hookState.onBeforeStart = hookExec;\n preStartHookWorkflowId = event.workflowId;\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"hook_after_step\": {\n const hookExec: HookExecution = {\n type: \"onAfterStep\",\n state: \"success\",\n ts: event.ts,\n durationMs: event.durationMs,\n context: {\n stepKey: event.stepKey,\n },\n };\n hookState.onAfterStep.set(event.stepKey, hookExec);\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"hook_after_step_error\": {\n const hookExec: HookExecution = {\n type: \"onAfterStep\",\n state: \"error\",\n ts: event.ts,\n durationMs: event.durationMs,\n error: event.error,\n context: {\n stepKey: event.stepKey,\n },\n };\n hookState.onAfterStep.set(event.stepKey, hookExec);\n lastUpdatedAt = Date.now();\n break;\n }\n\n // Stream events\n case \"stream_created\": {\n const streamKey = `${event.workflowId}:${event.namespace}`;\n activeStreams.set(streamKey, {\n id: generateId(),\n namespace: event.namespace,\n startTs: event.ts,\n writeCount: 0,\n readCount: 0,\n streamState: \"active\",\n backpressureOccurred: false,\n finalPosition: 0,\n });\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"stream_write\": {\n const streamKey = `${event.workflowId}:${event.namespace}`;\n const stream = activeStreams.get(streamKey);\n if (stream) {\n stream.writeCount++;\n stream.finalPosition = Math.max(stream.finalPosition, event.position);\n }\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"stream_read\": {\n const streamKey = `${event.workflowId}:${event.namespace}`;\n const stream = activeStreams.get(streamKey);\n if (stream) {\n stream.readCount++;\n }\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"stream_close\": {\n const streamKey = `${event.workflowId}:${event.namespace}`;\n const stream = activeStreams.get(streamKey);\n if (stream) {\n stream.streamState = \"closed\";\n stream.finalPosition = event.finalPosition;\n // Create StreamNode and add to current nodes\n const node: StreamNode = {\n type: \"stream\",\n id: stream.id,\n namespace: stream.namespace,\n state: \"success\",\n startTs: stream.startTs,\n endTs: event.ts,\n durationMs: event.ts - stream.startTs,\n writeCount: stream.writeCount,\n readCount: stream.readCount,\n finalPosition: event.finalPosition,\n streamState: \"closed\",\n backpressureOccurred: stream.backpressureOccurred,\n };\n addNode(node);\n activeStreams.delete(streamKey);\n }\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"stream_error\": {\n const streamKey = `${event.workflowId}:${event.namespace}`;\n const stream = activeStreams.get(streamKey);\n if (stream) {\n stream.streamState = \"error\";\n // Create StreamNode with error state and add to current nodes\n const node: StreamNode = {\n type: \"stream\",\n id: stream.id,\n namespace: stream.namespace,\n state: \"error\",\n error: event.error,\n startTs: stream.startTs,\n endTs: event.ts,\n durationMs: event.ts - stream.startTs,\n writeCount: stream.writeCount,\n readCount: stream.readCount,\n finalPosition: event.position,\n streamState: \"error\",\n backpressureOccurred: stream.backpressureOccurred,\n };\n addNode(node);\n activeStreams.delete(streamKey);\n }\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"stream_backpressure\": {\n const streamKey = `${event.workflowId}:${event.namespace}`;\n const stream = activeStreams.get(streamKey);\n if (stream) {\n stream.backpressureOccurred = true;\n }\n lastUpdatedAt = Date.now();\n break;\n }\n }\n\n // Capture snapshot after processing event (for time-travel)\n captureSnapshot(event);\n }\n\n /**\n * Handle a scope event (parallel/race start/end).\n */\n function handleScopeEvent(event: ScopeStartEvent | ScopeEndEvent): void {\n if (event.type === \"scope_start\") {\n scopeStack.push({\n id: event.scopeId,\n name: event.name,\n type: event.scopeType,\n startTs: event.ts,\n children: [],\n });\n lastUpdatedAt = Date.now();\n } else if (event.type === \"scope_end\") {\n // Find the scope by ID (not just pop from top) to handle out-of-order events\n const scopeIndex = scopeStack.findIndex((s) => s.id === event.scopeId);\n if (scopeIndex === -1) {\n // Scope not found (already closed), ignore event\n return;\n }\n\n // Close all nested scopes first (those that started after this one)\n // Process in reverse order (innermost first) so each gets added to its parent\n while (scopeStack.length > scopeIndex + 1) {\n const nestedScope = scopeStack.pop()!;\n const nestedNode: ParallelNode | RaceNode =\n nestedScope.type === \"race\"\n ? {\n type: \"race\",\n id: nestedScope.id,\n name: nestedScope.name,\n state: deriveState(nestedScope.children),\n startTs: nestedScope.startTs,\n endTs: event.ts,\n children: nestedScope.children,\n }\n : {\n type: \"parallel\",\n id: nestedScope.id,\n name: nestedScope.name,\n state: deriveState(nestedScope.children),\n startTs: nestedScope.startTs,\n endTs: event.ts,\n children: nestedScope.children,\n mode: nestedScope.type === \"allSettled\" ? \"allSettled\" : \"all\",\n };\n // Add to the parent scope (which is now at the top of the remaining stack)\n scopeStack[scopeStack.length - 1].children.push(nestedNode);\n }\n\n // Now close the target scope\n const [scope] = scopeStack.splice(scopeIndex, 1);\n\n const node: ParallelNode | RaceNode =\n scope.type === \"race\"\n ? {\n type: \"race\",\n id: scope.id,\n name: scope.name,\n state: deriveState(scope.children),\n startTs: scope.startTs,\n endTs: event.ts,\n durationMs: event.durationMs,\n children: scope.children,\n winnerId: event.winnerId,\n }\n : {\n type: \"parallel\",\n id: scope.id,\n name: scope.name,\n state: deriveState(scope.children),\n startTs: scope.startTs,\n endTs: event.ts,\n durationMs: event.durationMs,\n children: scope.children,\n mode: scope.type === \"allSettled\" ? \"allSettled\" : \"all\",\n };\n addNode(node);\n }\n }\n\n /**\n * Handle a decision event (conditional branch start/branch/end).\n */\n function handleDecisionEvent(\n event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent\n ): void {\n if (event.type === \"decision_start\") {\n decisionStack.push({\n id: event.decisionId,\n name: event.name,\n condition: event.condition,\n decisionValue: event.decisionValue,\n startTs: event.ts,\n branches: new Map(),\n pendingChildren: [],\n });\n lastUpdatedAt = Date.now();\n } else if (event.type === \"decision_branch\") {\n // Match by decisionId so outer decisions receive branch events while inner is on stack\n const decision = decisionStack.find((d) => d.id === event.decisionId);\n if (decision) {\n // Find or create branch\n const branchKey = event.branchLabel;\n const existing = decision.branches.get(branchKey);\n if (existing) {\n // Update existing branch\n existing.taken = event.taken;\n // If this branch is now taken and there are pending children, transfer them\n if (event.taken && decision.pendingChildren.length > 0) {\n existing.children.unshift(...decision.pendingChildren);\n decision.pendingChildren = [];\n }\n } else {\n // Create new branch - if taken, include any pending children\n const children = event.taken ? [...decision.pendingChildren] : [];\n if (event.taken) {\n decision.pendingChildren = [];\n }\n decision.branches.set(branchKey, {\n label: event.branchLabel,\n condition: event.condition,\n taken: event.taken,\n children,\n });\n }\n lastUpdatedAt = Date.now();\n }\n } else if (event.type === \"decision_end\") {\n // Match by decisionId so out-of-order end (e.g. outer ends before inner) is handled\n const index = decisionStack.findIndex((d) => d.id === event.decisionId);\n if (index !== -1) {\n const [decision] = decisionStack.splice(index, 1);\n // Temporarily trim stack to parent scope so addNode attaches to the right place\n const toRestore = decisionStack.splice(index);\n\n // Convert branches map to array; if no branch events were emitted, put pendingChildren in a single synthetic branch\n let branches: DecisionBranch[] = Array.from(decision.branches.values());\n if (branches.length === 0 && decision.pendingChildren.length > 0) {\n branches = [\n {\n label: \"default\",\n taken: true,\n children: [...decision.pendingChildren],\n },\n ];\n }\n\n // Infer branchTaken from branches if not provided in event\n const inferredBranchTaken = branches.find((b) => b.taken)?.label;\n\n const node: DecisionNode = {\n type: \"decision\",\n id: decision.id,\n name: decision.name,\n state: deriveState(\n branches.flatMap((b) => (b.taken ? b.children : []))\n ),\n startTs: decision.startTs,\n endTs: event.ts,\n durationMs: event.durationMs,\n condition: decision.condition,\n decisionValue: decision.decisionValue,\n branchTaken: event.branchTaken ?? inferredBranchTaken,\n branches,\n };\n addNode(node);\n decisionStack.push(...toRestore);\n lastUpdatedAt = Date.now();\n }\n }\n }\n\n /**\n * Derive the state of a parent node from its children.\n */\n function deriveState(children: FlowNode[]): StepState {\n if (children.length === 0) return \"success\";\n\n const hasError = children.some((c) => c.state === \"error\");\n if (hasError) return \"error\";\n\n const allSuccess = children.every(\n (c) => c.state === \"success\" || c.state === \"cached\"\n );\n if (allSuccess) return \"success\";\n\n const hasRunning = children.some((c) => c.state === \"running\");\n if (hasRunning) return \"running\";\n\n return \"pending\";\n }\n\n /**\n * Get the current nodes including any active (running) steps and streams.\n */\n function getCurrentNodes(): FlowNode[] {\n const nodes = [...currentNodes];\n\n // Add active steps as running nodes\n for (const [, active] of activeSteps) {\n nodes.push({\n type: \"step\",\n id: active.id,\n name: active.name,\n key: active.key,\n state: \"running\",\n startTs: active.startTs,\n ...(active.retryCount > 0 && { retryCount: active.retryCount }),\n ...(active.timedOut && { timedOut: true, timeoutMs: active.timeoutMs }),\n });\n }\n\n // Add active streams as running nodes\n for (const [, stream] of activeStreams) {\n nodes.push({\n type: \"stream\",\n id: stream.id,\n namespace: stream.namespace,\n state: \"running\",\n startTs: stream.startTs,\n writeCount: stream.writeCount,\n readCount: stream.readCount,\n finalPosition: stream.finalPosition,\n streamState: stream.streamState,\n backpressureOccurred: stream.backpressureOccurred,\n } satisfies StreamNode);\n }\n\n return nodes;\n }\n\n /**\n * Build and return the current IR state.\n */\n function getIR(): WorkflowIR {\n let children = getCurrentNodes();\n\n // Apply parallel detection if enabled\n if (detectParallel) {\n children = detectParallelGroups(children, parallelDetection);\n }\n\n const root: WorkflowNode = {\n type: \"workflow\",\n id: workflowId ?? defaultWorkflowId,\n workflowId: workflowId ?? defaultWorkflowId,\n state: workflowState,\n startTs: workflowStartTs,\n endTs: workflowEndTs,\n durationMs: workflowDurationMs,\n children,\n error: workflowError,\n };\n\n // Include hooks if any have been recorded\n const hasHooks =\n hookState.shouldRun !== undefined ||\n hookState.onBeforeStart !== undefined ||\n hookState.onAfterStep.size > 0;\n\n return {\n root,\n metadata: {\n createdAt,\n lastUpdatedAt,\n },\n ...(hasHooks && { hooks: hookState }),\n };\n }\n\n /**\n * Reset the builder state.\n */\n function reset(): void {\n workflowId = undefined;\n workflowStartTs = undefined;\n workflowEndTs = undefined;\n workflowState = \"pending\";\n workflowError = undefined;\n workflowDurationMs = undefined;\n activeSteps.clear();\n scopeStack.length = 0;\n decisionStack.length = 0;\n activeStreams.clear();\n currentNodes = [];\n createdAt = Date.now();\n lastUpdatedAt = createdAt;\n // Clear hooks\n hookState = {\n onAfterStep: new Map(),\n };\n preStartHookWorkflowId = undefined;\n // Clear snapshots\n snapshots.length = 0;\n eventIndex = 0;\n }\n\n /**\n * Get all recorded snapshots.\n */\n function getSnapshots(): IRSnapshot[] {\n return [...snapshots];\n }\n\n /**\n * Get a snapshot at a specific index.\n */\n function getSnapshotAt(index: number): IRSnapshot | undefined {\n return snapshots[index];\n }\n\n /**\n * Get the IR state at a specific snapshot index.\n */\n function getIRAt(index: number): WorkflowIR | undefined {\n return snapshots[index]?.ir;\n }\n\n /**\n * Clear all recorded snapshots.\n */\n function clearSnapshots(): void {\n snapshots.length = 0;\n eventIndex = 0;\n }\n\n return {\n handleEvent,\n handleScopeEvent,\n handleDecisionEvent,\n getIR,\n reset,\n // Snapshot methods for time-travel\n getSnapshots,\n getSnapshotAt,\n getIRAt,\n clearSnapshots,\n /** Check if there are active (running) steps */\n get hasActiveSteps() {\n return activeSteps.size > 0;\n },\n /** Get the current workflow state */\n get state() {\n return workflowState;\n },\n /** Get the number of recorded snapshots */\n get snapshotCount() {\n return snapshots.length;\n },\n /** Check if snapshot recording is enabled */\n get snapshotsEnabled() {\n return enableSnapshots;\n },\n /** Enable or disable snapshot recording (e.g. for time-travel stop/start) */\n setSnapshotsEnabled(enabled: boolean): void {\n enableSnapshots = enabled;\n },\n };\n}\n\n/**\n * Type for the IR builder instance.\n */\nexport type IRBuilder = ReturnType<typeof createIRBuilder>;\n","/**\n * ASCII Terminal Renderer\n *\n * Renders the workflow IR as ASCII art with box-drawing characters\n * and ANSI colors for terminal display.\n */\n\nimport { ok, err, type Result } from \"awaitly\";\nimport type {\n FlowNode,\n ParallelNode,\n RaceNode,\n DecisionNode,\n StreamNode,\n Renderer,\n RenderOptions,\n StepNode,\n WorkflowIR,\n EnhancedRenderOptions,\n HeatLevel,\n WorkflowHooks,\n HookExecution,\n} from \"../types\";\nimport { isParallelNode, isRaceNode, isStepNode, isDecisionNode, isStreamNode } from \"../types\";\nimport { formatDuration } from \"../utils/timing\";\nimport {\n bold,\n colorByState,\n colorize,\n defaultColorScheme,\n dim,\n getColoredSymbol,\n stripAnsi,\n} from \"./colors\";\n\n/**\n * Error types for stringify operations.\n */\nexport type StringifyError = \"STRINGIFY_ERROR\";\n\n// =============================================================================\n// Box Drawing Characters\n// =============================================================================\n\nconst BOX = {\n topLeft: \"┌\",\n topRight: \"┐\",\n bottomLeft: \"└\",\n bottomRight: \"┘\",\n horizontal: \"─\",\n vertical: \"│\",\n teeRight: \"├\",\n teeLeft: \"┤\",\n teeDown: \"┬\",\n teeUp: \"┴\",\n cross: \"┼\",\n} as const;\n\n// =============================================================================\n// Heatmap Colors (ANSI)\n// =============================================================================\n\n/**\n * ANSI color codes for heatmap visualization.\n */\nconst HEAT_COLORS: Record<HeatLevel, string> = {\n cold: \"\\x1b[34m\", // Blue\n cool: \"\\x1b[36m\", // Cyan\n neutral: \"\", // Default (no color)\n warm: \"\\x1b[33m\", // Yellow\n hot: \"\\x1b[31m\", // Red\n critical: \"\\x1b[41m\", // Red background\n};\n\nconst RESET = \"\\x1b[0m\";\n\n/**\n * Get ANSI color code for a heat level.\n */\nfunction getHeatColor(heat: number): string {\n if (heat < 0.2) return HEAT_COLORS.cold;\n if (heat < 0.4) return HEAT_COLORS.cool;\n if (heat < 0.6) return HEAT_COLORS.neutral;\n if (heat < 0.8) return HEAT_COLORS.warm;\n if (heat < 0.95) return HEAT_COLORS.hot;\n return HEAT_COLORS.critical;\n}\n\n/**\n * Apply heat coloring to a string.\n */\nfunction applyHeatColor(text: string, heat: number): string {\n const color = getHeatColor(heat);\n if (!color) return text;\n return `${color}${text}${RESET}`;\n}\n\n/**\n * Safely stringify a value, handling circular references and BigInt.\n * Returns Result with either the stringified value or an error.\n */\nfunction safeStringify(value: unknown): Result<string, StringifyError> {\n try {\n const replacer = (_key: string, v: unknown): unknown => {\n if (typeof v !== \"bigint\") return v;\n const n = Number(v);\n return Number.isSafeInteger(n) ? n : v.toString();\n };\n return ok(JSON.stringify(value, replacer));\n } catch {\n return err(\"STRINGIFY_ERROR\");\n }\n}\n\n/**\n * Get stringified value or fallback for unserializable values.\n */\nfunction getStringified(value: unknown): string {\n const result = safeStringify(value);\n return result.ok ? result.value : \"[unserializable]\";\n}\n\n// =============================================================================\n// Sparkline Characters\n// =============================================================================\n\nconst SPARK_CHARS = \"▁▂▃▄▅▆▇█\";\n\n/**\n * Render a sparkline from an array of values.\n *\n * @param values Array of numeric values\n * @param width Maximum characters to use (default: 10)\n * @returns Sparkline string\n */\nexport function renderSparkline(values: number[], width = 10): string {\n if (values.length === 0) return \"\";\n\n // Take last N values\n const subset = values.slice(-width);\n const min = Math.min(...subset);\n const max = Math.max(...subset);\n const range = max - min || 1;\n\n return subset\n .map((v) => {\n const normalized = (v - min) / range;\n const index = Math.floor(normalized * (SPARK_CHARS.length - 1));\n return SPARK_CHARS[index];\n })\n .join(\"\");\n}\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Pad a string to a fixed width, accounting for ANSI codes.\n */\nfunction padEnd(str: string, width: number): string {\n const visibleLen = stripAnsi(str).length;\n const padding = Math.max(0, width - visibleLen);\n return str + \" \".repeat(padding);\n}\n\n/**\n * Create a horizontal line with optional title.\n */\nfunction horizontalLine(width: number, title?: string): string {\n if (!title) {\n return BOX.horizontal.repeat(width);\n }\n\n const titleText = ` ${title} `;\n // Use visible length (strip ANSI codes) for width calculation\n const visibleTitleLen = stripAnsi(titleText).length;\n const remainingWidth = width - visibleTitleLen;\n if (remainingWidth < 4) {\n return BOX.horizontal.repeat(width);\n }\n\n const leftPad = 2;\n const rightPad = remainingWidth - leftPad;\n\n return (\n BOX.horizontal.repeat(leftPad) + titleText + BOX.horizontal.repeat(rightPad)\n );\n}\n\n// =============================================================================\n// Hook Rendering\n// =============================================================================\n\n/**\n * Render a single hook execution.\n */\nfunction renderHookExecution(\n hook: HookExecution,\n label: string,\n colors: ReturnType<typeof Object.assign>\n): string {\n const symbol = hook.state === \"success\"\n ? colorize(\"⚙\", colors.success)\n : colorize(\"⚠\", colors.error);\n\n const timing = hook.durationMs !== undefined\n ? dim(` [${formatDuration(hook.durationMs)}]`)\n : \"\";\n\n let context = \"\";\n if (hook.type === \"shouldRun\" && hook.context?.skipped) {\n context = dim(\" → workflow skipped\");\n } else if (hook.type === \"shouldRun\" && hook.context?.result === true) {\n context = dim(\" → proceed\");\n } else if (hook.type === \"onBeforeStart\" && hook.context?.skipped) {\n context = dim(\" → workflow skipped\");\n } else if (hook.type === \"onAfterStep\" && hook.context?.stepKey) {\n context = dim(` (${hook.context.stepKey})`);\n }\n\n const error = hook.state === \"error\" && hook.error\n ? dim(` error: ${String(hook.error)}`)\n : \"\";\n\n return `${symbol} ${dim(label)}${context}${timing}${error}`;\n}\n\n/**\n * Render workflow hooks section.\n */\nfunction renderHooks(\n hooks: WorkflowHooks,\n colors: ReturnType<typeof Object.assign>\n): string[] {\n const lines: string[] = [];\n\n // Render shouldRun hook\n if (hooks.shouldRun) {\n lines.push(renderHookExecution(hooks.shouldRun, \"shouldRun\", colors));\n }\n\n // Render onBeforeStart hook\n if (hooks.onBeforeStart) {\n lines.push(renderHookExecution(hooks.onBeforeStart, \"onBeforeStart\", colors));\n }\n\n // We don't render onAfterStep hooks here - they're shown inline with steps\n // But if there are any, add a separator\n if (lines.length > 0) {\n lines.push(dim(\"────────────────────\")); // Separator between hooks and steps\n }\n\n return lines;\n}\n\n// =============================================================================\n// ASCII Renderer\n// =============================================================================\n\n/**\n * Create the ASCII terminal renderer.\n */\nexport function asciiRenderer(): Renderer {\n return {\n name: \"ascii\",\n supportsLive: true,\n\n render(ir: WorkflowIR, options: RenderOptions): string {\n const colors = { ...defaultColorScheme, ...options.colors };\n // Ensure minimum width to prevent negative repeat counts\n const width = Math.max(options.terminalWidth ?? 60, 5);\n const innerWidth = width - 4; // Account for borders\n\n const lines: string[] = [];\n\n // Header\n const workflowName = ir.root.name ?? \"workflow\";\n const headerTitle = bold(workflowName);\n lines.push(\n `${BOX.topLeft}${horizontalLine(width - 2, headerTitle)}${BOX.topRight}`\n );\n lines.push(`${BOX.vertical}${\" \".repeat(width - 2)}${BOX.vertical}`);\n\n // Render hooks (if any)\n if (ir.hooks) {\n const hookLines = renderHooks(ir.hooks, colors);\n for (const line of hookLines) {\n lines.push(\n `${BOX.vertical} ${padEnd(line, innerWidth)}${BOX.vertical}`\n );\n }\n }\n\n // Render children\n const childLines = renderNodes(ir.root.children, options, colors, 0, ir.hooks);\n for (const line of childLines) {\n lines.push(\n `${BOX.vertical} ${padEnd(line, innerWidth)}${BOX.vertical}`\n );\n }\n\n // Footer with timing\n lines.push(`${BOX.vertical}${\" \".repeat(width - 2)}${BOX.vertical}`);\n\n if (ir.root.durationMs !== undefined && options.showTimings) {\n const status =\n ir.root.state === \"success\"\n ? \"Completed\"\n : ir.root.state === \"aborted\"\n ? \"Cancelled\"\n : \"Failed\";\n const statusColored = colorByState(status, ir.root.state, colors);\n const footer = `${statusColored} in ${formatDuration(ir.root.durationMs)}`;\n lines.push(\n `${BOX.vertical} ${padEnd(footer, innerWidth)}${BOX.vertical}`\n );\n lines.push(`${BOX.vertical}${\" \".repeat(width - 2)}${BOX.vertical}`);\n }\n\n lines.push(\n `${BOX.bottomLeft}${BOX.horizontal.repeat(width - 2)}${BOX.bottomRight}`\n );\n\n return lines.join(\"\\n\");\n },\n };\n}\n\n/**\n * Render a list of nodes.\n */\nfunction renderNodes(\n nodes: FlowNode[],\n options: RenderOptions,\n colors: ReturnType<typeof Object.assign>,\n depth: number,\n hooks?: WorkflowHooks\n): string[] {\n const lines: string[] = [];\n\n for (const node of nodes) {\n if (isStepNode(node)) {\n lines.push(renderStepNode(node, options, colors, hooks));\n } else if (isParallelNode(node)) {\n lines.push(...renderParallelNode(node, options, colors, depth, hooks));\n } else if (isRaceNode(node)) {\n lines.push(...renderRaceNode(node, options, colors, depth, hooks));\n } else if (isDecisionNode(node)) {\n lines.push(...renderDecisionNode(node, options, colors, depth, hooks));\n } else if (isStreamNode(node)) {\n lines.push(renderStreamNode(node, options, colors));\n }\n }\n\n return lines;\n}\n\n/**\n * Render a single step node.\n */\nfunction renderStepNode(\n node: StepNode,\n options: RenderOptions,\n colors: ReturnType<typeof Object.assign>,\n hooks?: WorkflowHooks\n): string {\n const symbol = getColoredSymbol(node.state, colors);\n const name = node.name ?? node.key ?? \"step\";\n\n // Check for enhanced options\n const enhanced = options as EnhancedRenderOptions;\n // Heatmap lookup order matches PerformanceAnalyzer: key ?? name ?? id\n const heat = enhanced.showHeatmap && enhanced.heatmapData\n ? enhanced.heatmapData.heat.get(node.key ?? \"\") ??\n enhanced.heatmapData.heat.get(node.name ?? \"\") ??\n enhanced.heatmapData.heat.get(node.id)\n : undefined;\n\n // Apply heat coloring or default state coloring\n let nameColored: string;\n if (heat !== undefined) {\n nameColored = applyHeatColor(name, heat);\n } else {\n nameColored = colorByState(name, node.state, colors);\n }\n\n let line = `${symbol} ${nameColored}`;\n\n // Add key if requested (only when step has a name, so key is not already the label)\n if (options.showKeys && node.key && node.name) {\n line += dim(` [key: ${node.key}]`);\n }\n\n // Add input/output if available (for decision understanding)\n if (node.input !== undefined) {\n const inputStr = typeof node.input === \"string\"\n ? node.input\n : getStringified(node.input).slice(0, 30);\n line += dim(` [in: ${inputStr}${inputStr.length >= 30 ? \"...\" : \"\"}]`);\n }\n if (node.output !== undefined && node.state === \"success\") {\n const outputStr = typeof node.output === \"string\"\n ? node.output\n : getStringified(node.output).slice(0, 30);\n line += dim(` [out: ${outputStr}${outputStr.length >= 30 ? \"...\" : \"\"}]`);\n }\n\n // Add timing if available and requested\n if (options.showTimings && node.durationMs !== undefined) {\n // Apply heat coloring to timing if enabled\n const timingStr = formatDuration(node.durationMs);\n const timingDisplay = heat !== undefined\n ? applyHeatColor(`[${timingStr}]`, heat)\n : dim(`[${timingStr}]`);\n line += ` ${timingDisplay}`;\n }\n\n // Add sparkline if enabled and history available (lookup order: key ?? name ?? id, like analyzer)\n if (enhanced.showSparklines && enhanced.timingHistory) {\n const history =\n enhanced.timingHistory.get(node.key ?? \"\") ??\n enhanced.timingHistory.get(node.name ?? \"\") ??\n enhanced.timingHistory.get(node.id);\n if (history && history.length > 1) {\n line += ` ${dim(renderSparkline(history))}`;\n }\n }\n\n // Add retry indicator if retries occurred\n if (node.retryCount !== undefined && node.retryCount > 0) {\n line += dim(` [${node.retryCount} ${node.retryCount === 1 ? \"retry\" : \"retries\"}]`);\n }\n\n // Add timeout indicator if step timed out\n if (node.timedOut) {\n const timeoutInfo = node.timeoutMs !== undefined ? ` ${node.timeoutMs}ms` : \"\";\n line += dim(` [timeout${timeoutInfo}]`);\n }\n\n // Add onAfterStep hook indicator if present (check by key first, then by id)\n const hookKey = node.key ?? node.id;\n if (hooks && hookKey && hooks.onAfterStep.has(hookKey)) {\n const hookExec = hooks.onAfterStep.get(hookKey)!;\n const hookSymbol = hookExec.state === \"success\"\n ? colorize(\"⚙\", colors.success)\n : colorize(\"⚠\", colors.error);\n const hookTiming = hookExec.durationMs !== undefined\n ? dim(` ${formatDuration(hookExec.durationMs)}`)\n : \"\";\n line += ` ${hookSymbol}${hookTiming}`;\n }\n\n return line;\n}\n\n/**\n * Render a stream node.\n */\nfunction renderStreamNode(\n node: StreamNode,\n options: RenderOptions,\n colors: ReturnType<typeof Object.assign>\n): string {\n // Use stream-specific symbol\n const stateSymbol = node.streamState === \"active\"\n ? colorize(\"⟳\", colors.running)\n : node.streamState === \"closed\"\n ? colorize(\"✓\", colors.success)\n : colorize(\"✗\", colors.error);\n\n const name = `stream:${node.namespace}`;\n const nameColored = colorByState(name, node.state, colors);\n\n // Show write/read counts\n const counts = dim(`[W:${node.writeCount} R:${node.readCount}]`);\n\n let line = `${stateSymbol} ${nameColored} ${counts}`;\n\n // Add timing if available and requested\n if (options.showTimings && node.durationMs !== undefined) {\n line += ` ${dim(`[${formatDuration(node.durationMs)}]`)}`;\n }\n\n // Add backpressure indicator if occurred\n if (node.backpressureOccurred) {\n line += dim(\" [backpressure]\");\n }\n\n // Add final position\n if (node.streamState === \"closed\") {\n line += dim(` pos:${node.finalPosition}`);\n }\n\n return line;\n}\n\n/**\n * Render a parallel node (allAsync).\n */\nfunction renderParallelNode(\n node: ParallelNode,\n options: RenderOptions,\n colors: ReturnType<typeof Object.assign>,\n depth: number,\n hooks?: WorkflowHooks\n): string[] {\n const lines: string[] = [];\n const indent = \" \".repeat(depth);\n\n // Header\n const symbol = getColoredSymbol(node.state, colors);\n const name = node.name ?? \"parallel\";\n const mode = node.mode === \"allSettled\" ? \" (allSettled)\" : \"\";\n lines.push(`${indent}${BOX.teeRight}${BOX.teeDown}${BOX.horizontal} ${symbol} ${bold(name)}${mode}`);\n\n // Children\n if (node.children.length === 0) {\n // Empty parallel scope - operations inside allAsync/anyAsync weren't tracked as steps\n lines.push(`${indent}${BOX.vertical} ${dim(\"(operations not individually tracked)\")}`);\n lines.push(`${indent}${BOX.vertical} ${dim(\"(wrap each operation with step() to see individual steps)\")}`);\n } else {\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n const isLast = i === node.children.length - 1;\n const prefix = isLast ? `${indent}${BOX.vertical} ${BOX.bottomLeft}` : `${indent}${BOX.vertical} ${BOX.teeRight}`;\n\n if (isStepNode(child)) {\n lines.push(`${prefix} ${renderStepNode(child, options, colors, hooks)}`);\n } else {\n // Nested structure - recurse\n const nestedLines = renderNodes([child], options, colors, depth + 1, hooks);\n for (const line of nestedLines) {\n lines.push(`${indent}${BOX.vertical} ${line}`);\n }\n }\n }\n }\n\n // Timing footer\n if (options.showTimings && node.durationMs !== undefined) {\n lines.push(`${indent}${BOX.bottomLeft}${BOX.horizontal}${BOX.horizontal} ${dim(`[${formatDuration(node.durationMs)}]`)}`);\n }\n\n return lines;\n}\n\n/**\n * Render a race node (anyAsync).\n */\nfunction renderRaceNode(\n node: RaceNode,\n options: RenderOptions,\n colors: ReturnType<typeof Object.assign>,\n depth: number,\n hooks?: WorkflowHooks\n): string[] {\n const lines: string[] = [];\n const indent = \" \".repeat(depth);\n\n // Header with lightning bolt for race\n const symbol = getColoredSymbol(node.state, colors);\n const name = node.name ?? \"race\";\n lines.push(`${indent}${BOX.teeRight}⚡ ${symbol} ${bold(name)}`);\n\n // Children\n if (node.children.length === 0) {\n // Empty race scope - operations inside anyAsync weren't tracked as steps\n lines.push(`${indent}${BOX.vertical} ${dim(\"(operations not individually tracked)\")}`);\n lines.push(`${indent}${BOX.vertical} ${dim(\"(wrap each operation with step() to see individual steps)\")}`);\n } else {\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n const isLast = i === node.children.length - 1;\n const prefix = isLast ? `${indent}${BOX.vertical} ${BOX.bottomLeft}` : `${indent}${BOX.vertical} ${BOX.teeRight}`;\n\n // Mark winner\n const isWinner = node.winnerId && child.id === node.winnerId;\n const winnerSuffix = isWinner ? dim(\" (winner)\") : \"\";\n\n if (isStepNode(child)) {\n lines.push(`${prefix} ${renderStepNode(child, options, colors, hooks)}${winnerSuffix}`);\n } else {\n const nestedLines = renderNodes([child], options, colors, depth + 1, hooks);\n for (const line of nestedLines) {\n lines.push(`${indent}${BOX.vertical} ${line}`);\n }\n }\n }\n }\n\n // Timing footer\n if (options.showTimings && node.durationMs !== undefined) {\n lines.push(`${indent}${BOX.bottomLeft}${BOX.horizontal}${BOX.horizontal} ${dim(`[${formatDuration(node.durationMs)}]`)}`);\n }\n\n return lines;\n}\n\n/**\n * Render a decision node (conditional branch).\n */\nfunction renderDecisionNode(\n node: DecisionNode,\n options: RenderOptions,\n colors: ReturnType<typeof Object.assign>,\n depth: number,\n hooks?: WorkflowHooks\n): string[] {\n const lines: string[] = [];\n const indent = \" \".repeat(depth);\n\n // Header with decision info\n const symbol = getColoredSymbol(node.state, colors);\n const name = node.name ?? \"decision\";\n const condition = node.condition\n ? dim(` (${node.condition})`)\n : \"\";\n const decisionValue = node.decisionValue !== undefined\n ? dim(` = ${String(node.decisionValue)}`)\n : \"\";\n const branchTaken = node.branchTaken !== undefined\n ? dim(` → ${String(node.branchTaken)}`)\n : \"\";\n\n lines.push(\n `${indent}${BOX.teeRight}${BOX.teeDown}${BOX.horizontal} ${symbol} ${bold(name)}${condition}${decisionValue}${branchTaken}`\n );\n\n // Render branches\n for (let i = 0; i < node.branches.length; i++) {\n const branch = node.branches[i];\n const isLast = i === node.branches.length - 1;\n const prefix = isLast\n ? `${indent}${BOX.vertical} ${BOX.bottomLeft}`\n : `${indent}${BOX.vertical} ${BOX.teeRight}`;\n\n // Branch label with taken/skipped indicator\n const branchSymbol = branch.taken ? \"✓\" : \"⊘\";\n const branchColor = branch.taken ? colors.success : colors.skipped;\n const branchLabel = colorize(\n `${branchSymbol} ${branch.label}`,\n branchColor\n );\n const branchCondition = branch.condition\n ? dim(` (${branch.condition})`)\n : \"\";\n\n lines.push(`${prefix} ${branchLabel}${branchCondition}`);\n\n // Render children of this branch\n if (branch.children.length > 0) {\n const childLines = renderNodes(branch.children, options, colors, depth + 1, hooks);\n for (const line of childLines) {\n lines.push(`${indent}${BOX.vertical} ${line}`);\n }\n } else if (!branch.taken) {\n // Show that this branch was skipped\n lines.push(\n `${indent}${BOX.vertical} ${dim(\"(skipped)\")}`\n );\n }\n }\n\n // Timing footer\n if (options.showTimings && node.durationMs !== undefined) {\n lines.push(\n `${indent}${BOX.bottomLeft}${BOX.horizontal}${BOX.horizontal} ${dim(`[${formatDuration(node.durationMs)}]`)}`\n );\n }\n\n return lines;\n}\n\nexport { defaultColorScheme };\n","/**\n * Workflow Visualization - Intermediate Representation Types\n *\n * The IR (Intermediate Representation) is a DSL that represents workflow\n * execution structure. Events are converted to IR, which can then be\n * rendered to various output formats (ASCII, Mermaid, JSON, etc.).\n */\n\n// =============================================================================\n// Step States\n// =============================================================================\n\n/**\n * Execution state of a step with semantic meaning for visualization.\n *\n * Color mapping:\n * - pending → white/clear (not started)\n * - running → yellow (currently executing)\n * - success → green (completed successfully)\n * - error → red (failed with error)\n * - aborted → gray (cancelled, e.g., in race)\n * - cached → blue (served from cache)\n * - skipped → dim gray (not executed due to conditional logic)\n */\nexport type StepState =\n | \"pending\"\n | \"running\"\n | \"success\"\n | \"error\"\n | \"aborted\"\n | \"cached\"\n | \"skipped\";\n\n// =============================================================================\n// Node Types\n// =============================================================================\n\n/**\n * Base properties shared by all IR nodes.\n */\nexport interface BaseNode {\n /** Unique identifier for this node */\n id: string;\n /** Human-readable name (from step options or inferred) */\n name?: string;\n /** Cache key if this is a keyed step */\n key?: string;\n /** Current execution state */\n state: StepState;\n /** Timestamp when execution started */\n startTs?: number;\n /** Timestamp when execution ended */\n endTs?: number;\n /** Duration in milliseconds */\n durationMs?: number;\n /** Error value if state is 'error' */\n error?: unknown;\n /** Input value that triggered this step (for decision understanding) */\n input?: unknown;\n /** Output value from this step (for decision understanding) */\n output?: unknown;\n /** Number of retry attempts made (0 = no retries, 1 = one retry, etc.) */\n retryCount?: number;\n /** Whether this step experienced a timeout (may have retried after) */\n timedOut?: boolean;\n /** Timeout duration in ms (if timed out) */\n timeoutMs?: number;\n}\n\n/**\n * A single step execution node.\n */\nexport interface StepNode extends BaseNode {\n type: \"step\";\n}\n\n/**\n * Sequential execution - steps run one after another.\n * This is the implicit structure when steps are awaited in sequence.\n */\nexport interface SequenceNode extends BaseNode {\n type: \"sequence\";\n children: FlowNode[];\n}\n\n/**\n * Parallel execution - all branches run simultaneously.\n * Created by allAsync() or allSettledAsync().\n */\nexport interface ParallelNode extends BaseNode {\n type: \"parallel\";\n children: FlowNode[];\n /**\n * Execution mode:\n * - 'all': Fails on first error (allAsync)\n * - 'allSettled': Collects all results (allSettledAsync)\n */\n mode: \"all\" | \"allSettled\";\n}\n\n/**\n * Race execution - first to complete wins.\n * Created by anyAsync().\n */\nexport interface RaceNode extends BaseNode {\n type: \"race\";\n children: FlowNode[];\n /** ID of the winning branch (first to succeed) */\n winnerId?: string;\n}\n\n/**\n * Stream operation node.\n * Tracks streaming events (write, read, close, error, backpressure).\n */\nexport interface StreamNode extends BaseNode {\n type: \"stream\";\n /** Stream namespace identifier */\n namespace: string;\n /** Total number of write operations */\n writeCount: number;\n /** Total number of read operations */\n readCount: number;\n /** Final position when stream closed */\n finalPosition: number;\n /** Current stream state */\n streamState: \"active\" | \"closed\" | \"error\";\n /** Whether backpressure was encountered during streaming */\n backpressureOccurred: boolean;\n}\n\n/**\n * Decision point - conditional branch (if/switch).\n * Shows which branch was taken and why.\n */\nexport interface DecisionNode extends BaseNode {\n type: \"decision\";\n /** Condition that was evaluated (e.g., \"user.role === 'admin'\") */\n condition?: string;\n /** Value that was evaluated (the input to the decision) */\n decisionValue?: unknown;\n /** Which branch was taken (true/false, or the matched case) */\n branchTaken?: string | boolean;\n /** All possible branches (including skipped ones) */\n branches: DecisionBranch[];\n}\n\n/**\n * A branch in a decision node.\n */\nexport interface DecisionBranch {\n /** Label for this branch (e.g., \"if\", \"else\", \"case 'admin'\") */\n label: string;\n /** Condition that would trigger this branch */\n condition?: string;\n /** Whether this branch was taken */\n taken: boolean;\n /** Steps in this branch */\n children: FlowNode[];\n}\n\n/**\n * Union of all flow node types.\n */\nexport type FlowNode = StepNode | SequenceNode | ParallelNode | RaceNode | DecisionNode | StreamNode;\n\n/**\n * Root node representing the entire workflow.\n */\nexport interface WorkflowNode extends BaseNode {\n type: \"workflow\";\n /** Correlation ID from the workflow execution */\n workflowId: string;\n /** Child nodes (steps, parallel blocks, etc.) */\n children: FlowNode[];\n}\n\n// =============================================================================\n// Workflow IR\n// =============================================================================\n\n/**\n * Complete workflow intermediate representation.\n * This is the main data structure produced by the IR builder.\n */\nexport interface WorkflowIR {\n /** Root workflow node */\n root: WorkflowNode;\n /** Metadata about the IR */\n metadata: {\n /** When the IR was first created */\n createdAt: number;\n /** When the IR was last updated */\n lastUpdatedAt: number;\n };\n /** Hook executions (if any hooks are configured) */\n hooks?: WorkflowHooks;\n}\n\n// =============================================================================\n// Scope Events (for parallel/race detection)\n// =============================================================================\n\n// Re-export ScopeType from awaitly for consistency\nexport type { ScopeType } from \"awaitly/workflow\";\nimport type { ScopeType, WorkflowEvent, WorkflowOptions } from \"awaitly/workflow\";\nimport type { UnexpectedError } from \"awaitly/core\";\n\n/**\n * Event emitted when entering a parallel/race scope.\n * This matches the scope_start event in WorkflowEvent.\n */\nexport interface ScopeStartEvent {\n type: \"scope_start\";\n workflowId: string;\n scopeId: string;\n scopeType: ScopeType;\n name?: string;\n ts: number;\n}\n\n/**\n * Event emitted when exiting a parallel/race scope.\n */\nexport interface ScopeEndEvent {\n type: \"scope_end\";\n workflowId: string;\n scopeId: string;\n ts: number;\n durationMs: number;\n /** For race scopes, the ID of the winning branch */\n winnerId?: string;\n}\n\n/**\n * Event emitted when a decision point is encountered.\n * Use this to track conditional logic (if/switch).\n */\nexport interface DecisionStartEvent {\n type: \"decision_start\";\n workflowId: string;\n decisionId: string;\n /** Condition being evaluated (e.g., \"user.role === 'admin'\") */\n condition?: string;\n /** Value being evaluated */\n decisionValue?: unknown;\n /** Name/label for this decision point */\n name?: string;\n ts: number;\n}\n\n/**\n * Event emitted when a decision branch is taken.\n */\nexport interface DecisionBranchEvent {\n type: \"decision_branch\";\n workflowId: string;\n decisionId: string;\n /** Label for this branch (e.g., \"if\", \"else\", \"case 'admin'\") */\n branchLabel: string;\n /** Condition for this branch */\n condition?: string;\n /** Whether this branch was taken */\n taken: boolean;\n ts: number;\n}\n\n/**\n * Event emitted when a decision point completes.\n */\nexport interface DecisionEndEvent {\n type: \"decision_end\";\n workflowId: string;\n decisionId: string;\n /** Which branch was taken */\n branchTaken?: string | boolean;\n ts: number;\n durationMs: number;\n}\n\n/**\n * Event emitted when a step is skipped due to conditional logic.\n */\nexport interface StepSkippedEvent {\n type: \"step_skipped\";\n workflowId: string;\n stepKey?: string;\n name?: string;\n /** Reason why this step was skipped (e.g., \"condition was false\") */\n reason?: string;\n /** The decision that caused this skip */\n decisionId?: string;\n ts: number;\n}\n\n/**\n * Union of scope-related events.\n */\nexport type ScopeEvent = ScopeStartEvent | ScopeEndEvent;\n\n/**\n * Union of decision-related events.\n */\nexport type DecisionEvent = DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent;\n\n// =============================================================================\n// Renderer Types\n// =============================================================================\n\n/**\n * Color scheme for rendering step states.\n */\nexport interface ColorScheme {\n pending: string;\n running: string;\n success: string;\n error: string;\n aborted: string;\n cached: string;\n skipped: string;\n}\n\n/**\n * Options passed to renderers.\n */\nexport interface RenderOptions {\n /** Show timing information (duration) */\n showTimings: boolean;\n /** Show step cache keys */\n showKeys: boolean;\n /** Terminal width for ASCII renderer */\n terminalWidth?: number;\n /** Color scheme */\n colors: ColorScheme;\n}\n\n/**\n * Extended options for Mermaid renderer.\n * Controls how edges are displayed for retries, errors, and timeouts.\n */\nexport interface MermaidRenderOptions extends RenderOptions {\n /** Show retry as self-loop edge (default: true) */\n showRetryEdges?: boolean;\n /** Show error flow to error node (default: true) */\n showErrorEdges?: boolean;\n /** Show timeout as alternative path (default: true) */\n showTimeoutEdges?: boolean;\n}\n\n/**\n * Renderer interface - transforms IR to output format.\n */\nexport interface Renderer {\n /** Unique identifier for this renderer */\n readonly name: string;\n /** Render IR to string output */\n render(ir: WorkflowIR, options: RenderOptions): string;\n /** Whether this renderer supports live (incremental) updates */\n supportsLive?: boolean;\n /** Render incremental update (optional) */\n renderUpdate?(\n ir: WorkflowIR,\n changedNodes: FlowNode[],\n options: RenderOptions\n ): string;\n}\n\n// =============================================================================\n// Visualizer Types\n// =============================================================================\n\n/**\n * Output format for rendering.\n */\nexport type OutputFormat = \"ascii\" | \"mermaid\" | \"json\" | \"logger\" | \"flowchart\";\n\n/**\n * Options for creating a visualizer.\n */\nexport interface VisualizerOptions {\n /** Name for the workflow in visualizations */\n workflowName?: string;\n /** Enable parallel detection heuristics (default: true) */\n detectParallel?: boolean;\n /** Show timing information (default: true) */\n showTimings?: boolean;\n /** Show step keys (default: false) */\n showKeys?: boolean;\n /** Custom color scheme */\n colors?: Partial<ColorScheme>;\n /**\n * Export configuration for URL generation methods.\n * Note: Treated as immutable after creation - do not mutate.\n */\n export?: {\n /** Default export provider (opt-in). If not set, export methods require explicit provider. */\n default?: ExportOptions;\n };\n}\n\n/**\n * Options for createVisualizingWorkflow convenience factory.\n * Combines WorkflowOptions with VisualizerOptions.\n *\n * @example\n * ```typescript\n * const { workflow, visualizer } = createVisualizingWorkflow(deps, {\n * workflowName: 'checkout',\n * showTimings: true,\n * forwardTo: (event) => console.log(event.type),\n * });\n * ```\n */\nexport interface VisualizingWorkflowOptions<E, C = void>\n extends Omit<WorkflowOptions<E, C>, \"onEvent\">,\n VisualizerOptions {\n /** Forward events to additional handler (runs after visualization) */\n forwardTo?: (event: WorkflowEvent<E | UnexpectedError, C>, ctx: C) => void;\n}\n\n/**\n * Options for live visualization.\n */\nexport interface LiveVisualizerOptions extends VisualizerOptions {\n /** Output stream (default: process.stdout) */\n stream?: NodeJS.WriteStream;\n /** Update interval in ms (default: 100) */\n updateInterval?: number;\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Check if a node is a StepNode.\n */\nexport function isStepNode(node: FlowNode): node is StepNode {\n return node.type === \"step\";\n}\n\n/**\n * Check if a node is a SequenceNode.\n */\nexport function isSequenceNode(node: FlowNode): node is SequenceNode {\n return node.type === \"sequence\";\n}\n\n/**\n * Check if a node is a ParallelNode.\n */\nexport function isParallelNode(node: FlowNode): node is ParallelNode {\n return node.type === \"parallel\";\n}\n\n/**\n * Check if a node is a RaceNode.\n */\nexport function isRaceNode(node: FlowNode): node is RaceNode {\n return node.type === \"race\";\n}\n\n/**\n * Check if a node is a DecisionNode.\n */\nexport function isDecisionNode(node: FlowNode): node is DecisionNode {\n return node.type === \"decision\";\n}\n\n/**\n * Check if a node is a StreamNode.\n */\nexport function isStreamNode(node: FlowNode): node is StreamNode {\n return node.type === \"stream\";\n}\n\n/**\n * Check if a node has children.\n */\nexport function hasChildren(\n node: FlowNode\n): node is SequenceNode | ParallelNode | RaceNode | DecisionNode {\n return \"children\" in node || (node.type === \"decision\" && \"branches\" in node);\n}\n\n// =============================================================================\n// Time Travel Types\n// =============================================================================\n\n/**\n * Snapshot of an active step's state at a point in time.\n */\nexport interface ActiveStepSnapshot {\n id: string;\n name?: string;\n key?: string;\n startTs: number;\n retryCount: number;\n timedOut: boolean;\n timeoutMs?: number;\n}\n\n/**\n * A snapshot of the complete IR state at a specific point in time.\n * Used for time-travel debugging - each event creates a snapshot.\n */\nexport interface IRSnapshot {\n /** Unique identifier for this snapshot */\n id: string;\n /** Index in the event sequence (0-based) */\n eventIndex: number;\n /** The event that triggered this snapshot */\n event: unknown; // WorkflowEvent - avoid circular import\n /** Complete IR state at this moment */\n ir: WorkflowIR;\n /** Timestamp when snapshot was taken */\n timestamp: number;\n /** Active step states at this moment (for debugging) */\n activeSteps: Map<string, ActiveStepSnapshot>;\n}\n\n/**\n * State of the time-travel controller.\n */\nexport interface TimeTravelState {\n /** All recorded snapshots */\n snapshots: IRSnapshot[];\n /** Current snapshot index (for playback position) */\n currentIndex: number;\n /** Whether playback is active */\n isPlaying: boolean;\n /** Playback speed multiplier (1.0 = realtime, 2.0 = 2x speed) */\n playbackSpeed: number;\n /** Whether recording is active */\n isRecording: boolean;\n}\n\n// =============================================================================\n// Performance Analysis Types\n// =============================================================================\n\n/**\n * Performance metrics for a single node across multiple runs.\n */\nexport interface NodePerformance {\n /** Node identifier (name or step ID) */\n nodeId: string;\n /** Average duration across all samples */\n avgDurationMs: number;\n /** Minimum duration observed */\n minDurationMs: number;\n /** Maximum duration observed */\n maxDurationMs: number;\n /** Standard deviation of durations */\n stdDevMs: number;\n /** Number of timing samples collected */\n samples: number;\n /** Retry frequency (0-1, where 1 = always retries) */\n retryRate: number;\n /** Timeout frequency (0-1) */\n timeoutRate: number;\n /** Error rate (0-1) */\n errorRate: number;\n /** Percentile data for distribution analysis */\n percentiles: {\n p50: number;\n p90: number;\n p95: number;\n p99: number;\n };\n}\n\n/**\n * Heatmap data for visualizing performance across nodes.\n */\nexport interface HeatmapData {\n /** Map of node ID to heat level (0-1, where 1 is hottest/slowest) */\n heat: Map<string, number>;\n /** The metric used for heat calculation */\n metric: \"duration\" | \"retryRate\" | \"errorRate\";\n /** Statistics used to compute heat values */\n stats: {\n /** Minimum value in the dataset */\n min: number;\n /** Maximum value in the dataset */\n max: number;\n /** Mean value */\n mean: number;\n /** Threshold above which a node is considered \"hot\" */\n threshold: number;\n };\n}\n\n/**\n * Heat level for visual styling.\n */\nexport type HeatLevel = \"cold\" | \"cool\" | \"neutral\" | \"warm\" | \"hot\" | \"critical\";\n\n// =============================================================================\n// HTML Renderer Types\n// =============================================================================\n\n/**\n * Theme for the HTML visualizer.\n */\nexport type HTMLTheme = \"light\" | \"dark\" | \"auto\";\n\n/**\n * Layout direction for the workflow diagram.\n */\nexport type LayoutDirection = \"TB\" | \"LR\" | \"BT\" | \"RL\";\n\n/**\n * Options for the HTML renderer.\n */\nexport interface HTMLRenderOptions extends RenderOptions {\n /** Enable interactive features (click to inspect, zoom/pan) */\n interactive: boolean;\n /** Include time-travel controls */\n timeTravel: boolean;\n /** Include performance heatmap overlay */\n heatmap: boolean;\n /** Animation duration for transitions (ms) */\n animationDuration: number;\n /** Color theme */\n theme: HTMLTheme;\n /** Diagram layout direction */\n layout: LayoutDirection;\n /** Heatmap data (if heatmap is enabled) */\n heatmapData?: HeatmapData;\n /** WebSocket URL for live updates (if streaming) */\n wsUrl?: string;\n}\n\n/**\n * Message sent from the web visualizer to the dev server.\n */\nexport interface WebVisualizerMessage {\n type:\n | \"time_travel_seek\"\n | \"time_travel_play\"\n | \"time_travel_pause\"\n | \"time_travel_step_forward\"\n | \"time_travel_step_backward\"\n | \"request_snapshots\"\n | \"toggle_heatmap\"\n | \"set_heatmap_metric\";\n payload?: unknown;\n}\n\n/**\n * Message sent from the dev server to the web visualizer.\n */\nexport interface ServerMessage {\n type:\n | \"ir_update\"\n | \"snapshot\"\n | \"snapshots_list\"\n | \"performance_data\"\n | \"workflow_complete\"\n | \"time_travel_state\";\n payload: unknown;\n}\n\n// =============================================================================\n// Enhanced ASCII Renderer Types\n// =============================================================================\n\n/**\n * Extended render options for the enhanced ASCII renderer.\n */\nexport interface EnhancedRenderOptions extends RenderOptions {\n /** Show performance heatmap coloring */\n showHeatmap?: boolean;\n /** Heatmap data for coloring nodes */\n heatmapData?: HeatmapData;\n /** Show timing sparklines (requires historical data) */\n showSparklines?: boolean;\n /** Historical timing data for sparklines: nodeId → array of durations */\n timingHistory?: Map<string, number[]>;\n}\n\n/**\n * Options for the flowchart ASCII renderer.\n * Renders workflow as a proper flowchart with boxes and arrows.\n */\nexport interface FlowchartRenderOptions extends EnhancedRenderOptions {\n /** Show start and end nodes (default: true) */\n showStartEnd?: boolean;\n /** Reduce vertical spacing between nodes (default: false) */\n compact?: boolean;\n /** Box border style (default: 'single') */\n boxStyle?: \"single\" | \"double\" | \"rounded\";\n}\n\n// =============================================================================\n// Hook Execution Types\n// =============================================================================\n\n/**\n * State of a hook execution.\n */\nexport type HookState = \"pending\" | \"running\" | \"success\" | \"error\";\n\n/**\n * Execution record for a workflow hook.\n */\nexport interface HookExecution {\n /** Hook type identifier */\n type: \"shouldRun\" | \"onBeforeStart\" | \"onAfterStep\";\n /** Execution state */\n state: HookState;\n /** Timestamp when hook started */\n ts: number;\n /** Duration in milliseconds */\n durationMs?: number;\n /** Error if hook failed */\n error?: unknown;\n /** Additional context (e.g., stepKey for onAfterStep) */\n context?: {\n /** Step key for onAfterStep hooks */\n stepKey?: string;\n /** Result of shouldRun hook */\n result?: boolean;\n /** Whether workflow was skipped due to shouldRun returning false */\n skipped?: boolean;\n };\n}\n\n/**\n * Hook execution summary for the workflow.\n */\nexport interface WorkflowHooks {\n /** shouldRun hook execution (if configured) */\n shouldRun?: HookExecution;\n /** onBeforeStart hook execution (if configured) */\n onBeforeStart?: HookExecution;\n /** onAfterStep hook executions (keyed by stepKey) */\n onAfterStep: Map<string, HookExecution>;\n}\n\n// =============================================================================\n// Export Types\n// =============================================================================\n\n/**\n * Export format for diagram URLs.\n */\nexport type ExportFormat = \"svg\" | \"png\" | \"pdf\";\n\n/**\n * Diagram source - future-proof union for multiple diagram types.\n * Uses \"kind\" internally, maps to \"diagramType\" for Kroki API.\n */\nexport type DiagramSource =\n | { kind: \"mermaid\"; source: string }\n | { kind: \"graphviz\"; source: string }\n | { kind: \"plantuml\"; source: string };\n\n/**\n * Kroki-specific export options.\n * Note: No background/scale - Kroki doesn't support them for mermaid diagrams.\n */\nexport interface KrokiExportOptions {\n /** Provider identifier */\n provider: \"kroki\";\n /** Base URL for self-hosted Kroki (default: https://kroki.io) */\n baseUrl?: string;\n}\n\n/**\n * Mermaid.ink-specific export options.\n * Supports additional styling options like background, scale, and theme.\n */\nexport interface MermaidInkExportOptions {\n /** Provider identifier */\n provider: \"mermaid-ink\";\n /** Mermaid theme */\n mermaidTheme?: \"default\" | \"dark\" | \"forest\" | \"neutral\";\n /** Background color: \"transparent\" or hex color (e.g., \"1b1b1f\") */\n background?: \"transparent\" | string;\n /** Image scale (1-3) */\n scale?: number;\n /** Fit PDF to diagram size */\n fit?: boolean;\n /** Image width in pixels */\n width?: number;\n /** Image height in pixels */\n height?: number;\n /** Paper size for PDF */\n paper?: \"a4\" | \"letter\";\n}\n\n/**\n * Discriminated union of export options.\n * Provider is the discriminant - no implicit defaults.\n */\nexport type ExportOptions = KrokiExportOptions | MermaidInkExportOptions;\n","/**\n * ANSI color utilities for terminal output.\n */\n\nimport type { ColorScheme, StepState } from \"../types\";\n\n// =============================================================================\n// ANSI Escape Codes\n// =============================================================================\n\nconst RESET = \"\\x1b[0m\";\nconst BOLD = \"\\x1b[1m\";\nconst DIM = \"\\x1b[2m\";\n\n// Foreground colors\nconst FG_RED = \"\\x1b[31m\";\nconst FG_GREEN = \"\\x1b[32m\";\nconst FG_YELLOW = \"\\x1b[33m\";\nconst FG_BLUE = \"\\x1b[34m\";\nconst FG_GRAY = \"\\x1b[90m\";\nconst FG_WHITE = \"\\x1b[37m\";\n\n// =============================================================================\n// Color Functions\n// =============================================================================\n\n/**\n * Apply ANSI color to text.\n */\nexport function colorize(text: string, color: string): string {\n if (!color) return text;\n return `${color}${text}${RESET}`;\n}\n\n/**\n * Make text bold.\n */\nexport function bold(text: string): string {\n return `${BOLD}${text}${RESET}`;\n}\n\n/**\n * Make text dim.\n */\nexport function dim(text: string): string {\n return `${DIM}${text}${RESET}`;\n}\n\n// =============================================================================\n// Default Color Scheme\n// =============================================================================\n\n/**\n * Default ANSI color scheme for step states.\n */\nexport const defaultColorScheme: ColorScheme = {\n pending: FG_WHITE,\n running: FG_YELLOW,\n success: FG_GREEN,\n error: FG_RED,\n aborted: FG_GRAY,\n cached: FG_BLUE,\n skipped: DIM + FG_GRAY, // Dim gray for skipped steps\n};\n\n// =============================================================================\n// State Symbols\n// =============================================================================\n\n/**\n * Get the symbol for a step state.\n */\nexport function getStateSymbol(state: StepState): string {\n switch (state) {\n case \"pending\":\n return \"○\"; // Empty circle\n case \"running\":\n return \"⟳\"; // Rotating arrows\n case \"success\":\n return \"✓\"; // Check mark\n case \"error\":\n return \"✗\"; // X mark\n case \"aborted\":\n return \"⊘\"; // Circled slash\n case \"cached\":\n return \"↺\"; // Cached/replay\n case \"skipped\":\n return \"⊘\"; // Circled slash (same as aborted, but different color)\n }\n}\n\n/**\n * Get the colored symbol for a step state.\n */\nexport function getColoredSymbol(state: StepState, colors: ColorScheme): string {\n const symbol = getStateSymbol(state);\n return colorize(symbol, colors[state]);\n}\n\n/**\n * Get colored text based on step state.\n */\nexport function colorByState(\n text: string,\n state: StepState,\n colors: ColorScheme\n): string {\n return colorize(text, colors[state]);\n}\n\n// =============================================================================\n// Strip ANSI\n// =============================================================================\n\n/**\n * Strip ANSI escape codes from a string.\n * Useful for calculating visible string length.\n */\nexport function stripAnsi(str: string): string {\n // eslint-disable-next-line no-control-regex\n return str.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n}\n\n/**\n * Get the visible length of a string (without ANSI codes).\n */\nexport function visibleLength(str: string): string {\n return stripAnsi(str);\n}\n","/**\n * Mermaid Diagram Renderer\n *\n * Renders the workflow IR as a Mermaid flowchart diagram.\n * Supports sequential flows, parallel (subgraph), and race patterns.\n */\n\nimport { ok, err, type Result } from \"awaitly\";\nimport type {\n FlowNode,\n ParallelNode,\n RaceNode,\n DecisionNode,\n StreamNode,\n Renderer,\n RenderOptions,\n MermaidRenderOptions,\n StepNode,\n StepState,\n WorkflowIR,\n EnhancedRenderOptions,\n HeatLevel,\n WorkflowHooks,\n} from \"../types\";\nimport { isParallelNode, isRaceNode, isStepNode, isDecisionNode, isStreamNode } from \"../types\";\nimport { formatDuration } from \"../utils/timing\";\nimport { getHeatLevel } from \"../performance-analyzer\";\n\n/**\n * Error types for stringify operations.\n */\nexport type StringifyError = \"STRINGIFY_ERROR\";\n\n// =============================================================================\n// Mermaid Style Definitions\n// =============================================================================\n\n/**\n * Get Mermaid class definition for step states.\n * Colors inspired by AWS Step Functions and XState visualizers for professional appearance.\n */\nfunction getStyleDefinitions(): string[] {\n return [\n // Pending - light gray, subtle\n \" classDef pending fill:#f3f4f6,stroke:#9ca3af,stroke-width:2px,color:#374151\",\n // Running - amber/yellow, indicates active execution\n \" classDef running fill:#fef3c7,stroke:#f59e0b,stroke-width:3px,color:#92400e\",\n // Success - green, clear positive indicator\n \" classDef success fill:#d1fae5,stroke:#10b981,stroke-width:3px,color:#065f46\",\n // Error - red, clear negative indicator\n \" classDef error fill:#fee2e2,stroke:#ef4444,stroke-width:3px,color:#991b1b\",\n // Aborted - gray, indicates cancellation\n \" classDef aborted fill:#f3f4f6,stroke:#6b7280,stroke-width:2px,color:#4b5563,stroke-dasharray: 5 5\",\n // Cached - blue, indicates cache hit\n \" classDef cached fill:#dbeafe,stroke:#3b82f6,stroke-width:3px,color:#1e40af\",\n // Skipped - light gray with dashed border\n \" classDef skipped fill:#f9fafb,stroke:#d1d5db,stroke-width:2px,color:#6b7280,stroke-dasharray: 5 5\",\n // Stream - purple/violet, indicates streaming operation\n \" classDef stream fill:#ede9fe,stroke:#8b5cf6,stroke-width:3px,color:#5b21b6\",\n // Stream active - purple with animation indicator\n \" classDef streamActive fill:#ddd6fe,stroke:#7c3aed,stroke-width:3px,color:#4c1d95\",\n // Stream error - purple-red for stream errors\n \" classDef streamError fill:#fce7f3,stroke:#db2777,stroke-width:3px,color:#9d174d\",\n ];\n}\n\n/**\n * Get Mermaid class definitions for heatmap visualization.\n */\nfunction getHeatmapStyleDefinitions(): string[] {\n return [\n // Heatmap colors - cold to hot\n \" classDef heat_cold fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af\",\n \" classDef heat_cool fill:#ccfbf1,stroke:#14b8a6,stroke-width:2px,color:#0f766e\",\n \" classDef heat_neutral fill:#f3f4f6,stroke:#6b7280,stroke-width:2px,color:#374151\",\n \" classDef heat_warm fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#92400e\",\n \" classDef heat_hot fill:#fed7aa,stroke:#f97316,stroke-width:3px,color:#c2410c\",\n \" classDef heat_critical fill:#fecaca,stroke:#ef4444,stroke-width:3px,color:#b91c1c\",\n ];\n}\n\n/**\n * Get the Mermaid class name for a heat level.\n */\nfunction getHeatClass(level: HeatLevel): string {\n return `heat_${level}`;\n}\n\n/**\n * Get the Mermaid class name for a step state.\n */\nfunction getStateClass(state: StepState): string {\n return state;\n}\n\n/**\n * Get Mermaid class definitions for hook visualization.\n */\nfunction getHookStyleDefinitions(): string[] {\n return [\n // Hook styles - gear icon aesthetic\n \" classDef hook_success fill:#e0f2fe,stroke:#0284c7,stroke-width:2px,color:#0c4a6e\",\n \" classDef hook_error fill:#fef2f2,stroke:#dc2626,stroke-width:2px,color:#7f1d1d\",\n ];\n}\n\n/**\n * Safely stringify a value, handling circular references and BigInt.\n * Returns Result with either the stringified value or an error.\n */\nfunction safeStringify(value: unknown): Result<string, StringifyError> {\n try {\n const replacer = (_key: string, v: unknown): unknown => {\n if (typeof v !== \"bigint\") return v;\n const n = Number(v);\n return Number.isSafeInteger(n) ? n : v.toString();\n };\n return ok(JSON.stringify(value, replacer));\n } catch {\n return err(\"STRINGIFY_ERROR\");\n }\n}\n\n/**\n * Get stringified value or fallback for unserializable values.\n */\nfunction getStringified(value: unknown): string {\n const result = safeStringify(value);\n return result.ok ? result.value : \"[unserializable]\";\n}\n\n/**\n * Render hooks as nodes before the workflow starts.\n * Returns the ID of the last hook node (to connect to workflow start).\n */\nfunction renderHooks(\n hooks: WorkflowHooks,\n lines: string[],\n options: RenderOptions\n): { lastHookId: string | undefined } {\n let lastHookId: string | undefined;\n\n // Render shouldRun hook\n if (hooks.shouldRun) {\n const hookId = \"hook_shouldRun\";\n const state = hooks.shouldRun.state === \"success\" ? \"hook_success\" : \"hook_error\";\n const icon = hooks.shouldRun.state === \"success\" ? \"⚙\" : \"⚠\";\n const timing = options.showTimings && hooks.shouldRun.durationMs !== undefined\n ? ` ${formatDuration(hooks.shouldRun.durationMs)}`\n : \"\";\n const context = hooks.shouldRun.context?.skipped\n ? \"\\\\nskipped workflow\"\n : hooks.shouldRun.context?.result === true\n ? \"\\\\nproceed\"\n : \"\";\n\n lines.push(` ${hookId}[[\"${icon} shouldRun${context}${timing}\"]]:::${state}`);\n lastHookId = hookId;\n }\n\n // Render onBeforeStart hook\n if (hooks.onBeforeStart) {\n const hookId = \"hook_beforeStart\";\n const state = hooks.onBeforeStart.state === \"success\" ? \"hook_success\" : \"hook_error\";\n const icon = hooks.onBeforeStart.state === \"success\" ? \"⚙\" : \"⚠\";\n const timing = options.showTimings && hooks.onBeforeStart.durationMs !== undefined\n ? ` ${formatDuration(hooks.onBeforeStart.durationMs)}`\n : \"\";\n const context = hooks.onBeforeStart.context?.skipped\n ? \"\\\\nskipped workflow\"\n : \"\";\n\n lines.push(` ${hookId}[[\"${icon} onBeforeStart${context}${timing}\"]]:::${state}`);\n\n // Connect from previous hook if exists\n if (lastHookId) {\n lines.push(` ${lastHookId} --> ${hookId}`);\n }\n lastHookId = hookId;\n }\n\n return { lastHookId };\n}\n\n// =============================================================================\n// Node ID Generation\n// =============================================================================\n\nlet nodeCounter = 0;\nconst usedDecisionIds = new Set<string>();\nconst usedStepIds = new Set<string>();\n\nfunction generateNodeId(prefix: string = \"node\"): string {\n return `${prefix}_${++nodeCounter}`;\n}\n\nfunction resetNodeCounter(): void {\n nodeCounter = 0;\n usedDecisionIds.clear();\n usedStepIds.clear();\n}\n\n// =============================================================================\n// Mermaid Text Escaping\n// =============================================================================\n\n/**\n * Escape text for use in Mermaid diagrams.\n * Only escapes characters that break quoted strings in Mermaid.\n *\n * With bracket-quote syntax (e.g., `nodeId[\"label\"]`), special characters\n * like {}[]() are allowed inside the quoted label.\n *\n * @param text - Text to escape\n * @returns Escaped text safe for Mermaid quoted labels\n */\nfunction escapeMermaidText(text: string): string {\n return text\n .replace(/\"/g, \"#quot;\") // Escape double quotes for Mermaid\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .trim();\n}\n\n/**\n * Escape text for use in Mermaid subgraph names.\n * Subgraph names need special handling - brackets and braces must be removed.\n *\n * @param text - Text to escape for subgraph name\n * @returns Escaped text safe for subgraph names\n */\nfunction escapeSubgraphName(text: string): string {\n return escapeMermaidText(text)\n .replace(/[{}[\\]()]/g, \"\"); // Remove brackets, braces, and parentheses from subgraph names\n}\n\n// =============================================================================\n// Mermaid Renderer\n// =============================================================================\n\n/**\n * Create the Mermaid diagram renderer.\n */\nexport function mermaidRenderer(): Renderer {\n return {\n name: \"mermaid\",\n supportsLive: false,\n\n render(ir: WorkflowIR, options: RenderOptions): string {\n resetNodeCounter();\n const lines: string[] = [];\n\n // Check for enhanced options (heatmap)\n const enhanced = options as EnhancedRenderOptions;\n\n // Diagram header\n lines.push(\"flowchart TD\");\n\n // Render hooks first (if any)\n let hookExitId: string | undefined;\n if (ir.hooks) {\n const hookResult = renderHooks(ir.hooks, lines, options);\n hookExitId = hookResult.lastHookId;\n }\n\n // Start node - more visually distinctive\n const startId = \"start\";\n lines.push(` ${startId}((\"▶ Start\"))`);\n\n // Connect hooks to start node\n if (hookExitId) {\n lines.push(` ${hookExitId} --> ${startId}`);\n }\n\n // Track the last node for connections\n let prevNodeId = startId;\n\n // Render children (passing hooks for onAfterStep annotations)\n for (const child of ir.root.children) {\n const result = renderNode(child, options, lines, enhanced, ir.hooks);\n lines.push(` ${prevNodeId} --> ${result.entryId}`);\n prevNodeId = result.exitId;\n }\n\n // End node (if workflow reached a terminal state) - more visually distinctive\n const terminalStates = [\"success\", \"error\", \"aborted\"] as const;\n if (terminalStates.includes(ir.root.state as (typeof terminalStates)[number])) {\n const endId = \"finish\";\n const endIcon =\n ir.root.state === \"success\" ? \"✓\"\n : ir.root.state === \"error\" ? \"✗\"\n : \"⊘\";\n const endLabel =\n ir.root.state === \"success\" ? \"Done\"\n : ir.root.state === \"error\" ? \"Failed\"\n : \"Cancelled\";\n const endShape = `((\"${endIcon} ${endLabel}\"))`;\n const endClass =\n ir.root.state === \"success\" ? \":::success\"\n : ir.root.state === \"error\" ? \":::error\"\n : \":::aborted\";\n lines.push(` ${endId}${endShape}${endClass}`);\n lines.push(` ${prevNodeId} --> ${endId}`);\n }\n\n // Add style definitions\n lines.push(\"\");\n lines.push(...getStyleDefinitions());\n\n // Add heatmap styles if enabled\n if (enhanced.showHeatmap) {\n lines.push(...getHeatmapStyleDefinitions());\n }\n\n // Add hook styles if hooks were rendered\n if (ir.hooks) {\n lines.push(...getHookStyleDefinitions());\n }\n\n return lines.join(\"\\n\");\n },\n };\n}\n\n/**\n * Render result with entry and exit node IDs.\n */\ninterface RenderResult {\n entryId: string;\n exitId: string;\n}\n\n/**\n * Render a node and return its entry/exit IDs.\n */\nfunction renderNode(\n node: FlowNode,\n options: RenderOptions,\n lines: string[],\n enhanced?: EnhancedRenderOptions,\n hooks?: WorkflowHooks\n): RenderResult {\n if (isStepNode(node)) {\n return renderStepNode(node, options, lines, enhanced, hooks);\n } else if (isParallelNode(node)) {\n return renderParallelNode(node, options, lines, enhanced, hooks);\n } else if (isRaceNode(node)) {\n return renderRaceNode(node, options, lines, enhanced, hooks);\n } else if (isDecisionNode(node)) {\n return renderDecisionNode(node, options, lines, enhanced, hooks);\n } else if (isStreamNode(node)) {\n return renderStreamNode(node, options, lines);\n }\n\n // Fallback for sequence or unknown nodes\n const id = generateNodeId(\"unknown\");\n lines.push(` ${id}[\"Unknown Node\"]`);\n return { entryId: id, exitId: id };\n}\n\n/**\n * Render a step node.\n */\nfunction renderStepNode(\n node: StepNode,\n options: RenderOptions,\n lines: string[],\n enhanced?: EnhancedRenderOptions,\n hooks?: WorkflowHooks\n): RenderResult {\n // Cast to MermaidRenderOptions to access extended options\n const mermaidOpts = options as MermaidRenderOptions;\n const showRetryEdges = mermaidOpts.showRetryEdges ?? true;\n const showErrorEdges = mermaidOpts.showErrorEdges ?? true;\n const showTimeoutEdges = mermaidOpts.showTimeoutEdges ?? true;\n\n // Generate step ID, ensuring uniqueness even with duplicate keys\n let id = node.key\n ? `step_${node.key.replace(/[^a-zA-Z0-9]/g, \"_\")}`\n : generateNodeId(\"step\");\n\n // Ensure uniqueness by appending suffix if collision\n if (usedStepIds.has(id)) {\n let suffix = 2;\n while (usedStepIds.has(`${id}_${suffix}`)) {\n suffix++;\n }\n id = `${id}_${suffix}`;\n }\n usedStepIds.add(id);\n\n const baseLabel = node.name ?? node.key ?? \"Step\";\n const labelText = options.showKeys && node.key && node.name\n ? `${baseLabel} [${node.key}]`\n : baseLabel;\n const label = escapeMermaidText(labelText);\n\n // Format timing - use space instead of parentheses to avoid Mermaid parse errors\n const timing =\n options.showTimings && node.durationMs !== undefined\n ? ` ${formatDuration(node.durationMs)}`\n : \"\";\n\n // Add visual indicators based on state (like XState/AWS Step Functions)\n let stateIcon = \"\";\n switch (node.state) {\n case \"success\":\n stateIcon = \"✓ \";\n break;\n case \"error\":\n stateIcon = \"✗ \";\n break;\n case \"cached\":\n stateIcon = \"💾 \";\n break;\n case \"running\":\n stateIcon = \"⏳ \";\n break;\n case \"skipped\":\n stateIcon = \"⊘ \";\n break;\n }\n\n // Add input/output info if available\n // Use newlines for multi-line labels, but escape special characters\n let ioInfo = \"\";\n if (node.input !== undefined) {\n const inputStr = typeof node.input === \"string\"\n ? escapeMermaidText(node.input)\n : escapeMermaidText(getStringified(node.input).slice(0, 20));\n ioInfo += `\\\\nin: ${inputStr}`;\n }\n if (node.output !== undefined && node.state === \"success\") {\n const outputStr = typeof node.output === \"string\"\n ? escapeMermaidText(node.output)\n : escapeMermaidText(getStringified(node.output).slice(0, 20));\n ioInfo += `\\\\nout: ${outputStr}`;\n }\n\n // Add onAfterStep hook info if present (check by key first, then by id)\n let hookInfo = \"\";\n const hookKey = node.key ?? node.id;\n if (hooks && hookKey && hooks.onAfterStep.has(hookKey)) {\n const hookExec = hooks.onAfterStep.get(hookKey)!;\n const hookIcon = hookExec.state === \"success\" ? \"⚙\" : \"⚠\";\n const hookTiming = options.showTimings && hookExec.durationMs !== undefined\n ? ` ${formatDuration(hookExec.durationMs)}`\n : \"\";\n hookInfo = `\\\\n${hookIcon} hook${hookTiming}`;\n }\n\n // Combine all label parts with icon (retry/timeout info moved to edges)\n const escapedLabel = (stateIcon + label + ioInfo + hookInfo + timing).trim();\n\n // Determine class: use heatmap if enabled and data available, otherwise use state\n // Lookup order matches PerformanceAnalyzer: key ?? name ?? id\n let nodeClass: string;\n const heat = enhanced?.showHeatmap && enhanced.heatmapData\n ? enhanced.heatmapData.heat.get(node.key ?? \"\") ??\n enhanced.heatmapData.heat.get(node.name ?? \"\") ??\n enhanced.heatmapData.heat.get(node.id)\n : undefined;\n\n if (heat !== undefined) {\n const level = getHeatLevel(heat);\n nodeClass = getHeatClass(level);\n } else {\n nodeClass = getStateClass(node.state);\n }\n\n // Use different shapes based on state (like AWS Step Functions)\n let shape: string;\n switch (node.state) {\n case \"error\":\n // Hexagon for errors (more distinctive)\n shape = `{{\"${escapedLabel}\"}}`;\n break;\n case \"cached\":\n // Rounded rectangle with double border for cached\n shape = `[(\"${escapedLabel}\")]`;\n break;\n case \"skipped\":\n // Dashed border via class (applied once in lines.push below)\n shape = `[\"${escapedLabel}\"]`;\n break;\n default:\n // Standard rectangle for normal steps\n shape = `[\"${escapedLabel}\"]`;\n }\n\n lines.push(` ${id}${shape}:::${nodeClass}`);\n\n // NEW: Add retry loop edge (self-loop showing retries)\n if (showRetryEdges && node.retryCount !== undefined && node.retryCount > 0) {\n const retryLabel = `↻ ${node.retryCount} retr${node.retryCount === 1 ? \"y\" : \"ies\"}`;\n lines.push(` ${id} -.->|\"${retryLabel}\"| ${id}`);\n }\n\n // NEW: Add error path edge (flow to error node)\n if (showErrorEdges && node.state === \"error\" && node.error !== undefined) {\n const errorNodeId = `ERR_${id}`;\n const errorLabel = escapeMermaidText(String(node.error)).slice(0, 30);\n lines.push(` ${errorNodeId}{{\"${errorLabel}\"}}`);\n lines.push(` ${id} -->|error| ${errorNodeId}`);\n lines.push(` style ${errorNodeId} fill:#fee2e2,stroke:#dc2626`);\n }\n\n // NEW: Add timeout edge (alternative timeout path)\n if (showTimeoutEdges && node.timedOut) {\n const timeoutNodeId = `TO_${id}`;\n const timeoutMs = node.timeoutMs !== undefined ? `${node.timeoutMs}ms` : \"\";\n lines.push(` ${timeoutNodeId}{{\"⏱ Timeout ${timeoutMs}\"}}`);\n lines.push(` ${id} -.->|timeout| ${timeoutNodeId}`);\n lines.push(` style ${timeoutNodeId} fill:#fef3c7,stroke:#f59e0b`);\n }\n\n return { entryId: id, exitId: id };\n}\n\n/**\n * Render a parallel node as a subgraph with fork/join.\n */\nfunction renderParallelNode(\n node: ParallelNode,\n options: RenderOptions,\n lines: string[],\n enhanced?: EnhancedRenderOptions,\n hooks?: WorkflowHooks\n): RenderResult {\n const subgraphId = generateNodeId(\"parallel\");\n const forkId = `${subgraphId}_fork`;\n const joinId = `${subgraphId}_join`;\n const name = escapeSubgraphName(node.name ?? \"Parallel\");\n const modeLabel = node.mode === \"allSettled\" ? \" (allSettled)\" : \"\";\n\n // If no children, render as a simple step-like node with note\n if (node.children.length === 0) {\n const id = subgraphId;\n const label = escapeMermaidText(`${name}${modeLabel}`);\n const note = \"operations not individually tracked\";\n const timing = options.showTimings && node.durationMs !== undefined\n ? ` ${formatDuration(node.durationMs)}`\n : \"\";\n\n // Use a rounded rectangle to indicate it's a parallel operation\n lines.push(` ${id}[\"${label}${timing}\\\\n${note}\"]:::${getStateClass(node.state)}`);\n return { entryId: id, exitId: id };\n }\n\n // Subgraph for parallel block with proper visual hierarchy\n lines.push(` subgraph ${subgraphId}[\"${name}${modeLabel}\"]`);\n lines.push(` direction TB`);\n\n // Fork node (diamond) - more visually distinct\n lines.push(` ${forkId}{\"⚡ Fork\"}`);\n\n // Child branches - render in parallel columns\n const childExitIds: string[] = [];\n for (const child of node.children) {\n const result = renderNode(child, options, lines, enhanced, hooks);\n lines.push(` ${forkId} --> ${result.entryId}`);\n childExitIds.push(result.exitId);\n }\n\n // Join node (diamond) - visually distinct\n lines.push(` ${joinId}{\"✓ Join\"}`);\n for (const exitId of childExitIds) {\n lines.push(` ${exitId} --> ${joinId}`);\n }\n\n lines.push(` end`);\n\n // Apply state styling to subgraph\n const stateClass = getStateClass(node.state);\n lines.push(` class ${subgraphId} ${stateClass}`);\n\n return { entryId: forkId, exitId: joinId };\n}\n\n/**\n * Render a race node as a subgraph with racing indicator.\n */\nfunction renderRaceNode(\n node: RaceNode,\n options: RenderOptions,\n lines: string[],\n enhanced?: EnhancedRenderOptions,\n hooks?: WorkflowHooks\n): RenderResult {\n const subgraphId = generateNodeId(\"race\");\n const startId = `${subgraphId}_start`;\n const endId = `${subgraphId}_end`;\n const name = escapeSubgraphName(node.name ?? \"Race\");\n\n // If no children, render as a simple step-like node with note\n if (node.children.length === 0) {\n const id = subgraphId;\n const label = escapeMermaidText(name);\n const note = \"operations not individually tracked\";\n const timing = options.showTimings && node.durationMs !== undefined\n ? ` ${formatDuration(node.durationMs)}`\n : \"\";\n\n lines.push(` ${id}[\"⚡ ${label}${timing}\\\\n${note}\"]:::${getStateClass(node.state)}`);\n return { entryId: id, exitId: id };\n }\n\n // Subgraph for race block - escape name and emoji is safe in quoted strings\n lines.push(` subgraph ${subgraphId}[\"⚡ ${name}\"]`);\n lines.push(` direction TB`);\n\n // Start node - use a more distinctive shape\n lines.push(` ${startId}((\"🏁 Start\"))`);\n\n // Child branches\n const childExitIds: Array<{ exitId: string; isWinner: boolean }> = [];\n let winnerExitId: string | undefined;\n\n for (const child of node.children) {\n const result = renderNode(child, options, lines, enhanced, hooks);\n const isWinner = node.winnerId === child.id;\n lines.push(` ${startId} --> ${result.entryId}`);\n\n if (isWinner) {\n winnerExitId = result.exitId;\n }\n childExitIds.push({ exitId: result.exitId, isWinner });\n }\n\n // End node - more distinctive\n lines.push(` ${endId}((\"✓ First\"))`);\n\n // Connect winner with thick line, others with dashed (cancelled)\n for (const { exitId, isWinner } of childExitIds) {\n if (isWinner && winnerExitId) {\n lines.push(` ${exitId} ==>|🏆 Winner| ${endId}`);\n } else if (node.winnerId) {\n // Non-winner: show as cancelled\n lines.push(` ${exitId} -. cancelled .-> ${endId}`);\n } else {\n // No winner determined, normal connection\n lines.push(` ${exitId} --> ${endId}`);\n }\n }\n\n lines.push(` end`);\n\n const stateClass = getStateClass(node.state);\n lines.push(` class ${subgraphId} ${stateClass}`);\n\n return { entryId: startId, exitId: endId };\n}\n\n/**\n * Render a decision node as a diamond with branches.\n */\nfunction renderDecisionNode(\n node: DecisionNode,\n options: RenderOptions,\n lines: string[],\n enhanced?: EnhancedRenderOptions,\n hooks?: WorkflowHooks\n): RenderResult {\n // Generate decision ID, ensuring uniqueness even with duplicate keys\n let decisionId = node.key\n ? `decision_${node.key.replace(/[^a-zA-Z0-9]/g, \"_\")}`\n : generateNodeId(\"decision\");\n\n // Ensure uniqueness by appending suffix if collision\n if (usedDecisionIds.has(decisionId)) {\n let suffix = 2;\n while (usedDecisionIds.has(`${decisionId}_${suffix}`)) {\n suffix++;\n }\n decisionId = `${decisionId}_${suffix}`;\n }\n usedDecisionIds.add(decisionId);\n\n // Escape condition and decision value - remove characters that break Mermaid\n const condition = escapeMermaidText(node.condition ?? \"condition\");\n const decisionValue = node.decisionValue !== undefined\n ? ` = ${escapeMermaidText(String(node.decisionValue)).slice(0, 30)}`\n : \"\";\n\n // Decision diamond - ensure no invalid characters\n const decisionLabel = `${condition}${decisionValue}`.trim();\n lines.push(` ${decisionId}{\"${decisionLabel}\"}`);\n\n // Render branches\n const branchExitIds: string[] = [];\n let takenBranchExitId: string | undefined;\n const usedBranchIds = new Set<string>();\n\n for (const branch of node.branches) {\n // Generate base branch ID from sanitized label\n let branchId = `${decisionId}_${branch.label.replace(/[^a-zA-Z0-9]/g, \"_\")}`;\n // Ensure uniqueness by appending index if collision\n if (usedBranchIds.has(branchId)) {\n let suffix = 2;\n while (usedBranchIds.has(`${branchId}_${suffix}`)) {\n suffix++;\n }\n branchId = `${branchId}_${suffix}`;\n }\n usedBranchIds.add(branchId);\n // Escape branch label - remove parentheses and other special chars\n const branchLabelText = escapeMermaidText(branch.label);\n const branchLabel = branch.taken\n ? `${branchLabelText} ✓`\n : `${branchLabelText} skipped`;\n const branchClass = branch.taken ? \":::success\" : \":::skipped\";\n\n // Branch label node\n lines.push(` ${branchId}[\"${branchLabel}\"]${branchClass}`);\n\n // Connect decision to branch\n // Mermaid edge labels must be simple text - escape special characters\n // Also remove pipe character as it's used for edge label syntax\n const edgeLabel = branch.condition\n ? `|${escapeMermaidText(branch.condition).replace(/\\|/g, \"\")}|`\n : \"\";\n lines.push(` ${decisionId} -->${edgeLabel} ${branchId}`);\n\n // Render children of this branch\n if (branch.children.length > 0) {\n let prevId = branchId;\n for (const child of branch.children) {\n const result = renderNode(child, options, lines, enhanced, hooks);\n lines.push(` ${prevId} --> ${result.entryId}`);\n prevId = result.exitId;\n }\n branchExitIds.push(prevId);\n if (branch.taken) {\n takenBranchExitId = prevId;\n }\n } else {\n branchExitIds.push(branchId);\n if (branch.taken) {\n takenBranchExitId = branchId;\n }\n }\n }\n\n // Join point (if we have a taken branch)\n if (takenBranchExitId) {\n return { entryId: decisionId, exitId: takenBranchExitId };\n }\n\n // If no branch was taken, return decision as exit\n return { entryId: decisionId, exitId: decisionId };\n}\n\n/**\n * Render a stream node.\n * Uses hexagonal shape to distinguish from regular steps.\n */\nfunction renderStreamNode(\n node: StreamNode,\n options: RenderOptions,\n lines: string[]\n): RenderResult {\n const id = `stream_${node.namespace.replace(/[^a-zA-Z0-9]/g, \"_\")}_${generateNodeId(\"\")}`;\n\n // Format counts\n const counts = `W:${node.writeCount} R:${node.readCount}`;\n\n // Add state icon\n let stateIcon = \"\";\n switch (node.streamState) {\n case \"active\":\n stateIcon = \"⟳ \";\n break;\n case \"closed\":\n stateIcon = \"✓ \";\n break;\n case \"error\":\n stateIcon = \"✗ \";\n break;\n }\n\n // Format timing\n const timing =\n options.showTimings && node.durationMs !== undefined\n ? ` ${formatDuration(node.durationMs)}`\n : \"\";\n\n // Backpressure indicator\n const backpressure = node.backpressureOccurred ? \"\\\\nbackpressure\" : \"\";\n\n // Combine label parts - use hexagon shape for streams\n const label = `${stateIcon}stream:${escapeMermaidText(node.namespace)}\\\\n${counts}${backpressure}${timing}`;\n\n // Determine class based on stream state\n let nodeClass: string;\n if (node.streamState === \"error\") {\n nodeClass = \"streamError\";\n } else if (node.streamState === \"active\") {\n nodeClass = \"streamActive\";\n } else {\n nodeClass = \"stream\";\n }\n\n // Hexagonal shape for streams: {{\"label\"}}\n lines.push(` ${id}{{\"${label}\"}}:::${nodeClass}`);\n\n return { entryId: id, exitId: id };\n}\n\nexport { mermaidRenderer as default };\n","/**\n * Performance Analyzer\n *\n * Analyzes workflow execution data to identify:\n * - Slow steps (bottlenecks)\n * - Retry patterns\n * - Error-prone steps\n * - Timing anomalies\n *\n * Aggregates metrics across multiple workflow runs to provide\n * statistical insights and heatmap visualization data.\n */\n\nimport type { WorkflowEvent } from \"awaitly/workflow\";\nimport type {\n NodePerformance,\n HeatmapData,\n WorkflowIR,\n FlowNode,\n HeatLevel,\n} from \"./types\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * A recorded workflow run for analysis.\n */\nexport interface WorkflowRun {\n /** Unique identifier for this run */\n id: string;\n /** Workflow start timestamp */\n startTime: number;\n /** All events from the workflow execution */\n events: WorkflowEvent<unknown>[];\n}\n\n/**\n * Performance analyzer interface.\n */\nexport interface PerformanceAnalyzer {\n /** Add a completed workflow run for analysis */\n addRun: (run: WorkflowRun) => void;\n\n /** Add events incrementally (alternative to addRun) */\n addEvent: (event: WorkflowEvent<unknown>) => void;\n\n /** Finalize current run (when using addEvent) */\n finalizeRun: (runId: string) => void;\n\n /** Get performance stats for a specific node */\n getNodePerformance: (nodeId: string) => NodePerformance | undefined;\n\n /** Get heatmap data for an IR */\n getHeatmap: (\n ir: WorkflowIR,\n metric?: \"duration\" | \"retryRate\" | \"errorRate\"\n ) => HeatmapData;\n\n /** Get slowest nodes */\n getSlowestNodes: (limit?: number) => NodePerformance[];\n\n /** Get error-prone nodes */\n getErrorProneNodes: (limit?: number) => NodePerformance[];\n\n /** Get retry-prone nodes */\n getRetryProneNodes: (limit?: number) => NodePerformance[];\n\n /** Get all performance data */\n getAllPerformance: () => Map<string, NodePerformance>;\n\n /** Export performance data as JSON */\n exportData: () => string;\n\n /** Import performance data from JSON */\n importData: (json: string) => void;\n\n /** Clear all collected data */\n clear: () => void;\n}\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Flatten all nodes from an IR tree.\n */\nfunction flattenNodes(nodes: FlowNode[]): FlowNode[] {\n const result: FlowNode[] = [];\n for (const node of nodes) {\n result.push(node);\n if (\"children\" in node && Array.isArray(node.children)) {\n result.push(...flattenNodes(node.children));\n }\n if (\"branches\" in node) {\n for (const branch of node.branches) {\n result.push(...flattenNodes(branch.children));\n }\n }\n }\n return result;\n}\n\n/**\n * Calculate percentile value from sorted array.\n */\nfunction percentile(sortedValues: number[], p: number): number {\n if (sortedValues.length === 0) return 0;\n const index = Math.floor(sortedValues.length * p);\n return sortedValues[Math.min(index, sortedValues.length - 1)];\n}\n\n/**\n * Get heat level from normalized value (0-1).\n */\nexport function getHeatLevel(heat: number): HeatLevel {\n if (heat < 0.2) return \"cold\";\n if (heat < 0.4) return \"cool\";\n if (heat < 0.6) return \"neutral\";\n if (heat < 0.8) return \"warm\";\n if (heat < 0.95) return \"hot\";\n return \"critical\";\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Create a performance analyzer for workflow metrics.\n *\n * @example\n * ```typescript\n * const analyzer = createPerformanceAnalyzer();\n *\n * // Add completed runs\n * analyzer.addRun({ id: 'run-1', startTime: Date.now(), events });\n *\n * // Get insights\n * const slowest = analyzer.getSlowestNodes(5);\n * const heatmap = analyzer.getHeatmap(ir, 'duration');\n * ```\n */\nexport function createPerformanceAnalyzer(): PerformanceAnalyzer {\n // Timing data: nodeId → array of durations (ms)\n const timingData = new Map<string, number[]>();\n\n // Retry data: nodeId → { retried runs, total runs }\n const retryData = new Map<string, { retried: number; total: number }>();\n\n // Error data: nodeId → { error runs, total runs }\n const errorData = new Map<string, { errors: number; total: number }>();\n\n // Timeout data: nodeId → { timed out, total }\n const timeoutData = new Map<string, { timedOut: number; total: number }>();\n\n // Current run state (for incremental event adding)\n let currentRunEvents: WorkflowEvent<unknown>[] = [];\n\n /**\n * Get node ID from event.\n * Prioritizes stepKey, then stepId, then name so distinct steps (same name, no key)\n * are not merged.\n */\n function getNodeId(event: {\n stepId?: string;\n stepKey?: string;\n name?: string;\n }): string {\n return event.stepKey ?? event.stepId ?? event.name ?? \"unknown\";\n }\n\n /**\n * Process events from a workflow run.\n */\n function processEvents(events: WorkflowEvent<unknown>[]): void {\n // Track step state during processing\n const stepState = new Map<\n string,\n {\n retried: boolean;\n timedOut: boolean;\n }\n >();\n\n for (const event of events) {\n switch (event.type) {\n case \"step_start\": {\n const id = getNodeId(event);\n stepState.set(id, { retried: false, timedOut: false });\n break;\n }\n\n case \"step_retry\": {\n const id = getNodeId(event);\n const state = stepState.get(id);\n if (state) {\n state.retried = true;\n }\n break;\n }\n\n case \"step_timeout\": {\n const id = getNodeId(event);\n const state = stepState.get(id);\n if (state) {\n state.timedOut = true;\n }\n // Don't update counts here - wait for step completion\n // to get accurate total count across all runs\n break;\n }\n\n case \"step_success\": {\n const id = getNodeId(event);\n const state = stepState.get(id);\n\n // Record timing\n const timings = timingData.get(id) ?? [];\n timings.push(event.durationMs);\n timingData.set(id, timings);\n\n // Record retry status\n const retry = retryData.get(id) ?? { retried: 0, total: 0 };\n retry.total++;\n if (state?.retried) retry.retried++;\n retryData.set(id, retry);\n\n // Record timeout status\n const timeout = timeoutData.get(id) ?? { timedOut: 0, total: 0 };\n timeout.total++;\n if (state?.timedOut) timeout.timedOut++;\n timeoutData.set(id, timeout);\n\n // Record success (no error)\n const error = errorData.get(id) ?? { errors: 0, total: 0 };\n error.total++;\n errorData.set(id, error);\n\n stepState.delete(id);\n break;\n }\n\n case \"step_error\": {\n const id = getNodeId(event);\n const state = stepState.get(id);\n\n // Record timing\n const timings = timingData.get(id) ?? [];\n timings.push(event.durationMs);\n timingData.set(id, timings);\n\n // Record retry status\n const retry = retryData.get(id) ?? { retried: 0, total: 0 };\n retry.total++;\n if (state?.retried) retry.retried++;\n retryData.set(id, retry);\n\n // Record timeout status\n const timeout = timeoutData.get(id) ?? { timedOut: 0, total: 0 };\n timeout.total++;\n if (state?.timedOut) timeout.timedOut++;\n timeoutData.set(id, timeout);\n\n // Record error\n const error = errorData.get(id) ?? { errors: 0, total: 0 };\n error.total++;\n error.errors++;\n errorData.set(id, error);\n\n stepState.delete(id);\n break;\n }\n }\n }\n }\n\n /**\n * Add a completed workflow run.\n */\n function addRun(run: WorkflowRun): void {\n processEvents(run.events);\n }\n\n /**\n * Add an event incrementally.\n */\n function addEvent(event: WorkflowEvent<unknown>): void {\n currentRunEvents.push(event);\n }\n\n /**\n * Finalize current run (process accumulated events).\n */\n function finalizeRun(_runId: string): void {\n if (currentRunEvents.length > 0) {\n processEvents(currentRunEvents);\n currentRunEvents = [];\n }\n }\n\n /**\n * Compute performance metrics for a node.\n */\n function computePerformance(nodeId: string): NodePerformance | undefined {\n const timings = timingData.get(nodeId);\n if (!timings || timings.length === 0) return undefined;\n\n const sorted = [...timings].sort((a, b) => a - b);\n const sum = sorted.reduce((a, b) => a + b, 0);\n const mean = sum / sorted.length;\n const variance =\n sorted.reduce((acc, t) => acc + (t - mean) ** 2, 0) / sorted.length;\n\n const retry = retryData.get(nodeId) ?? { retried: 0, total: 1 };\n const error = errorData.get(nodeId) ?? { errors: 0, total: 1 };\n const timeout = timeoutData.get(nodeId) ?? { timedOut: 0, total: 1 };\n\n return {\n nodeId,\n avgDurationMs: mean,\n minDurationMs: sorted[0],\n maxDurationMs: sorted[sorted.length - 1],\n stdDevMs: Math.sqrt(variance),\n samples: sorted.length,\n retryRate: retry.total > 0 ? retry.retried / retry.total : 0,\n timeoutRate: timeout.total > 0 ? timeout.timedOut / timeout.total : 0,\n errorRate: error.total > 0 ? error.errors / error.total : 0,\n percentiles: {\n p50: percentile(sorted, 0.5),\n p90: percentile(sorted, 0.9),\n p95: percentile(sorted, 0.95),\n p99: percentile(sorted, 0.99),\n },\n };\n }\n\n /**\n * Get performance stats for a specific node.\n */\n function getNodePerformance(nodeId: string): NodePerformance | undefined {\n return computePerformance(nodeId);\n }\n\n /**\n * Get heatmap data for an IR.\n */\n function getHeatmap(\n ir: WorkflowIR,\n metric: \"duration\" | \"retryRate\" | \"errorRate\" = \"duration\"\n ): HeatmapData {\n const heat = new Map<string, number>();\n const allNodes = flattenNodes(ir.root.children);\n\n // Compute values for all nodes\n const values: Array<{ id: string; value: number }> = [];\n for (const node of allNodes) {\n // Use same lookup order as getNodeId: stepKey ?? stepId ?? name (IR has key, id, name)\n const lookupKey =\n (\"key\" in node ? node.key : undefined) ?? node.id ?? node.name;\n const perf = computePerformance(lookupKey);\n if (perf) {\n let value: number;\n switch (metric) {\n case \"duration\":\n value = perf.avgDurationMs;\n break;\n case \"retryRate\":\n value = perf.retryRate;\n break;\n case \"errorRate\":\n value = perf.errorRate;\n break;\n }\n values.push({ id: node.id, value });\n }\n }\n\n if (values.length === 0) {\n return {\n heat,\n metric,\n stats: { min: 0, max: 0, mean: 0, threshold: 0 },\n };\n }\n\n // Compute statistics\n const vals = values.map((v) => v.value);\n const min = Math.min(...vals);\n const max = Math.max(...vals);\n const mean = vals.reduce((a, b) => a + b, 0) / vals.length;\n const range = max - min || 1;\n\n // Normalize to 0-1 heat values\n for (const { id, value } of values) {\n heat.set(id, (value - min) / range);\n }\n\n return {\n heat,\n metric,\n stats: {\n min,\n max,\n mean,\n threshold: mean + (max - mean) * 0.5, // 50% above mean is \"hot\"\n },\n };\n }\n\n /**\n * Get all performance data.\n */\n function getAllPerformance(): Map<string, NodePerformance> {\n const result = new Map<string, NodePerformance>();\n for (const nodeId of timingData.keys()) {\n const perf = computePerformance(nodeId);\n if (perf) result.set(nodeId, perf);\n }\n return result;\n }\n\n /**\n * Get slowest nodes by average duration.\n */\n function getSlowestNodes(limit = 10): NodePerformance[] {\n const all = getAllPerformance();\n return [...all.values()]\n .sort((a, b) => b.avgDurationMs - a.avgDurationMs)\n .slice(0, limit);\n }\n\n /**\n * Get error-prone nodes by error rate.\n */\n function getErrorProneNodes(limit = 10): NodePerformance[] {\n const all = getAllPerformance();\n return [...all.values()]\n .filter((p) => p.errorRate > 0)\n .sort((a, b) => b.errorRate - a.errorRate)\n .slice(0, limit);\n }\n\n /**\n * Get retry-prone nodes by retry rate.\n */\n function getRetryProneNodes(limit = 10): NodePerformance[] {\n const all = getAllPerformance();\n return [...all.values()]\n .filter((p) => p.retryRate > 0)\n .sort((a, b) => b.retryRate - a.retryRate)\n .slice(0, limit);\n }\n\n /**\n * Export performance data as JSON.\n */\n function exportData(): string {\n return JSON.stringify({\n timingData: Object.fromEntries(timingData),\n retryData: Object.fromEntries(retryData),\n errorData: Object.fromEntries(errorData),\n timeoutData: Object.fromEntries(timeoutData),\n });\n }\n\n /**\n * Import performance data from JSON.\n */\n function importData(json: string): void {\n const data = JSON.parse(json) as {\n timingData?: Record<string, number[]>;\n retryData?: Record<string, { retried: number; total: number }>;\n errorData?: Record<string, { errors: number; total: number }>;\n timeoutData?: Record<string, { timedOut: number; total: number }>;\n };\n\n // Clear existing data\n timingData.clear();\n retryData.clear();\n errorData.clear();\n timeoutData.clear();\n\n // Import timing data\n for (const [k, v] of Object.entries(data.timingData ?? {})) {\n timingData.set(k, v);\n }\n\n // Import retry data\n for (const [k, v] of Object.entries(data.retryData ?? {})) {\n retryData.set(k, v);\n }\n\n // Import error data\n for (const [k, v] of Object.entries(data.errorData ?? {})) {\n errorData.set(k, v);\n }\n\n // Import timeout data\n for (const [k, v] of Object.entries(data.timeoutData ?? {})) {\n timeoutData.set(k, v);\n }\n }\n\n /**\n * Clear all collected data.\n */\n function clear(): void {\n timingData.clear();\n retryData.clear();\n errorData.clear();\n timeoutData.clear();\n currentRunEvents = [];\n }\n\n return {\n addRun,\n addEvent,\n finalizeRun,\n getNodePerformance,\n getHeatmap,\n getSlowestNodes,\n getErrorProneNodes,\n getRetryProneNodes,\n getAllPerformance,\n exportData,\n importData,\n clear,\n };\n}\n","/**\n * Flowchart ASCII Renderer\n *\n * Renders workflow IR as a proper flowchart with boxes and arrows.\n * Uses a 2D canvas approach for spatial layout with proper fork/join patterns.\n */\n\nimport type {\n FlowNode,\n ParallelNode,\n RaceNode,\n DecisionNode,\n StreamNode,\n Renderer,\n RenderOptions,\n StepNode,\n WorkflowIR,\n FlowchartRenderOptions,\n EnhancedRenderOptions,\n HeatLevel,\n} from \"../types\";\nimport { isParallelNode, isRaceNode, isStepNode, isDecisionNode, isStreamNode } from \"../types\";\nimport { formatDuration } from \"../utils/timing\";\nimport {\n defaultColorScheme,\n stripAnsi,\n} from \"./colors\";\nimport { renderSparkline } from \"./ascii\";\nimport { getHeatLevel } from \"../performance-analyzer\";\n\n// =============================================================================\n// Box Drawing Characters\n// =============================================================================\n\nconst CHARS = {\n topLeft: \"┌\",\n topRight: \"┐\",\n bottomLeft: \"└\",\n bottomRight: \"┘\",\n horizontal: \"─\",\n vertical: \"│\",\n teeDown: \"┬\",\n teeUp: \"┴\",\n teeRight: \"├\",\n teeLeft: \"┤\",\n cross: \"┼\",\n arrowDown: \"▼\",\n arrowUp: \"▲\",\n} as const;\n\n// =============================================================================\n// Heatmap Colors\n// =============================================================================\n\nconst HEAT_COLORS: Record<HeatLevel, string> = {\n cold: \"\\x1b[34m\",\n cool: \"\\x1b[36m\",\n neutral: \"\",\n warm: \"\\x1b[33m\",\n hot: \"\\x1b[31m\",\n critical: \"\\x1b[41m\",\n};\n\nconst RESET = \"\\x1b[0m\";\n\n// =============================================================================\n// Canvas Module\n// =============================================================================\n\ninterface Canvas {\n cells: string[][];\n colors: (string | undefined)[][];\n width: number;\n height: number;\n}\n\nfunction createCanvas(width: number, height: number): Canvas {\n const cells: string[][] = [];\n const colors: (string | undefined)[][] = [];\n for (let y = 0; y < height; y++) {\n cells.push(Array(width).fill(\" \"));\n colors.push(Array(width).fill(undefined));\n }\n return { cells, colors, width, height };\n}\n\nfunction setChar(canvas: Canvas, x: number, y: number, char: string, color?: string): void {\n if (x >= 0 && x < canvas.width && y >= 0 && y < canvas.height) {\n canvas.cells[y][x] = char;\n if (color) canvas.colors[y][x] = color;\n }\n}\n\nfunction getChar(canvas: Canvas, x: number, y: number): string {\n if (x >= 0 && x < canvas.width && y >= 0 && y < canvas.height) {\n return canvas.cells[y][x];\n }\n return \" \";\n}\n\nfunction drawBox(canvas: Canvas, x: number, y: number, width: number, height: number): void {\n setChar(canvas, x, y, CHARS.topLeft);\n for (let i = 1; i < width - 1; i++) setChar(canvas, x + i, y, CHARS.horizontal);\n setChar(canvas, x + width - 1, y, CHARS.topRight);\n\n for (let j = 1; j < height - 1; j++) {\n setChar(canvas, x, y + j, CHARS.vertical);\n setChar(canvas, x + width - 1, y + j, CHARS.vertical);\n }\n\n setChar(canvas, x, y + height - 1, CHARS.bottomLeft);\n for (let i = 1; i < width - 1; i++) setChar(canvas, x + i, y + height - 1, CHARS.horizontal);\n setChar(canvas, x + width - 1, y + height - 1, CHARS.bottomRight);\n}\n\nfunction drawText(canvas: Canvas, x: number, y: number, text: string, color?: string): void {\n const chars = stripAnsi(text).split(\"\");\n for (let i = 0; i < chars.length; i++) {\n setChar(canvas, x + i, y, chars[i], color);\n }\n}\n\nfunction drawVerticalLine(canvas: Canvas, x: number, startY: number, endY: number): void {\n const minY = Math.min(startY, endY);\n const maxY = Math.max(startY, endY);\n for (let y = minY; y <= maxY; y++) {\n const existing = getChar(canvas, x, y);\n if (existing === CHARS.horizontal) {\n setChar(canvas, x, y, CHARS.cross);\n } else if (existing === \" \" || existing === CHARS.vertical) {\n setChar(canvas, x, y, CHARS.vertical);\n }\n }\n}\n\nfunction drawHorizontalLine(canvas: Canvas, y: number, startX: number, endX: number): void {\n const minX = Math.min(startX, endX);\n const maxX = Math.max(startX, endX);\n for (let x = minX; x <= maxX; x++) {\n const existing = getChar(canvas, x, y);\n if (existing === CHARS.vertical) {\n setChar(canvas, x, y, CHARS.cross);\n } else if (existing === \" \" || existing === CHARS.horizontal) {\n setChar(canvas, x, y, CHARS.horizontal);\n }\n }\n}\n\nfunction drawArrow(canvas: Canvas, x: number, y: number): void {\n setChar(canvas, x, y, CHARS.arrowDown);\n}\n\nfunction canvasToString(canvas: Canvas): string {\n const lines: string[] = [];\n for (let y = 0; y < canvas.height; y++) {\n let line = \"\";\n for (let x = 0; x < canvas.width; x++) {\n const color = canvas.colors[y][x];\n const char = canvas.cells[y][x];\n if (color) {\n line += color + char + RESET;\n } else {\n line += char;\n }\n }\n lines.push(line.trimEnd());\n }\n while (lines.length > 0 && lines[lines.length - 1] === \"\") lines.pop();\n return lines.join(\"\\n\");\n}\n\n// =============================================================================\n// Layout Types\n// =============================================================================\n\ninterface LayoutNode {\n id: string;\n type: \"step\" | \"parallel\" | \"race\" | \"decision\" | \"start\" | \"end\" | \"stream\";\n label: string[];\n state: string;\n x: number;\n y: number;\n width: number;\n height: number;\n centerX: number; // Center X for connections\n bottomY: number; // Bottom Y for outgoing connections\n children?: LayoutNode[];\n metadata?: {\n isWinner?: boolean;\n heat?: number;\n verticalLayout?: boolean;\n streamState?: \"active\" | \"closed\" | \"error\";\n backpressureOccurred?: boolean;\n };\n}\n\n// =============================================================================\n// Text Wrapping & Measurement\n// =============================================================================\n\nconst MIN_BOX_WIDTH = 11;\nconst BOX_PADDING = 2;\nconst VERTICAL_GAP = 2;\nconst HORIZONTAL_GAP = 3;\n\nfunction wrapText(text: string, maxWidth: number): string[] {\n if (text.length <= maxWidth) return [text];\n const words = text.split(\" \");\n const lines: string[] = [];\n let current = \"\";\n for (const word of words) {\n if (!current) {\n current = word;\n } else if (current.length + 1 + word.length <= maxWidth) {\n current += \" \" + word;\n } else {\n lines.push(current);\n current = word;\n }\n }\n if (current) lines.push(current);\n if (lines.length === 0) {\n for (let i = 0; i < text.length; i += maxWidth) {\n lines.push(text.slice(i, i + maxWidth));\n }\n }\n return lines;\n}\n\nfunction getSymbol(state: string): string {\n switch (state) {\n case \"success\": return \"✓\";\n case \"error\": return \"✗\";\n case \"running\": return \"⟳\";\n case \"pending\": return \"○\";\n case \"aborted\":\n case \"skipped\": return \"⊘\";\n case \"cached\": return \"↺\";\n default: return \"○\";\n }\n}\n\n// =============================================================================\n// Layout Algorithm\n// =============================================================================\n\nfunction layoutWorkflow(\n ir: WorkflowIR,\n options: RenderOptions,\n canvasWidth: number\n): { nodes: LayoutNode[]; totalHeight: number } {\n const showStartEnd = (options as FlowchartRenderOptions).showStartEnd ?? true;\n const enhanced = options as EnhancedRenderOptions;\n const centerX = Math.floor(canvasWidth / 2);\n const nodes: LayoutNode[] = [];\n let currentY = 0;\n\n // Start node\n if (showStartEnd) {\n const startNode = createSimpleNode(\"start\", \"start\", [\"▶ Start\"], \"success\", centerX, currentY);\n nodes.push(startNode);\n currentY = startNode.bottomY + VERTICAL_GAP;\n }\n\n // Layout workflow children sequentially\n for (const child of ir.root.children) {\n const layoutResult = layoutFlowNode(child, centerX, currentY, canvasWidth - 4, options, enhanced);\n nodes.push(layoutResult.node);\n currentY = layoutResult.bottomY + VERTICAL_GAP;\n }\n\n // End node (terminal states: success, error, aborted)\n const terminalStates = [\"success\", \"error\", \"aborted\"] as const;\n if (showStartEnd && terminalStates.includes(ir.root.state as (typeof terminalStates)[number])) {\n const endLabel =\n ir.root.state === \"success\" ? \"✓ Done\"\n : ir.root.state === \"error\" ? \"✗ Failed\"\n : \"⊘ Cancelled\";\n const endNode = createSimpleNode(\"end\", \"end\", [endLabel], ir.root.state, centerX, currentY);\n nodes.push(endNode);\n currentY = endNode.bottomY;\n }\n\n return { nodes, totalHeight: currentY + 2 };\n}\n\nfunction createSimpleNode(\n id: string,\n type: LayoutNode[\"type\"],\n label: string[],\n state: string,\n centerX: number,\n y: number\n): LayoutNode {\n const maxLabelLen = Math.max(...label.map(l => stripAnsi(l).length));\n const width = Math.max(MIN_BOX_WIDTH, maxLabelLen + BOX_PADDING * 2);\n const height = label.length + 2;\n const x = centerX - Math.floor(width / 2);\n return {\n id,\n type,\n label,\n state,\n x,\n y,\n width,\n height,\n centerX,\n bottomY: y + height - 1,\n };\n}\n\ninterface LayoutFlowResult {\n node: LayoutNode;\n bottomY: number;\n}\n\nfunction layoutFlowNode(\n node: FlowNode,\n centerX: number,\n startY: number,\n maxWidth: number,\n options: RenderOptions,\n enhanced?: EnhancedRenderOptions\n): LayoutFlowResult {\n if (isStepNode(node)) {\n return layoutStepNode(node, centerX, startY, maxWidth, options, enhanced);\n }\n if (isParallelNode(node) || isRaceNode(node)) {\n return layoutBranchingNode(node, centerX, startY, maxWidth, options, enhanced);\n }\n if (isDecisionNode(node)) {\n return layoutDecisionNode(node, centerX, startY, maxWidth, options, enhanced);\n }\n if (isStreamNode(node)) {\n return layoutStreamNode(node, centerX, startY, maxWidth, options, enhanced);\n }\n // Fallback\n const fallback = createSimpleNode(node.id, \"step\", [\"?\"], node.state, centerX, startY);\n return { node: fallback, bottomY: fallback.bottomY };\n}\n\nfunction layoutStepNode(\n node: StepNode,\n centerX: number,\n startY: number,\n maxWidth: number,\n options: RenderOptions,\n enhanced?: EnhancedRenderOptions\n): LayoutFlowResult {\n const name = node.name ?? node.key ?? \"step\";\n const symbol = getSymbol(node.state);\n const lines: string[] = [];\n\n // Main label (include key if showKeys is true)\n let mainLabel = `${symbol} ${name}`;\n if (options.showKeys && node.key && node.name) {\n mainLabel += ` [${node.key}]`;\n }\n const innerWidth = Math.min(maxWidth - BOX_PADDING * 2, 40);\n lines.push(...wrapText(mainLabel, innerWidth));\n\n // Timing\n if (options.showTimings && node.durationMs !== undefined) {\n lines.push(`[${formatDuration(node.durationMs)}]`);\n }\n\n // Retry/timeout\n if (node.retryCount && node.retryCount > 0) {\n lines.push(`${node.retryCount}x retry`);\n }\n if (node.timedOut) {\n lines.push(\"timeout\");\n }\n\n // Sparkline (lookup order: key ?? name ?? id, like analyzer)\n if (enhanced?.showSparklines && enhanced.timingHistory) {\n const history =\n enhanced.timingHistory.get(node.key ?? \"\") ??\n enhanced.timingHistory.get(node.name ?? \"\") ??\n enhanced.timingHistory.get(node.id);\n if (history && history.length > 1) {\n lines.push(renderSparkline(history, 8));\n }\n }\n\n const labelWidth = Math.max(...lines.map(l => stripAnsi(l).length));\n const width = Math.max(MIN_BOX_WIDTH, labelWidth + BOX_PADDING * 2);\n const height = lines.length + 2;\n const x = centerX - Math.floor(width / 2);\n\n // Heat - lookup order matches performance analyzer: key ?? name ?? id\n let heat: number | undefined;\n if (enhanced?.showHeatmap && enhanced.heatmapData) {\n const lookupKey = node.key ?? node.name ?? node.id;\n heat = enhanced.heatmapData.heat.get(node.id) ?? enhanced.heatmapData.heat.get(lookupKey);\n }\n\n const layoutNode: LayoutNode = {\n id: node.id,\n type: \"step\",\n label: lines,\n state: node.state,\n x,\n y: startY,\n width,\n height,\n centerX,\n bottomY: startY + height - 1,\n metadata: heat !== undefined ? { heat } : undefined,\n };\n\n return { node: layoutNode, bottomY: layoutNode.bottomY };\n}\n\nfunction layoutStreamNode(\n node: StreamNode,\n centerX: number,\n startY: number,\n maxWidth: number,\n options: RenderOptions,\n _enhanced?: EnhancedRenderOptions\n): LayoutFlowResult {\n const name = `stream:${node.namespace}`;\n const symbol = node.streamState === \"active\" ? \"⟳\"\n : node.streamState === \"closed\" ? \"✓\"\n : \"✗\";\n const lines: string[] = [];\n\n // Main label\n const mainLabel = `${symbol} ${name}`;\n const innerWidth = Math.min(maxWidth - BOX_PADDING * 2, 40);\n lines.push(...wrapText(mainLabel, innerWidth));\n\n // Write/Read counts\n lines.push(`W:${node.writeCount} R:${node.readCount}`);\n\n // Timing\n if (options.showTimings && node.durationMs !== undefined) {\n lines.push(`[${formatDuration(node.durationMs)}]`);\n }\n\n // Backpressure indicator\n if (node.backpressureOccurred) {\n lines.push(\"backpressure\");\n }\n\n const labelWidth = Math.max(...lines.map(l => stripAnsi(l).length));\n const width = Math.max(MIN_BOX_WIDTH, labelWidth + BOX_PADDING * 2);\n const height = lines.length + 2;\n const x = centerX - Math.floor(width / 2);\n\n const layoutNode: LayoutNode = {\n id: node.id,\n type: \"stream\",\n label: lines,\n state: node.state,\n x,\n y: startY,\n width,\n height,\n centerX,\n bottomY: startY + height - 1,\n metadata: {\n streamState: node.streamState,\n backpressureOccurred: node.backpressureOccurred,\n },\n };\n\n return { node: layoutNode, bottomY: layoutNode.bottomY };\n}\n\nfunction layoutBranchingNode(\n node: ParallelNode | RaceNode,\n centerX: number,\n startY: number,\n maxWidth: number,\n options: RenderOptions,\n enhanced?: EnhancedRenderOptions\n): LayoutFlowResult {\n const isRace = isRaceNode(node);\n const name = node.name ?? (isRace ? \"race\" : \"parallel\");\n const symbol = isRace ? \"⚡\" : \"⫘\";\n const headerLabel = `${symbol} ${name}`;\n\n // No children - simple box\n if (node.children.length === 0) {\n const lines = [headerLabel, \"(not tracked)\"];\n const result = createSimpleNode(node.id, isRace ? \"race\" : \"parallel\", lines, node.state, centerX, startY);\n return { node: result, bottomY: result.bottomY };\n }\n\n // Calculate child widths first\n const childMaxWidth = Math.floor((maxWidth - HORIZONTAL_GAP * (node.children.length - 1)) / node.children.length);\n const childWidths: number[] = [];\n\n for (const child of node.children) {\n const measured = measureFlowNode(child, Math.max(childMaxWidth, MIN_BOX_WIDTH), options, enhanced);\n childWidths.push(measured.width);\n }\n\n const totalChildWidth = childWidths.reduce((a, b) => a + b, 0) + HORIZONTAL_GAP * (node.children.length - 1);\n\n // Check if horizontal layout would overflow - fall back to vertical\n const useVerticalLayout = totalChildWidth > maxWidth && node.children.length > 1;\n\n // Header\n const headerWidth = Math.max(MIN_BOX_WIDTH, headerLabel.length + BOX_PADDING * 2);\n const headerHeight = 3;\n const headerX = centerX - Math.floor(headerWidth / 2);\n\n let currentY = startY;\n\n // Header node (we won't add it as separate node, integrate into parent)\n currentY += headerHeight;\n currentY += 1; // Fork line row\n currentY += 1; // Arrow row\n\n // Position children\n const children: LayoutNode[] = [];\n\n if (useVerticalLayout) {\n // Vertical layout - stack children sequentially\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n const result = layoutFlowNode(child, centerX, currentY, maxWidth, options, enhanced);\n\n // Mark winner\n if (isRace && isRaceNode(node) && node.winnerId === child.id) {\n result.node.metadata = { ...result.node.metadata, isWinner: true };\n }\n\n children.push(result.node);\n currentY = result.bottomY + VERTICAL_GAP;\n }\n } else {\n // Horizontal layout - spread children side by side\n let childX = centerX - Math.floor(totalChildWidth / 2);\n\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n const childCenterX = childX + Math.floor(childWidths[i] / 2);\n const result = layoutFlowNode(child, childCenterX, currentY, childWidths[i], options, enhanced);\n\n // Mark winner\n if (isRace && isRaceNode(node) && node.winnerId === child.id) {\n result.node.metadata = { ...result.node.metadata, isWinner: true };\n }\n\n children.push(result.node);\n childX += childWidths[i] + HORIZONTAL_GAP;\n }\n }\n\n // Calculate bottom after all children\n const childrenBottomY = Math.max(...children.map(c => c.bottomY));\n\n // For single child or vertical layout, no join line needed\n // For multiple children in horizontal layout, we have: child bottom -> join line -> vertical segment\n const needsJoinLine = !useVerticalLayout && children.length > 1;\n const totalBottomY = needsJoinLine\n ? childrenBottomY + 2 // Horizontal multi-child: join line + one row below\n : childrenBottomY; // Single child or vertical: no join line\n\n // Create parent node with children\n const parentNode: LayoutNode = {\n id: node.id,\n type: isRace ? \"race\" : \"parallel\",\n label: [headerLabel],\n state: node.state,\n x: headerX,\n y: startY,\n width: headerWidth,\n height: headerHeight,\n centerX,\n bottomY: totalBottomY,\n children,\n metadata: { verticalLayout: useVerticalLayout },\n };\n\n return { node: parentNode, bottomY: totalBottomY };\n}\n\nfunction layoutDecisionNode(\n node: DecisionNode,\n centerX: number,\n startY: number,\n maxWidth: number,\n options: RenderOptions,\n enhanced?: EnhancedRenderOptions\n): LayoutFlowResult {\n const name = node.name ?? \"decision\";\n const condition = node.condition ? ` (${node.condition.slice(0, 20)})` : \"\";\n const headerLabel = `◇ ${name}${condition}`;\n\n // Wrap header label properly\n const innerWidth = Math.min(maxWidth - BOX_PADDING * 2, 40);\n const labelLines = wrapText(headerLabel, innerWidth);\n const labelWidth = Math.max(...labelLines.map(l => stripAnsi(l).length));\n const headerWidth = Math.max(MIN_BOX_WIDTH, labelWidth + BOX_PADDING * 2);\n const headerHeight = labelLines.length + 2; // Dynamic height based on wrapped lines\n const headerX = centerX - Math.floor(headerWidth / 2);\n\n // Prefer taken branch only if it has children; otherwise use first branch with children\n const takenBranch = node.branches.find(b => b.taken);\n const branchToRender =\n (takenBranch && takenBranch.children.length > 0)\n ? takenBranch\n : node.branches.find(b => b.children.length > 0);\n\n if (!branchToRender || branchToRender.children.length === 0) {\n // No children in any branch - just show header\n const result: LayoutNode = {\n id: node.id,\n type: \"decision\",\n label: labelLines,\n state: node.state,\n x: headerX,\n y: startY,\n width: headerWidth,\n height: headerHeight,\n centerX,\n bottomY: startY + headerHeight - 1,\n };\n return { node: result, bottomY: result.bottomY };\n }\n\n // Layout children of branch to render\n let currentY = startY + headerHeight + VERTICAL_GAP;\n const children: LayoutNode[] = [];\n\n for (const child of branchToRender.children) {\n const result = layoutFlowNode(child, centerX, currentY, maxWidth, options, enhanced);\n children.push(result.node);\n currentY = result.bottomY + VERTICAL_GAP;\n }\n\n const bottomY = children.length > 0 ? children[children.length - 1].bottomY : startY + headerHeight - 1;\n\n const parentNode: LayoutNode = {\n id: node.id,\n type: \"decision\",\n label: labelLines,\n state: node.state,\n x: headerX,\n y: startY,\n width: headerWidth,\n height: headerHeight,\n centerX,\n bottomY,\n children,\n };\n\n return { node: parentNode, bottomY };\n}\n\nfunction measureFlowNode(\n node: FlowNode,\n maxWidth: number,\n options: RenderOptions,\n enhanced?: EnhancedRenderOptions\n): { width: number; height: number } {\n if (isStepNode(node)) {\n const name = node.name ?? node.key ?? \"step\";\n const symbol = getSymbol(node.state);\n let lineCount = 1;\n if (options.showTimings && node.durationMs !== undefined) lineCount++;\n if (node.retryCount && node.retryCount > 0) lineCount++;\n if (node.timedOut) lineCount++;\n if (enhanced?.showSparklines && (enhanced.timingHistory?.has(node.key ?? \"\") || enhanced.timingHistory?.has(node.name ?? \"\") || enhanced.timingHistory?.has(node.id))) lineCount++;\n\n let mainLabel = `${symbol} ${name}`;\n if (options.showKeys && node.key && node.name) {\n mainLabel += ` [${node.key}]`;\n }\n const width = Math.min(maxWidth, Math.max(MIN_BOX_WIDTH, mainLabel.length + BOX_PADDING * 2));\n const height = lineCount + 2;\n return { width, height };\n }\n\n if (isParallelNode(node) || isRaceNode(node)) {\n if (node.children.length === 0) {\n return { width: MIN_BOX_WIDTH + 4, height: 4 };\n }\n const childMaxWidth = Math.floor(maxWidth / node.children.length);\n let totalWidth = 0;\n let maxHeight = 0;\n for (const child of node.children) {\n const m = measureFlowNode(child, childMaxWidth, options, enhanced);\n totalWidth += m.width;\n maxHeight = Math.max(maxHeight, m.height);\n }\n totalWidth += HORIZONTAL_GAP * (node.children.length - 1);\n return { width: Math.max(totalWidth, MIN_BOX_WIDTH), height: 3 + 2 + maxHeight + 2 };\n }\n\n if (isDecisionNode(node)) {\n const takenBranch = node.branches.find(b => b.taken);\n let childHeight = 0;\n if (takenBranch) {\n for (const child of takenBranch.children) {\n const m = measureFlowNode(child, maxWidth, options, enhanced);\n childHeight += m.height + VERTICAL_GAP;\n }\n }\n return { width: Math.min(maxWidth, 30), height: 3 + childHeight };\n }\n\n if (isStreamNode(node)) {\n const name = `stream:${node.namespace}`;\n let lineCount = 2; // name + counts\n if (options.showTimings && node.durationMs !== undefined) lineCount++;\n if (node.backpressureOccurred) lineCount++;\n const width = Math.min(maxWidth, Math.max(MIN_BOX_WIDTH, name.length + BOX_PADDING * 2 + 4));\n const height = lineCount + 2;\n return { width, height };\n }\n\n return { width: MIN_BOX_WIDTH, height: 3 };\n}\n\n// =============================================================================\n// Rendering - Hierarchical with proper connections\n// =============================================================================\n\nfunction renderNodes(\n canvas: Canvas,\n nodes: LayoutNode[],\n options: RenderOptions\n): void {\n const colors = { ...defaultColorScheme, ...options.colors };\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n const isLast = i === nodes.length - 1;\n\n // Render this node and its children\n renderNode(canvas, node, colors);\n\n // Draw connection to next sibling node\n if (!isLast) {\n const nextNode = nodes[i + 1];\n const fromX = node.centerX;\n const fromY = node.bottomY + 1;\n const toX = nextNode.centerX;\n const toY = nextNode.y - 1;\n\n // Vertical line\n drawVerticalLine(canvas, fromX, fromY, toY - 1);\n // Arrow\n drawArrow(canvas, toX, toY);\n }\n }\n}\n\nfunction renderNode(\n canvas: Canvas,\n node: LayoutNode,\n colors: Record<string, string>\n): void {\n // Draw the box\n const hasTopConnector = node.type !== \"start\";\n const hasBottomConnector = node.type !== \"end\" && (!node.children || node.children.length === 0);\n\n drawBox(canvas, node.x, node.y, node.width, node.height);\n\n // Add connector points\n if (hasTopConnector) {\n setChar(canvas, node.centerX, node.y, CHARS.teeUp);\n }\n if (hasBottomConnector || (node.children && node.children.length > 0)) {\n setChar(canvas, node.centerX, node.y + node.height - 1, CHARS.teeDown);\n }\n\n // Draw label\n const innerWidth = node.width - BOX_PADDING * 2;\n const color = getNodeColor(node, colors);\n\n for (let j = 0; j < node.label.length; j++) {\n const line = node.label[j];\n const lineX = node.x + 1 + Math.floor((innerWidth - stripAnsi(line).length) / 2);\n const lineY = node.y + 1 + j;\n drawText(canvas, lineX, lineY, line, color);\n }\n\n // Winner indicator\n if (node.metadata?.isWinner) {\n drawText(canvas, node.x + node.width - 2, node.y, \"🏆\");\n }\n\n // Render children with proper fork/join connections\n if (node.children && node.children.length > 0) {\n if (node.type === \"parallel\" || node.type === \"race\") {\n renderBranchingChildren(canvas, node, colors);\n } else {\n // Sequential children (e.g., decision taken branch)\n renderSequentialChildren(canvas, node, colors);\n }\n }\n}\n\nfunction renderBranchingChildren(\n canvas: Canvas,\n parent: LayoutNode,\n colors: Record<string, string>\n): void {\n const children = parent.children!;\n if (children.length === 0) return;\n\n const forkY = parent.y + parent.height;\n const forkX = parent.centerX;\n\n // Check if using vertical layout (fallback for overflow)\n const useVerticalLayout = parent.metadata?.verticalLayout === true;\n\n if (useVerticalLayout) {\n // Vertical layout - render as sequential children\n // Draw connection from parent header to first child\n const firstChild = children[0];\n drawVerticalLine(canvas, forkX, forkY, firstChild.y - 2);\n drawArrow(canvas, firstChild.centerX, firstChild.y - 1);\n\n // Render children and connections between them\n for (let i = 0; i < children.length; i++) {\n const child = children[i];\n renderNode(canvas, child, colors);\n\n if (i < children.length - 1) {\n const nextChild = children[i + 1];\n drawVerticalLine(canvas, child.centerX, child.bottomY + 1, nextChild.y - 2);\n drawArrow(canvas, nextChild.centerX, nextChild.y - 1);\n }\n }\n return;\n }\n\n // Horizontal layout - draw fork/join pattern\n if (children.length === 1) {\n // Single child - just vertical line\n drawVerticalLine(canvas, forkX, forkY, children[0].y - 2);\n drawArrow(canvas, children[0].centerX, children[0].y - 1);\n } else {\n // Multiple children - fork pattern\n const childCenters = children.map(c => c.centerX);\n const minX = Math.min(...childCenters);\n const maxX = Math.max(...childCenters);\n\n // Vertical line down from parent\n drawVerticalLine(canvas, forkX, forkY, forkY + 1);\n\n // Horizontal fork line\n drawHorizontalLine(canvas, forkY + 1, minX, maxX);\n\n // Set proper junction at center\n setChar(canvas, forkX, forkY + 1, CHARS.teeUp);\n\n // Vertical lines down to each child and arrows\n for (const child of children) {\n const cx = child.centerX;\n if (cx === minX) {\n setChar(canvas, cx, forkY + 1, CHARS.topLeft);\n } else if (cx === maxX) {\n setChar(canvas, cx, forkY + 1, CHARS.topRight);\n } else if (cx !== forkX) {\n setChar(canvas, cx, forkY + 1, CHARS.teeDown);\n }\n drawVerticalLine(canvas, cx, forkY + 2, child.y - 2);\n drawArrow(canvas, cx, child.y - 1);\n }\n }\n\n // Render each child\n for (const child of children) {\n renderNode(canvas, child, colors);\n }\n\n // Draw join - gather all children back together (only for horizontal multi-child)\n if (children.length > 1) {\n const childBottoms = children.map(c => c.bottomY);\n const maxChildBottom = Math.max(...childBottoms);\n const joinY = maxChildBottom + 1;\n\n const childCenters = children.map(c => c.centerX);\n const minX = Math.min(...childCenters);\n const maxX = Math.max(...childCenters);\n\n // Vertical lines up from each child bottom to join line\n for (const child of children) {\n if (child.bottomY < maxChildBottom) {\n drawVerticalLine(canvas, child.centerX, child.bottomY + 1, joinY - 1);\n }\n }\n\n // Horizontal join line\n drawHorizontalLine(canvas, joinY, minX, maxX);\n\n // Set proper junction characters\n for (const child of children) {\n const cx = child.centerX;\n if (cx === minX) {\n setChar(canvas, cx, joinY, CHARS.bottomLeft);\n } else if (cx === maxX) {\n setChar(canvas, cx, joinY, CHARS.bottomRight);\n } else {\n setChar(canvas, cx, joinY, CHARS.teeUp);\n }\n }\n\n // Tee down at join center and vertical segment below\n setChar(canvas, parent.centerX, joinY, CHARS.teeDown);\n // Draw vertical line from join to parent's bottomY for next sibling connection\n setChar(canvas, parent.centerX, joinY + 1, CHARS.vertical);\n }\n}\n\nfunction renderSequentialChildren(\n canvas: Canvas,\n parent: LayoutNode,\n colors: Record<string, string>\n): void {\n const children = parent.children!;\n if (children.length === 0) return;\n\n // Draw connection from parent to first child\n const fromY = parent.y + parent.height;\n const firstChild = children[0];\n drawVerticalLine(canvas, parent.centerX, fromY, firstChild.y - 2);\n drawArrow(canvas, firstChild.centerX, firstChild.y - 1);\n\n // Render children and connections between them\n for (let i = 0; i < children.length; i++) {\n const child = children[i];\n renderNode(canvas, child, colors);\n\n if (i < children.length - 1) {\n const nextChild = children[i + 1];\n drawVerticalLine(canvas, child.centerX, child.bottomY + 1, nextChild.y - 2);\n drawArrow(canvas, nextChild.centerX, nextChild.y - 1);\n }\n }\n}\n\n// Stream-specific ANSI colors\nconst STREAM_COLORS = {\n active: \"\\x1b[36m\", // Cyan for active streams\n closed: \"\\x1b[32m\", // Green for closed (success)\n error: \"\\x1b[31m\", // Red for error\n} as const;\n\nfunction getNodeColor(node: LayoutNode, colors: Record<string, string>): string | undefined {\n if (node.metadata?.heat !== undefined) {\n const level = getHeatLevel(node.metadata.heat);\n return HEAT_COLORS[level] || undefined;\n }\n // Stream nodes use stream-specific colors based on streamState\n if (node.type === \"stream\" && node.metadata?.streamState) {\n return STREAM_COLORS[node.metadata.streamState] || undefined;\n }\n return colors[node.state] || undefined;\n}\n\n// =============================================================================\n// Main Renderer\n// =============================================================================\n\nexport function flowchartRenderer(): Renderer {\n return {\n name: \"flowchart\",\n supportsLive: false,\n\n render(ir: WorkflowIR, options: RenderOptions): string {\n const width = options.terminalWidth ?? 80;\n const { nodes, totalHeight } = layoutWorkflow(ir, options, width);\n const canvas = createCanvas(width, totalHeight);\n renderNodes(canvas, nodes, options);\n return canvasToString(canvas);\n },\n };\n}\n","/**\n * Logger Renderer - Outputs structured JSON optimized for logging systems.\n *\n * Works with any structured logger (Pino, Winston, Bunyan, console).\n * Includes workflow summary, step details, and optional ASCII diagram.\n *\n * @example\n * ```typescript\n * const logData = JSON.parse(viz.renderAs('logger'));\n * logger.info(logData, 'Workflow completed');\n * ```\n */\n\nimport type {\n Renderer,\n RenderOptions,\n WorkflowIR,\n FlowNode,\n StepNode,\n WorkflowHooks,\n} from \"../types\";\nimport { isStepNode, isSequenceNode, isParallelNode, isRaceNode, isDecisionNode } from \"../types\";\nimport { asciiRenderer } from \"./ascii\";\nimport { flowchartRenderer } from \"./flowchart\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Step log entry with execution details.\n */\nexport interface StepLog {\n id: string;\n name: string;\n key?: string;\n state: string;\n durationMs?: number;\n startTs?: number;\n endTs?: number;\n retryCount?: number;\n timedOut?: boolean;\n timeoutMs?: number;\n error?: string;\n}\n\n/**\n * Hook execution log entry.\n */\nexport interface HookLog {\n shouldRun?: {\n result?: boolean;\n durationMs?: number;\n error?: string;\n };\n onBeforeStart?: {\n durationMs?: number;\n error?: string;\n };\n onAfterStep?: Array<{\n stepKey: string;\n durationMs?: number;\n error?: string;\n }>;\n}\n\n/**\n * Summary statistics for the workflow.\n */\nexport interface WorkflowSummary {\n totalSteps: number;\n successCount: number;\n errorCount: number;\n cacheHits: number;\n skippedCount: number;\n totalRetries: number;\n slowestStep?: { name: string; durationMs: number };\n}\n\n/**\n * Complete logger output structure.\n */\nexport interface LoggerOutput {\n workflow: {\n id: string;\n name?: string;\n state: string;\n durationMs?: number;\n startedAt?: number;\n completedAt?: number;\n };\n steps: StepLog[];\n summary: WorkflowSummary;\n hooks?: HookLog;\n diagram?: string;\n}\n\n/**\n * Extended render options for logger renderer.\n */\nexport interface LoggerRenderOptions extends RenderOptions {\n /** Include ASCII diagram in output (default: true) */\n includeDiagram?: boolean;\n /** Strip ANSI color codes from diagram (default: true) */\n stripAnsiColors?: boolean;\n /** Diagram format: 'ascii' (tree-style) or 'flowchart' (boxes/arrows) (default: 'ascii') */\n diagramFormat?: \"ascii\" | \"flowchart\";\n}\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Strip ANSI escape codes from a string.\n */\nfunction stripAnsi(str: string): string {\n // eslint-disable-next-line no-control-regex\n return str.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n}\n\n/**\n * Collect all step nodes from the IR tree.\n */\nfunction collectSteps(nodes: FlowNode[]): StepNode[] {\n const steps: StepNode[] = [];\n\n function walk(nodeList: FlowNode[]): void {\n for (const node of nodeList) {\n if (isStepNode(node)) {\n steps.push(node);\n } else if (isSequenceNode(node)) {\n walk(node.children);\n } else if (isParallelNode(node) || isRaceNode(node)) {\n walk(node.children);\n } else if (isDecisionNode(node)) {\n for (const branch of node.branches) {\n if (branch.taken) {\n walk(branch.children);\n }\n }\n }\n }\n }\n\n walk(nodes);\n return steps;\n}\n\n/**\n * Convert a step node to a log entry.\n */\nfunction stepToLog(step: StepNode): StepLog {\n const log: StepLog = {\n id: step.id,\n name: step.name ?? step.key ?? step.id,\n state: step.state,\n };\n\n if (step.key) log.key = step.key;\n if (step.durationMs !== undefined) log.durationMs = step.durationMs;\n if (step.startTs !== undefined) log.startTs = step.startTs;\n if (step.endTs !== undefined) log.endTs = step.endTs;\n if (step.retryCount !== undefined && step.retryCount > 0) log.retryCount = step.retryCount;\n if (step.timedOut) {\n log.timedOut = true;\n if (step.timeoutMs !== undefined) log.timeoutMs = step.timeoutMs;\n }\n if (step.error !== undefined) {\n log.error = typeof step.error === \"string\" ? step.error : String(step.error);\n }\n\n return log;\n}\n\n/**\n * Calculate summary statistics from steps.\n */\nfunction calculateSummary(steps: StepNode[]): WorkflowSummary {\n let successCount = 0;\n let errorCount = 0;\n let cacheHits = 0;\n let skippedCount = 0;\n let totalRetries = 0;\n let slowestStep: { name: string; durationMs: number } | undefined;\n\n for (const step of steps) {\n if (step.state === \"success\") successCount++;\n if (step.state === \"error\") errorCount++;\n if (step.state === \"cached\") cacheHits++;\n if (step.state === \"skipped\") skippedCount++;\n if (step.retryCount !== undefined) totalRetries += step.retryCount;\n\n if (step.durationMs !== undefined) {\n if (!slowestStep || step.durationMs > slowestStep.durationMs) {\n slowestStep = {\n name: step.name ?? step.key ?? step.id,\n durationMs: step.durationMs,\n };\n }\n }\n }\n\n return {\n totalSteps: steps.length,\n successCount,\n errorCount,\n cacheHits,\n skippedCount,\n totalRetries,\n slowestStep,\n };\n}\n\n/**\n * Convert hooks to log format.\n */\nfunction hooksToLog(hooks: WorkflowHooks): HookLog {\n const log: HookLog = {};\n\n if (hooks.shouldRun) {\n log.shouldRun = {\n result: hooks.shouldRun.context?.result,\n durationMs: hooks.shouldRun.durationMs,\n };\n if (hooks.shouldRun.error !== undefined && hooks.shouldRun.error !== null) {\n log.shouldRun.error = String(hooks.shouldRun.error);\n }\n }\n\n if (hooks.onBeforeStart) {\n log.onBeforeStart = {\n durationMs: hooks.onBeforeStart.durationMs,\n };\n if (hooks.onBeforeStart.error !== undefined && hooks.onBeforeStart.error !== null) {\n log.onBeforeStart.error = String(hooks.onBeforeStart.error);\n }\n }\n\n if (hooks.onAfterStep.size > 0) {\n log.onAfterStep = [];\n for (const [stepKey, hook] of hooks.onAfterStep) {\n const entry: { stepKey: string; durationMs?: number; error?: string } = { stepKey };\n if (hook.durationMs !== undefined) entry.durationMs = hook.durationMs;\n if (hook.error !== undefined && hook.error !== null) entry.error = String(hook.error);\n log.onAfterStep.push(entry);\n }\n }\n\n return log;\n}\n\n/**\n * Build the complete logger output from IR.\n */\nfunction buildLoggerOutput(ir: WorkflowIR, options: LoggerRenderOptions): LoggerOutput {\n const root = ir.root;\n const steps = collectSteps(root.children);\n const includeDiagram = options.includeDiagram ?? true;\n const stripColors = options.stripAnsiColors ?? true;\n\n const output: LoggerOutput = {\n workflow: {\n id: root.workflowId,\n name: root.name,\n state: root.state,\n durationMs: root.durationMs,\n startedAt: root.startTs,\n completedAt: root.endTs,\n },\n steps: steps.map(stepToLog),\n summary: calculateSummary(steps),\n };\n\n // Add hooks if present\n if (ir.hooks) {\n const hookLog = hooksToLog(ir.hooks);\n if (Object.keys(hookLog).length > 0) {\n output.hooks = hookLog;\n }\n }\n\n // Add diagram if requested\n if (includeDiagram) {\n const diagramFormat = options.diagramFormat ?? \"ascii\";\n const renderer = diagramFormat === \"flowchart\"\n ? flowchartRenderer()\n : asciiRenderer();\n let diagram = renderer.render(ir, options);\n if (stripColors) {\n diagram = stripAnsi(diagram);\n }\n output.diagram = diagram;\n }\n\n return output;\n}\n\n// =============================================================================\n// Renderer\n// =============================================================================\n\n/**\n * Create a logger renderer that outputs structured JSON.\n *\n * @example\n * ```typescript\n * const viz = createVisualizer({ workflowName: 'checkout' });\n * // ... run workflow ...\n *\n * const logData = JSON.parse(viz.renderAs('logger'));\n * logger.info(logData, 'Workflow completed');\n * ```\n */\nexport function loggerRenderer(): Renderer {\n return {\n name: \"logger\",\n supportsLive: false,\n render(ir: WorkflowIR, options: RenderOptions): string {\n const loggerOptions = options as LoggerRenderOptions;\n const output = buildLoggerOutput(ir, loggerOptions);\n return JSON.stringify(output);\n },\n };\n}\n","/**\n * HTML Visualizer Styles\n *\n * CSS styles for the interactive HTML workflow visualizer.\n * Supports light, dark, and auto (system preference) themes.\n */\n\n/**\n * Generate CSS styles for the HTML visualizer.\n */\nexport function generateStyles(theme: \"light\" | \"dark\" | \"auto\"): string {\n const lightColors = {\n bg: \"#ffffff\",\n bgSecondary: \"#f8f9fa\",\n text: \"#212529\",\n textMuted: \"#6c757d\",\n border: \"#dee2e6\",\n primary: \"#0d6efd\",\n success: \"#198754\",\n error: \"#dc3545\",\n warning: \"#ffc107\",\n info: \"#0dcaf0\",\n muted: \"#6c757d\",\n // Node colors\n nodePending: \"#e9ecef\",\n nodeRunning: \"#fff3cd\",\n nodeSuccess: \"#d1e7dd\",\n nodeError: \"#f8d7da\",\n nodeAborted: \"#e9ecef\",\n nodeCached: \"#cfe2ff\",\n nodeSkipped: \"#f8f9fa\",\n // Heatmap colors\n heatCold: \"#0d6efd\",\n heatCool: \"#0dcaf0\",\n heatNeutral: \"#6c757d\",\n heatWarm: \"#ffc107\",\n heatHot: \"#fd7e14\",\n heatCritical: \"#dc3545\",\n };\n\n const darkColors = {\n bg: \"#212529\",\n bgSecondary: \"#343a40\",\n text: \"#f8f9fa\",\n textMuted: \"#adb5bd\",\n border: \"#495057\",\n primary: \"#0d6efd\",\n success: \"#198754\",\n error: \"#dc3545\",\n warning: \"#ffc107\",\n info: \"#0dcaf0\",\n muted: \"#6c757d\",\n // Node colors\n nodePending: \"#495057\",\n nodeRunning: \"#664d03\",\n nodeSuccess: \"#0f5132\",\n nodeError: \"#842029\",\n nodeAborted: \"#495057\",\n nodeCached: \"#084298\",\n nodeSkipped: \"#343a40\",\n // Heatmap colors\n heatCold: \"#0d6efd\",\n heatCool: \"#0dcaf0\",\n heatNeutral: \"#6c757d\",\n heatWarm: \"#ffc107\",\n heatHot: \"#fd7e14\",\n heatCritical: \"#dc3545\",\n };\n\n const generateThemeVars = (colors: typeof lightColors) => `\n --bg: ${colors.bg};\n --bg-secondary: ${colors.bgSecondary};\n --text: ${colors.text};\n --text-muted: ${colors.textMuted};\n --border: ${colors.border};\n --primary: ${colors.primary};\n --success: ${colors.success};\n --error: ${colors.error};\n --warning: ${colors.warning};\n --info: ${colors.info};\n --muted: ${colors.muted};\n --node-pending: ${colors.nodePending};\n --node-running: ${colors.nodeRunning};\n --node-success: ${colors.nodeSuccess};\n --node-error: ${colors.nodeError};\n --node-aborted: ${colors.nodeAborted};\n --node-cached: ${colors.nodeCached};\n --node-skipped: ${colors.nodeSkipped};\n --heat-cold: ${colors.heatCold};\n --heat-cool: ${colors.heatCool};\n --heat-neutral: ${colors.heatNeutral};\n --heat-warm: ${colors.heatWarm};\n --heat-hot: ${colors.heatHot};\n --heat-critical: ${colors.heatCritical};\n `;\n\n let themeCSS: string;\n\n if (theme === \"auto\") {\n themeCSS = `\n :root {\n ${generateThemeVars(lightColors)}\n }\n @media (prefers-color-scheme: dark) {\n :root {\n ${generateThemeVars(darkColors)}\n }\n }\n `;\n } else if (theme === \"dark\") {\n themeCSS = `\n :root {\n ${generateThemeVars(darkColors)}\n }\n `;\n } else {\n themeCSS = `\n :root {\n ${generateThemeVars(lightColors)}\n }\n `;\n }\n\n return `\n${themeCSS}\n\n* {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\nbody {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n background-color: var(--bg);\n color: var(--text);\n line-height: 1.5;\n}\n\n.workflow-visualizer {\n display: flex;\n flex-direction: column;\n height: 100vh;\n overflow: hidden;\n}\n\n/* Header */\n.wv-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 20px;\n background-color: var(--bg-secondary);\n border-bottom: 1px solid var(--border);\n}\n\n.wv-header h1 {\n font-size: 1.25rem;\n font-weight: 600;\n}\n\n.wv-controls {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.wv-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 6px 12px;\n font-size: 0.875rem;\n font-weight: 500;\n border: 1px solid var(--border);\n border-radius: 6px;\n background-color: var(--bg);\n color: var(--text);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.wv-btn:hover {\n background-color: var(--bg-secondary);\n}\n\n.wv-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n background-color: var(--bg-secondary);\n color: var(--text-muted);\n}\n\n.wv-btn--primary {\n background-color: var(--primary);\n border-color: var(--primary);\n color: white;\n}\n\n.wv-btn--primary:hover {\n opacity: 0.9;\n}\n\n.wv-btn--icon {\n padding: 6px;\n min-width: 32px;\n}\n\n/* Main content area */\n.wv-main {\n display: flex;\n flex: 1;\n overflow: hidden;\n}\n\n/* Diagram container */\n.wv-diagram {\n flex: 1;\n position: relative;\n overflow: hidden;\n background-color: var(--bg);\n background-image:\n radial-gradient(circle, var(--border) 1px, transparent 1px);\n background-size: 20px 20px;\n}\n\n.wv-diagram svg {\n width: 100%;\n height: 100%;\n}\n\n/* SVG Node styles */\n.wv-node {\n cursor: pointer;\n transition: filter 0.15s ease;\n}\n\n.wv-node:hover {\n filter: brightness(1.1) drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));\n}\n\n.wv-node rect,\n.wv-node circle {\n stroke: var(--border);\n stroke-width: 2;\n transition: all 0.2s ease;\n}\n\n.wv-node:hover rect,\n.wv-node:hover circle {\n stroke-width: 3;\n}\n\n.wv-node--pending rect { fill: var(--node-pending); }\n.wv-node--running rect { fill: var(--node-running); stroke: var(--warning); }\n.wv-node--success rect { fill: var(--node-success); stroke: var(--success); }\n.wv-node--error rect { fill: var(--node-error); stroke: var(--error); }\n.wv-node--aborted rect { fill: var(--node-aborted); }\n.wv-node--cached rect { fill: var(--node-cached); stroke: var(--info); }\n.wv-node--skipped rect { fill: var(--node-skipped); opacity: 0.6; }\n\n.wv-node--selected rect {\n stroke: var(--primary);\n stroke-width: 3;\n}\n\n.wv-node text {\n fill: var(--text);\n font-size: 12px;\n font-weight: 500;\n dominant-baseline: middle;\n text-anchor: middle;\n}\n\n.wv-node .wv-node-timing {\n fill: var(--text-muted);\n font-size: 10px;\n}\n\n/* Edge styles */\n.wv-edge {\n fill: none;\n stroke: var(--border);\n stroke-width: 2;\n}\n\n.wv-edge--active {\n stroke: var(--success);\n stroke-width: 2.5;\n}\n\n.wv-edge-arrow {\n fill: var(--border);\n}\n\n/* Parallel/Race container */\n.wv-container {\n fill: transparent;\n stroke: var(--border);\n stroke-dasharray: 5 3;\n}\n\n.wv-container--parallel { stroke: var(--info); }\n.wv-container--race { stroke: var(--warning); }\n\n.wv-container-label {\n fill: var(--text-muted);\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n/* Heatmap overlay */\n.wv-node--heat-cold rect { fill: var(--heat-cold) !important; opacity: 0.7; }\n.wv-node--heat-cool rect { fill: var(--heat-cool) !important; opacity: 0.7; }\n.wv-node--heat-neutral rect { fill: var(--heat-neutral) !important; opacity: 0.7; }\n.wv-node--heat-warm rect { fill: var(--heat-warm) !important; opacity: 0.7; }\n.wv-node--heat-hot rect { fill: var(--heat-hot) !important; opacity: 0.7; }\n.wv-node--heat-critical rect { fill: var(--heat-critical) !important; opacity: 0.85; }\n\n/* Timeline scrubber */\n.wv-timeline {\n height: 80px;\n padding: 12px 20px;\n background-color: var(--bg-secondary);\n border-top: 1px solid var(--border);\n}\n\n.wv-timeline-track {\n position: relative;\n height: 24px;\n background-color: var(--bg);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.wv-timeline-progress {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background-color: var(--primary);\n opacity: 0.3;\n transition: width 0.1s ease;\n}\n\n.wv-timeline-marker {\n position: absolute;\n top: 0;\n width: 3px;\n height: 100%;\n background-color: var(--primary);\n cursor: ew-resize;\n}\n\n.wv-timeline-events {\n display: flex;\n gap: 2px;\n height: 100%;\n align-items: center;\n padding: 4px;\n}\n\n.wv-timeline-event {\n width: 4px;\n height: 16px;\n border-radius: 2px;\n background-color: var(--muted);\n}\n\n.wv-timeline-event--success { background-color: var(--success); }\n.wv-timeline-event--error { background-color: var(--error); }\n.wv-timeline-event--running { background-color: var(--warning); }\n\n.wv-timeline-controls {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n align-items: center;\n}\n\n.wv-timeline-time {\n font-size: 0.75rem;\n color: var(--text-muted);\n font-family: monospace;\n}\n\n/* Inspector panel */\n.wv-inspector {\n width: 320px;\n background-color: var(--bg-secondary);\n border-left: 1px solid var(--border);\n overflow-y: auto;\n transition: width 0.2s ease;\n}\n\n.wv-inspector--collapsed {\n width: 0;\n}\n\n.wv-inspector-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--border);\n}\n\n.wv-inspector-header h2 {\n font-size: 0.875rem;\n font-weight: 600;\n}\n\n.wv-inspector-content {\n padding: 16px;\n}\n\n.wv-inspector-section {\n margin-bottom: 20px;\n}\n\n.wv-inspector-section h3 {\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--text-muted);\n margin-bottom: 8px;\n}\n\n.wv-inspector-row {\n display: flex;\n justify-content: space-between;\n padding: 4px 0;\n font-size: 0.875rem;\n}\n\n.wv-inspector-label {\n color: var(--text-muted);\n}\n\n.wv-inspector-value {\n font-weight: 500;\n font-family: monospace;\n}\n\n.wv-inspector-value--success { color: var(--success); }\n.wv-inspector-value--error { color: var(--error); }\n.wv-inspector-value--running { color: var(--warning); }\n\n/* Status badge */\n.wv-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n font-size: 0.75rem;\n font-weight: 500;\n border-radius: 4px;\n text-transform: capitalize;\n}\n\n.wv-badge--pending { background-color: var(--node-pending); }\n.wv-badge--running { background-color: var(--node-running); color: #664d03; }\n.wv-badge--success { background-color: var(--node-success); color: #0f5132; }\n.wv-badge--error { background-color: var(--node-error); color: #842029; }\n.wv-badge--cached { background-color: var(--node-cached); color: #084298; }\n\n/* Live indicator */\n.wv-live {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background-color: var(--error);\n color: white;\n font-size: 0.75rem;\n font-weight: 600;\n border-radius: 4px;\n}\n\n.wv-live-dot {\n width: 8px;\n height: 8px;\n background-color: white;\n border-radius: 50%;\n animation: wv-pulse 1.5s infinite;\n}\n\n@keyframes wv-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n\n/* Performance stats */\n.wv-perf-bar {\n height: 8px;\n background-color: var(--bg);\n border-radius: 4px;\n overflow: hidden;\n margin-top: 4px;\n}\n\n.wv-perf-bar-fill {\n height: 100%;\n border-radius: 4px;\n transition: width 0.3s ease;\n}\n\n.wv-perf-bar-fill--cold { background-color: var(--heat-cold); }\n.wv-perf-bar-fill--cool { background-color: var(--heat-cool); }\n.wv-perf-bar-fill--neutral { background-color: var(--heat-neutral); }\n.wv-perf-bar-fill--warm { background-color: var(--heat-warm); }\n.wv-perf-bar-fill--hot { background-color: var(--heat-hot); }\n.wv-perf-bar-fill--critical { background-color: var(--heat-critical); }\n\n/* Tooltip */\n.wv-tooltip {\n position: fixed;\n padding: 8px 12px;\n background-color: var(--text);\n color: var(--bg);\n font-size: 0.75rem;\n border-radius: 4px;\n pointer-events: none;\n z-index: 1000;\n max-width: 300px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n\n/* Loading state */\n.wv-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--text-muted);\n}\n\n.wv-spinner {\n width: 24px;\n height: 24px;\n border: 3px solid var(--border);\n border-top-color: var(--primary);\n border-radius: 50%;\n animation: wv-spin 1s linear infinite;\n margin-right: 8px;\n}\n\n@keyframes wv-spin {\n to { transform: rotate(360deg); }\n}\n\n/* Empty state */\n.wv-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--text-muted);\n text-align: center;\n padding: 40px;\n}\n\n.wv-empty svg {\n width: 64px;\n height: 64px;\n margin-bottom: 16px;\n opacity: 0.5;\n}\n\n/* Modal */\n.wv-modal {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n\n.wv-modal-content {\n background-color: var(--bg);\n border-radius: 8px;\n width: 90%;\n max-width: 600px;\n max-height: 90vh;\n display: flex;\n flex-direction: column;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);\n}\n\n.wv-modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--border);\n}\n\n.wv-modal-header h2 {\n font-size: 1.125rem;\n font-weight: 600;\n margin: 0;\n}\n\n.wv-modal-body {\n padding: 20px;\n overflow-y: auto;\n flex: 1;\n}\n\n.wv-modal-body p {\n margin: 0 0 12px 0;\n font-size: 0.875rem;\n color: var(--text-muted);\n}\n\n.wv-modal-body code {\n background-color: var(--bg-secondary);\n padding: 2px 6px;\n border-radius: 4px;\n font-size: 0.875rem;\n font-family: monospace;\n}\n\n.wv-textarea {\n width: 100%;\n padding: 12px;\n font-family: monospace;\n font-size: 0.875rem;\n border: 1px solid var(--border);\n border-radius: 6px;\n background-color: var(--bg);\n color: var(--text);\n resize: vertical;\n box-sizing: border-box;\n}\n\n.wv-textarea:focus {\n outline: none;\n border-color: var(--primary);\n}\n\n.wv-error {\n margin-top: 12px;\n padding: 12px;\n background-color: var(--node-error);\n color: #842029;\n border-radius: 6px;\n font-size: 0.875rem;\n}\n\n.wv-modal-footer {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n padding: 16px 20px;\n border-top: 1px solid var(--border);\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n .wv-inspector {\n position: fixed;\n right: 0;\n top: 0;\n bottom: 0;\n z-index: 100;\n box-shadow: -4px 0 12px rgba(0, 0, 0, 0.1);\n }\n\n .wv-timeline {\n height: auto;\n }\n\n .wv-modal-content {\n width: 95%;\n max-height: 95vh;\n }\n}\n`;\n}\n","/**\n * HTML Visualizer Client-Side JavaScript\n *\n * Handles interactivity in the browser:\n * - Zoom and pan\n * - Node selection and inspection\n * - Time-travel controls\n * - WebSocket live updates\n * - Heatmap toggle\n */\n\n/**\n * Generate client-side JavaScript for the HTML visualizer.\n */\nexport function generateClientScript(options: {\n wsUrl?: string;\n interactive: boolean;\n timeTravel: boolean;\n heatmap: boolean;\n}): string {\n return `\n(function() {\n 'use strict';\n\n // State\n let selectedNodeId = null;\n let transform = { x: 0, y: 0, scale: 1 };\n let isDragging = false;\n let dragStart = { x: 0, y: 0 };\n let ws = null;\n let snapshots = [];\n let currentSnapshotIndex = -1;\n let isPlaying = false;\n let playbackSpeed = 1;\n let heatmapEnabled = false;\n let heatmapMetric = 'duration';\n\n // DOM elements\n const diagram = document.getElementById('diagram');\n const svg = diagram?.querySelector('svg');\n const inspector = document.getElementById('inspector');\n const timeline = document.getElementById('timeline');\n\n // Initialize\n document.addEventListener('DOMContentLoaded', init);\n\n function init() {\n ${options.interactive ? \"setupInteractivity(); setupLoadJSON();\" : \"\"}\n // WebSocket must be initialized before time-travel and heatmap checks\n ${options.wsUrl ? `setupWebSocket('${options.wsUrl}');` : \"\"}\n ${options.timeTravel ? \"setupTimeTravel(); initializeTimeTravelFromData();\" : \"\"}\n ${options.heatmap ? \"setupHeatmap();\" : \"\"}\n setupKeyboardShortcuts();\n }\n\n // Load JSON functionality\n function setupLoadJSON() {\n const loadBtn = document.getElementById('load-json-btn');\n const modal = document.getElementById('load-json-modal');\n const closeBtn = document.getElementById('load-json-close');\n const cancelBtn = document.getElementById('load-json-cancel');\n const submitBtn = document.getElementById('load-json-submit');\n const input = document.getElementById('load-json-input');\n const errorDiv = document.getElementById('load-json-error');\n\n if (!loadBtn || !modal) return;\n\n loadBtn.addEventListener('click', () => {\n modal.style.display = 'flex';\n if (input) {\n input.value = '';\n input.focus();\n }\n if (errorDiv) errorDiv.style.display = 'none';\n });\n\n const closeModal = () => {\n modal.style.display = 'none';\n if (errorDiv) errorDiv.style.display = 'none';\n };\n\n closeBtn?.addEventListener('click', closeModal);\n cancelBtn?.addEventListener('click', closeModal);\n modal.addEventListener('click', (e) => {\n if (e.target === modal) closeModal();\n });\n\n submitBtn?.addEventListener('click', () => {\n if (!input) return;\n\n try {\n const jsonText = input.value.trim();\n if (!jsonText) {\n showError('Please paste JSON data');\n return;\n }\n\n const ir = JSON.parse(jsonText);\n\n // Validate IR structure\n if (!ir || !ir.root || !ir.root.type || ir.root.type !== 'workflow') {\n showError('Invalid workflow IR. Expected object with root.type === \"workflow\"');\n return;\n }\n\n // Update global data\n window.__WORKFLOW_IR__ = ir;\n const newData = buildWorkflowDataFromIR(ir);\n window.__WORKFLOW_DATA__ = newData;\n\n // Save to sessionStorage for full rebuild on reload\n try {\n sessionStorage.setItem('workflow_ir', JSON.stringify(ir));\n } catch (storageErr) {\n console.warn('Failed to save IR to sessionStorage:', storageErr);\n }\n\n // Rebuild the diagram (updates node states in-place)\n rebuildDiagram(ir);\n\n // Update inspector if node is selected\n if (selectedNodeId) {\n updateInspector(selectedNodeId);\n }\n\n closeModal();\n\n // Offer full rebuild via reload if structure changed significantly\n console.log('IR loaded. Node states updated. Reload page for full layout rebuild with new structure.');\n } catch (e) {\n showError('Invalid JSON: ' + e.message);\n }\n });\n\n function showError(message) {\n if (errorDiv) {\n errorDiv.textContent = message;\n errorDiv.style.display = 'block';\n }\n }\n }\n\n function buildWorkflowDataFromIR(ir) {\n const nodes = {};\n\n function collectNodes(flowNodes) {\n for (const node of flowNodes || []) {\n nodes[node.id] = {\n id: node.id,\n name: node.name,\n type: node.type,\n state: node.state,\n key: node.key,\n durationMs: node.durationMs,\n startTs: node.startTs,\n error: node.error !== undefined && node.error !== null ? String(node.error) : undefined,\n retryCount: node.retryCount,\n input: node.input,\n output: node.output,\n ...(node.type === 'stream' && {\n namespace: node.namespace,\n writeCount: node.writeCount,\n readCount: node.readCount,\n finalPosition: node.finalPosition,\n streamState: node.streamState,\n backpressureOccurred: node.backpressureOccurred,\n }),\n ...(node.type === 'decision' && {\n condition: node.condition,\n decisionValue: node.decisionValue,\n branchTaken: node.branchTaken,\n branches: node.branches,\n }),\n };\n\n if (node.children) {\n collectNodes(node.children);\n }\n if (node.branches) {\n for (const branch of node.branches) {\n collectNodes(branch.children);\n }\n }\n }\n }\n\n collectNodes(ir.root.children);\n const hooks = ir.hooks\n ? {\n ...ir.hooks,\n onAfterStep:\n ir.hooks.onAfterStep instanceof Map\n ? Object.fromEntries(ir.hooks.onAfterStep)\n : ir.hooks.onAfterStep,\n }\n : undefined;\n return { nodes, hooks };\n }\n\n function rebuildDiagram(ir) {\n // Update workflow data and node states\n const newData = buildWorkflowDataFromIR(ir);\n window.__WORKFLOW_DATA__ = newData;\n window.__WORKFLOW_IR__ = ir;\n\n // Update existing node states in SVG\n renderIR(ir);\n\n // Note: Full diagram rebuild (new nodes, layout changes) requires regenerating the HTML\n // For now, we update node states. Users can save the IR and regenerate HTML for full rebuild.\n console.log('Workflow IR loaded. Node states updated. For full diagram rebuild, regenerate HTML with the new IR.');\n }\n\n function initializeTimeTravelFromData() {\n // For static HTML (no WebSocket), time travel doesn't work since we only have one state\n // Initialize with empty snapshots and disable controls\n if (!ws) {\n snapshots = [];\n currentSnapshotIndex = -1;\n updateTimelineUI();\n\n // Disable time travel controls for static HTML\n const playBtn = document.getElementById('tt-play');\n const pauseBtn = document.getElementById('tt-pause');\n const prevBtn = document.getElementById('tt-prev');\n const nextBtn = document.getElementById('tt-next');\n const slider = document.getElementById('tt-slider');\n\n [playBtn, pauseBtn, prevBtn, nextBtn, slider].forEach(btn => {\n if (btn) {\n btn.disabled = true;\n btn.title = 'Time travel requires a live WebSocket connection';\n }\n });\n }\n }\n\n // Interactivity\n function setupInteractivity() {\n if (!diagram || !svg) return;\n\n // Zoom with mouse wheel\n diagram.addEventListener('wheel', (e) => {\n e.preventDefault();\n const delta = e.deltaY > 0 ? 0.9 : 1.1;\n const rect = diagram.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n\n // Zoom towards cursor position\n transform.scale *= delta;\n transform.scale = Math.max(0.1, Math.min(5, transform.scale));\n transform.x = x - (x - transform.x) * delta;\n transform.y = y - (y - transform.y) * delta;\n\n applyTransform();\n });\n\n // Pan with drag\n diagram.addEventListener('mousedown', (e) => {\n if (e.target === diagram || e.target === svg) {\n isDragging = true;\n dragStart = { x: e.clientX - transform.x, y: e.clientY - transform.y };\n diagram.style.cursor = 'grabbing';\n }\n });\n\n document.addEventListener('mousemove', (e) => {\n if (!isDragging) return;\n transform.x = e.clientX - dragStart.x;\n transform.y = e.clientY - dragStart.y;\n applyTransform();\n });\n\n document.addEventListener('mouseup', () => {\n isDragging = false;\n if (diagram) diagram.style.cursor = 'grab';\n });\n\n // Node selection - handle clicks on nodes and their children (rect, text, etc.)\n document.querySelectorAll('.wv-node').forEach((node) => {\n node.addEventListener('click', (e) => {\n e.stopPropagation();\n const nodeId = node.dataset.nodeId;\n if (nodeId) {\n selectNode(nodeId);\n }\n });\n });\n\n // Also handle clicks directly on child elements (rect, text) that bubble up\n if (svg) {\n svg.addEventListener('click', (e) => {\n // Find the closest .wv-node parent\n let target = e.target;\n while (target && target !== svg) {\n if (target.classList && target.classList.contains('wv-node')) {\n const nodeId = target.dataset?.nodeId;\n if (nodeId) {\n e.stopPropagation();\n selectNode(nodeId);\n return;\n }\n }\n target = target.parentElement;\n }\n });\n }\n\n // Deselect on background click\n diagram.addEventListener('click', (e) => {\n if (e.target === diagram || e.target === svg) {\n selectNode(null);\n }\n });\n\n // Zoom buttons\n document.getElementById('zoom-in')?.addEventListener('click', () => zoom(1.2));\n document.getElementById('zoom-out')?.addEventListener('click', () => zoom(0.8));\n document.getElementById('zoom-reset')?.addEventListener('click', resetZoom);\n\n // Set initial cursor\n diagram.style.cursor = 'grab';\n }\n\n function applyTransform() {\n if (!svg) return;\n const g = svg.querySelector('g.wv-root');\n if (g) {\n g.setAttribute('transform', 'translate(' + transform.x + ',' + transform.y + ') scale(' + transform.scale + ')');\n }\n }\n\n function zoom(factor) {\n if (!diagram) return;\n const rect = diagram.getBoundingClientRect();\n const centerX = rect.width / 2;\n const centerY = rect.height / 2;\n\n transform.scale *= factor;\n transform.scale = Math.max(0.1, Math.min(5, transform.scale));\n transform.x = centerX - (centerX - transform.x) * factor;\n transform.y = centerY - (centerY - transform.y) * factor;\n\n applyTransform();\n }\n\n function resetZoom() {\n transform = { x: 0, y: 0, scale: 1 };\n applyTransform();\n }\n\n function selectNode(nodeId) {\n // Remove previous selection\n document.querySelectorAll('.wv-node--selected').forEach((n) => {\n n.classList.remove('wv-node--selected');\n });\n\n selectedNodeId = nodeId;\n\n if (nodeId) {\n const node = document.querySelector('[data-node-id=\"' + nodeId + '\"]');\n if (node) {\n node.classList.add('wv-node--selected');\n updateInspector(nodeId);\n }\n } else {\n clearInspector();\n }\n }\n\n function updateInspector(nodeId) {\n const content = document.getElementById('inspector-content');\n if (!content) return;\n\n const nodeData = window.__WORKFLOW_DATA__?.nodes?.[nodeId];\n if (!nodeData) {\n // Clear and add empty message using safe DOM methods\n while (content.firstChild) content.removeChild(content.firstChild);\n const p = document.createElement('p');\n p.className = 'wv-empty';\n p.textContent = 'No data available';\n content.appendChild(p);\n return;\n }\n\n renderInspectorContent(content, nodeData);\n }\n\n function renderInspectorContent(container, node) {\n // Clear container using safe DOM method\n while (container.firstChild) container.removeChild(container.firstChild);\n\n // Node Info section\n const infoSection = createSection('Node Info');\n infoSection.appendChild(createRow('Name', node.name || node.id));\n infoSection.appendChild(createRow('Type', node.type));\n\n // State badge\n const stateRow = document.createElement('div');\n stateRow.className = 'wv-inspector-row';\n const stateLabel = document.createElement('span');\n stateLabel.className = 'wv-inspector-label';\n stateLabel.textContent = 'State';\n const stateBadge = document.createElement('span');\n stateBadge.className = 'wv-badge wv-badge--' + node.state;\n stateBadge.textContent = node.state;\n stateRow.appendChild(stateLabel);\n stateRow.appendChild(stateBadge);\n infoSection.appendChild(stateRow);\n\n if (node.key) {\n infoSection.appendChild(createRow('Key', node.key));\n }\n container.appendChild(infoSection);\n\n // Timing section\n if (node.durationMs !== undefined) {\n const timingSection = createSection('Timing');\n timingSection.appendChild(createRow('Duration', formatDuration(node.durationMs)));\n if (node.startTs !== undefined) {\n timingSection.appendChild(createRow('Started', new Date(node.startTs).toLocaleTimeString()));\n }\n container.appendChild(timingSection);\n }\n\n // Retries section\n if (node.retryCount !== undefined && node.retryCount > 0) {\n const retrySection = createSection('Retries');\n retrySection.appendChild(createRow('Retry Count', String(node.retryCount)));\n container.appendChild(retrySection);\n }\n\n // Input/Output section\n if (node.input !== undefined || node.output !== undefined) {\n const ioSection = createSection('Input / Output');\n if (node.input !== undefined) {\n const inputPre = document.createElement('pre');\n inputPre.style.cssText = 'font-size:11px;overflow:auto;max-height:120px;background:var(--bg);padding:8px;border-radius:4px;';\n inputPre.textContent = typeof node.input === 'string' ? node.input : safeStringify(node.input);\n ioSection.appendChild(createRow('Input', ''));\n ioSection.appendChild(inputPre);\n }\n if (node.output !== undefined) {\n const outputPre = document.createElement('pre');\n outputPre.style.cssText = 'font-size:11px;overflow:auto;max-height:120px;background:var(--bg);padding:8px;border-radius:4px;';\n outputPre.textContent = typeof node.output === 'string' ? node.output : safeStringify(node.output);\n ioSection.appendChild(createRow('Output', ''));\n ioSection.appendChild(outputPre);\n }\n container.appendChild(ioSection);\n }\n\n // Hook timing (onAfterStep)\n const hookKey = node.key || node.id;\n const hookExec = window.__WORKFLOW_DATA__?.hooks?.onAfterStep?.[hookKey];\n if (hookExec && (hookExec.durationMs !== undefined || hookExec.ts !== undefined)) {\n const hookSection = createSection('Hook (onAfterStep)');\n if (hookExec.durationMs !== undefined) {\n hookSection.appendChild(createRow('Duration', formatDuration(hookExec.durationMs)));\n }\n if (hookExec.ts !== undefined) {\n hookSection.appendChild(createRow('At', new Date(hookExec.ts).toLocaleTimeString()));\n }\n container.appendChild(hookSection);\n }\n\n // Error section (preserve falsy errors: 0, \"\", false)\n if (node.error !== undefined && node.error !== null) {\n const errorSection = createSection('Error');\n const pre = document.createElement('pre');\n pre.style.cssText = 'font-size:11px;overflow:auto;max-height:150px;background:var(--bg);padding:8px;border-radius:4px;';\n pre.textContent = String(node.error);\n errorSection.appendChild(pre);\n container.appendChild(errorSection);\n }\n }\n\n function createSection(title) {\n const section = document.createElement('div');\n section.className = 'wv-inspector-section';\n const h3 = document.createElement('h3');\n h3.textContent = title;\n section.appendChild(h3);\n return section;\n }\n\n function createRow(label, value) {\n const row = document.createElement('div');\n row.className = 'wv-inspector-row';\n const labelSpan = document.createElement('span');\n labelSpan.className = 'wv-inspector-label';\n labelSpan.textContent = label;\n const valueSpan = document.createElement('span');\n valueSpan.className = 'wv-inspector-value';\n valueSpan.textContent = value;\n row.appendChild(labelSpan);\n row.appendChild(valueSpan);\n return row;\n }\n\n function clearInspector() {\n const content = document.getElementById('inspector-content');\n if (content) {\n while (content.firstChild) content.removeChild(content.firstChild);\n const p = document.createElement('p');\n p.className = 'wv-empty';\n p.textContent = 'Select a node to inspect';\n content.appendChild(p);\n }\n }\n\n // Time Travel\n function setupTimeTravel() {\n const playBtn = document.getElementById('tt-play');\n const pauseBtn = document.getElementById('tt-pause');\n const prevBtn = document.getElementById('tt-prev');\n const nextBtn = document.getElementById('tt-next');\n const speedSelect = document.getElementById('tt-speed');\n const slider = document.getElementById('tt-slider');\n\n playBtn?.addEventListener('click', play);\n pauseBtn?.addEventListener('click', pause);\n prevBtn?.addEventListener('click', stepBackward);\n nextBtn?.addEventListener('click', stepForward);\n speedSelect?.addEventListener('change', (e) => {\n playbackSpeed = parseFloat(e.target.value);\n });\n slider?.addEventListener('input', (e) => {\n seek(parseInt(e.target.value, 10));\n });\n }\n\n function play() {\n if (snapshots.length === 0) {\n console.warn('[Visualizer] No snapshots available for time travel. Time travel requires a live WebSocket connection.');\n return;\n }\n isPlaying = true;\n updatePlaybackUI();\n playNext();\n }\n\n function pause() {\n isPlaying = false;\n updatePlaybackUI();\n }\n\n function playNext() {\n if (!isPlaying) return;\n if (currentSnapshotIndex < snapshots.length - 1) {\n const current = snapshots[currentSnapshotIndex];\n const next = snapshots[currentSnapshotIndex + 1];\n const delay = (next.timestamp - current.timestamp) / playbackSpeed;\n\n setTimeout(() => {\n stepForward();\n playNext();\n }, Math.max(16, delay));\n } else {\n pause();\n }\n }\n\n function stepForward() {\n if (snapshots.length === 0) {\n console.warn('[Visualizer] No snapshots available for time travel.');\n return;\n }\n if (currentSnapshotIndex < snapshots.length - 1) {\n seek(currentSnapshotIndex + 1);\n }\n }\n\n function stepBackward() {\n if (snapshots.length === 0) {\n console.warn('[Visualizer] No snapshots available for time travel.');\n return;\n }\n if (currentSnapshotIndex > 0) {\n seek(currentSnapshotIndex - 1);\n }\n }\n\n function seek(index) {\n if (index < 0 || index >= snapshots.length) return;\n currentSnapshotIndex = index;\n updateTimelineUI();\n\n // Request IR update from server or use cached\n if (ws && ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify({ type: 'time_travel_seek', payload: { index } }));\n } else if (snapshots[index]?.ir) {\n renderIR(snapshots[index].ir);\n }\n }\n\n function updatePlaybackUI() {\n const playBtn = document.getElementById('tt-play');\n const pauseBtn = document.getElementById('tt-pause');\n\n if (playBtn) playBtn.style.display = isPlaying ? 'none' : 'inline-flex';\n if (pauseBtn) pauseBtn.style.display = isPlaying ? 'inline-flex' : 'none';\n }\n\n function updateTimelineUI() {\n const slider = document.getElementById('tt-slider');\n const timeDisplay = document.getElementById('tt-time');\n\n if (slider) {\n slider.max = String(Math.max(0, snapshots.length - 1));\n slider.value = String(Math.max(0, currentSnapshotIndex));\n }\n\n if (timeDisplay) {\n timeDisplay.textContent = (currentSnapshotIndex + 1) + ' / ' + snapshots.length;\n }\n }\n\n // WebSocket\n function setupWebSocket(url) {\n ws = new WebSocket(url);\n\n ws.onopen = () => {\n console.log('[Visualizer] Connected to server');\n showLiveIndicator(true);\n ${options.timeTravel ? \"if (ws && ws.readyState === 1) ws.send(JSON.stringify({ type: 'request_snapshots' }));\" : \"\"}\n };\n\n ws.onclose = () => {\n console.log('[Visualizer] Disconnected from server');\n showLiveIndicator(false);\n // Attempt reconnect after 2 seconds\n setTimeout(() => setupWebSocket(url), 2000);\n };\n\n ws.onmessage = (event) => {\n try {\n const msg = JSON.parse(event.data);\n handleServerMessage(msg);\n } catch (e) {\n console.error('[Visualizer] Failed to parse message:', e);\n }\n };\n\n ws.onerror = (error) => {\n console.error('[Visualizer] WebSocket error:', error);\n };\n }\n\n function handleServerMessage(msg) {\n switch (msg.type) {\n case 'ir_update':\n renderIR(msg.payload);\n break;\n case 'snapshot':\n snapshots.push(msg.payload);\n currentSnapshotIndex = snapshots.length - 1;\n updateTimelineUI();\n break;\n case 'snapshots_list':\n snapshots = msg.payload;\n currentSnapshotIndex = snapshots.length - 1;\n updateTimelineUI();\n break;\n case 'performance_data':\n window.__PERFORMANCE_DATA__ = msg.payload;\n if (heatmapEnabled) applyHeatmap();\n break;\n case 'workflow_complete':\n showLiveIndicator(false);\n break;\n case 'time_travel_state':\n currentSnapshotIndex = msg.payload.currentIndex;\n isPlaying = msg.payload.isPlaying;\n playbackSpeed = msg.payload.playbackSpeed;\n updateTimelineUI();\n updatePlaybackUI();\n break;\n }\n }\n\n function showLiveIndicator(show) {\n const indicator = document.getElementById('live-indicator');\n if (indicator) {\n indicator.style.display = show ? 'flex' : 'none';\n }\n }\n\n // Heatmap\n function setupHeatmap() {\n const toggle = document.getElementById('heatmap-toggle');\n const metricSelect = document.getElementById('heatmap-metric');\n\n // For static HTML (no WebSocket), disable heatmap controls\n if (!ws) {\n if (toggle) {\n toggle.disabled = true;\n toggle.title = 'Heatmap requires a live WebSocket connection with performance data';\n }\n if (metricSelect) {\n metricSelect.disabled = true;\n metricSelect.title = 'Heatmap requires a live WebSocket connection with performance data';\n }\n return;\n }\n\n toggle?.addEventListener('click', () => {\n heatmapEnabled = !heatmapEnabled;\n toggle.classList.toggle('wv-btn--primary', heatmapEnabled);\n if (ws && ws.readyState === 1) {\n ws.send(JSON.stringify({ type: 'toggle_heatmap', payload: { enabled: heatmapEnabled } }));\n }\n if (heatmapEnabled) {\n applyHeatmap();\n } else {\n removeHeatmap();\n }\n });\n\n metricSelect?.addEventListener('change', (e) => {\n heatmapMetric = e.target.value;\n if (ws && ws.readyState === 1) {\n ws.send(JSON.stringify({ type: 'set_heatmap_metric', payload: { metric: heatmapMetric } }));\n }\n if (heatmapEnabled) applyHeatmap();\n });\n }\n\n function applyHeatmap() {\n const perfData = window.__PERFORMANCE_DATA__;\n if (!perfData) return;\n\n const heat = perfData.heat;\n document.querySelectorAll('.wv-node').forEach((node) => {\n const nodeId = node.dataset.nodeId;\n const value = heat && (typeof heat.get === 'function' ? heat.get(nodeId) : heat[nodeId]);\n\n // Remove existing heat classes\n node.classList.remove(\n 'wv-node--heat-cold',\n 'wv-node--heat-cool',\n 'wv-node--heat-neutral',\n 'wv-node--heat-warm',\n 'wv-node--heat-hot',\n 'wv-node--heat-critical'\n );\n\n if (value !== undefined) {\n const level = getHeatLevel(value);\n node.classList.add('wv-node--heat-' + level);\n }\n });\n }\n\n function removeHeatmap() {\n document.querySelectorAll('.wv-node').forEach((node) => {\n node.classList.remove(\n 'wv-node--heat-cold',\n 'wv-node--heat-cool',\n 'wv-node--heat-neutral',\n 'wv-node--heat-warm',\n 'wv-node--heat-hot',\n 'wv-node--heat-critical'\n );\n });\n }\n\n function getHeatLevel(heat) {\n if (heat < 0.2) return 'cold';\n if (heat < 0.4) return 'cool';\n if (heat < 0.6) return 'neutral';\n if (heat < 0.8) return 'warm';\n if (heat < 0.95) return 'hot';\n return 'critical';\n }\n\n // Keyboard shortcuts\n function setupKeyboardShortcuts() {\n document.addEventListener('keydown', (e) => {\n // Don't trigger shortcuts when typing in inputs\n if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;\n\n switch (e.key) {\n case ' ':\n e.preventDefault();\n isPlaying ? pause() : play();\n break;\n case 'ArrowLeft':\n e.preventDefault();\n stepBackward();\n break;\n case 'ArrowRight':\n e.preventDefault();\n stepForward();\n break;\n case '0':\n resetZoom();\n break;\n case '+':\n case '=':\n zoom(1.2);\n break;\n case '-':\n zoom(0.8);\n break;\n case 'h':\n ${options.heatmap ? \"document.getElementById('heatmap-toggle')?.click();\" : \"\"}\n break;\n case 'Escape':\n selectNode(null);\n break;\n }\n });\n }\n\n // Render functions\n function renderIR(ir) {\n window.__WORKFLOW_DATA__ = buildWorkflowDataFromIR(ir);\n\n // Update SVG node states\n document.querySelectorAll('.wv-node').forEach((node) => {\n const nodeId = node.dataset.nodeId;\n const nodeData = window.__WORKFLOW_DATA__.nodes[nodeId];\n if (nodeData) {\n // Update only state class so heatmap (wv-node--heat-*) and other classes are preserved\n node.className = node.className.replace(/wv-node--(success|error|running|cached|skipped|pending|aborted)/g, '').replace(/\\\\s+/g, ' ').trim();\n node.classList.add('wv-node', 'wv-node--' + nodeData.state);\n\n // Update timing text if present\n const timing = node.querySelector('.wv-node-timing');\n if (timing && nodeData.durationMs !== undefined) {\n timing.textContent = formatDuration(nodeData.durationMs);\n }\n }\n });\n\n // Re-apply selection so wv-node--selected is not lost after class updates\n if (selectedNodeId) {\n selectNode(selectedNodeId);\n }\n }\n\n function flattenNodes(nodes) {\n const result = [];\n for (const node of nodes) {\n result.push(node);\n if (node.children) {\n result.push(...flattenNodes(node.children));\n }\n if (node.branches) {\n for (const branch of node.branches) {\n result.push(...flattenNodes(branch.children));\n }\n }\n }\n return result;\n }\n\n // Utilities\n function safeStringify(val) {\n const seen = new WeakSet();\n try {\n return JSON.stringify(val, function(key, value) {\n if (typeof value === 'bigint') return value.toString();\n if (typeof value === 'object' && value !== null) {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n }\n return value;\n }, 2);\n } catch (e) {\n return String(val);\n }\n }\n\n function formatDuration(ms) {\n if (ms < 1000) return Math.round(ms) + 'ms';\n if (ms < 60000) {\n const s = (ms / 1000).toFixed(1).replace(/\\\\.0$/, '');\n return s + 's';\n }\n let min = Math.floor(ms / 60000);\n let sec = Math.round((ms % 60000) / 1000);\n if (sec >= 60) { min += 1; sec = 0; }\n if (sec === 0) return min + 'm';\n return min + 'm ' + sec + 's';\n }\n})();\n`;\n}\n","/**\n * HTML Renderer\n *\n * Renders the workflow IR as an interactive HTML page with:\n * - SVG-based workflow diagram\n * - Zoom and pan\n * - Node inspection\n * - Time-travel controls\n * - Performance heatmap overlay\n */\n\nimport type {\n FlowNode,\n HTMLRenderOptions,\n Renderer,\n WorkflowIR,\n StepState,\n RenderOptions,\n LayoutDirection,\n} from \"../types\";\nimport {\n isStepNode,\n isParallelNode,\n isRaceNode,\n isDecisionNode,\n isStreamNode,\n} from \"../types\";\nimport { generateStyles } from \"./html-styles\";\nimport { generateClientScript } from \"./html-client\";\nimport { formatDuration } from \"../utils/timing\";\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst NODE_WIDTH = 160;\nconst NODE_HEIGHT = 50;\nconst NODE_SPACING_H = 40;\nconst NODE_SPACING_V = 30;\nconst CONTAINER_PADDING = 20;\n\n// =============================================================================\n// Layout Types\n// =============================================================================\n\ninterface LayoutNode {\n id: string;\n name: string;\n type: string;\n state: StepState;\n x: number;\n y: number;\n width: number;\n height: number;\n durationMs?: number;\n children?: LayoutNode[];\n containerType?: \"parallel\" | \"race\" | \"decision\";\n containerLabel?: string;\n}\n\ninterface LayoutResult {\n nodes: LayoutNode[];\n width: number;\n height: number;\n}\n\n// =============================================================================\n// Layout Functions\n// =============================================================================\n\n/**\n * Layout workflow nodes for SVG rendering.\n * Supports all four directions: TB, BT, LR, RL.\n */\nfunction layoutWorkflow(\n nodes: FlowNode[],\n direction: LayoutDirection = \"TB\",\n options?: Pick<HTMLRenderOptions, \"showKeys\">\n): LayoutResult {\n const isVertical = direction === \"TB\" || direction === \"BT\";\n const isReversed = direction === \"RL\" || direction === \"BT\";\n const layoutNodes: LayoutNode[] = [];\n let currentX = CONTAINER_PADDING;\n let currentY = CONTAINER_PADDING;\n let maxWidth = 0;\n let maxHeight = 0;\n\n for (const node of nodes) {\n const result = layoutFlowNode(node, currentX, currentY, isVertical, options);\n layoutNodes.push(result.node);\n\n if (isVertical) {\n currentY += result.height + NODE_SPACING_V;\n maxWidth = Math.max(maxWidth, result.width);\n maxHeight = currentY;\n } else {\n currentX += result.width + NODE_SPACING_H;\n maxHeight = Math.max(maxHeight, result.height);\n maxWidth = currentX;\n }\n }\n\n const totalWidth = maxWidth + CONTAINER_PADDING;\n const totalHeight = maxHeight + CONTAINER_PADDING;\n\n // For reversed directions, mirror node positions\n if (isReversed) {\n for (const node of layoutNodes) {\n mirrorNode(node, totalWidth, totalHeight, isVertical);\n }\n }\n\n return {\n nodes: layoutNodes,\n width: totalWidth,\n height: totalHeight,\n };\n}\n\n/**\n * Mirror a node's position for reversed layouts (RL, BT).\n * Recursively mirrors children as well.\n */\nfunction mirrorNode(\n node: LayoutNode,\n totalWidth: number,\n totalHeight: number,\n isVertical: boolean\n): void {\n if (isVertical) {\n // BT: mirror vertically\n node.y = totalHeight - node.y - node.height;\n } else {\n // RL: mirror horizontally\n node.x = totalWidth - node.x - node.width;\n }\n\n // Recursively mirror children\n if (node.children) {\n for (const child of node.children) {\n mirrorNode(child, totalWidth, totalHeight, isVertical);\n }\n }\n}\n\n/**\n * Layout a single flow node.\n */\nfunction layoutFlowNode(\n node: FlowNode,\n x: number,\n y: number,\n _isVertical: boolean,\n options?: Pick<HTMLRenderOptions, \"showKeys\">\n): { node: LayoutNode; width: number; height: number } {\n if (isStepNode(node)) {\n const baseName = node.name ?? node.key ?? \"step\";\n const name =\n options?.showKeys && node.key && node.name\n ? `${baseName} [key: ${node.key}]`\n : baseName;\n return {\n node: {\n id: node.id,\n name,\n type: \"step\",\n state: node.state,\n x,\n y,\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n durationMs: node.durationMs,\n },\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n };\n }\n\n if (isStreamNode(node)) {\n const streamLabel =\n node.streamState === \"active\"\n ? `stream:${node.namespace} ⟳`\n : node.streamState === \"error\"\n ? `stream:${node.namespace} ✗`\n : `stream:${node.namespace} ✓`;\n return {\n node: {\n id: node.id,\n name: streamLabel,\n type: \"stream\",\n state: node.state,\n x,\n y,\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n durationMs: node.durationMs,\n },\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n };\n }\n\n if (isParallelNode(node) || isRaceNode(node)) {\n const containerType = isParallelNode(node) ? \"parallel\" : \"race\";\n const label = node.name ?? containerType;\n const children: LayoutNode[] = [];\n\n let innerX = x + CONTAINER_PADDING;\n const innerY = y + CONTAINER_PADDING + 20; // Extra space for label\n let innerMaxWidth = 0;\n let innerMaxHeight = 0;\n\n // Layout children horizontally in parallel\n for (const child of node.children) {\n const result = layoutFlowNode(child, innerX, innerY, true, options);\n children.push(result.node);\n innerX += result.width + NODE_SPACING_H;\n innerMaxHeight = Math.max(innerMaxHeight, result.height);\n }\n\n innerMaxWidth = innerX - x - CONTAINER_PADDING;\n const containerWidth = Math.max(\n innerMaxWidth + CONTAINER_PADDING,\n NODE_WIDTH + CONTAINER_PADDING * 2\n );\n const containerHeight =\n innerMaxHeight + CONTAINER_PADDING * 2 + 20; // Extra for label\n\n return {\n node: {\n id: node.id,\n name: label,\n type: containerType,\n state: node.state,\n x,\n y,\n width: containerWidth,\n height: containerHeight,\n durationMs: node.durationMs,\n children,\n containerType,\n containerLabel: containerType === \"parallel\" ? \"PARALLEL\" : \"RACE\",\n },\n width: containerWidth,\n height: containerHeight,\n };\n }\n\n if (isDecisionNode(node)) {\n const label = node.name ?? \"decision\";\n const children: LayoutNode[] = [];\n\n let innerX = x + CONTAINER_PADDING;\n const innerY = y + CONTAINER_PADDING + 20;\n let innerMaxHeight = 0;\n\n // Layout branches horizontally\n for (const branch of node.branches) {\n for (const child of branch.children) {\n const result = layoutFlowNode(child, innerX, innerY, true, options);\n children.push(result.node);\n innerX += result.width + NODE_SPACING_H;\n innerMaxHeight = Math.max(innerMaxHeight, result.height);\n }\n }\n\n const containerWidth = Math.max(\n innerX - x,\n NODE_WIDTH + CONTAINER_PADDING * 2\n );\n const containerHeight = innerMaxHeight + CONTAINER_PADDING * 2 + 20;\n\n return {\n node: {\n id: node.id,\n name: label,\n type: \"decision\",\n state: node.state,\n x,\n y,\n width: containerWidth,\n height: containerHeight,\n durationMs: node.durationMs,\n children,\n containerType: \"decision\",\n containerLabel: \"DECISION\",\n },\n width: containerWidth,\n height: containerHeight,\n };\n }\n\n // Default fallback\n return {\n node: {\n id: node.id,\n name: node.name ?? \"unknown\",\n type: node.type,\n state: node.state,\n x,\n y,\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n },\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n };\n}\n\n// =============================================================================\n// SVG Rendering\n// =============================================================================\n\n/**\n * Render a layout node to SVG.\n */\nfunction renderLayoutNodeSVG(node: LayoutNode, showTimings: boolean): string {\n if (node.containerType) {\n return renderContainerSVG(node, showTimings);\n }\n return renderStepSVG(node, showTimings);\n}\n\n/**\n * Render a step node as SVG.\n */\nfunction renderStepSVG(node: LayoutNode, showTimings: boolean): string {\n const rx = 8; // Border radius\n const timing =\n showTimings && node.durationMs !== undefined\n ? formatDuration(node.durationMs)\n : \"\";\n\n return `\n <g class=\"wv-node wv-node--${node.state}\" data-node-id=\"${escapeAttr(node.id)}\" transform=\"translate(${node.x}, ${node.y})\">\n <rect width=\"${node.width}\" height=\"${node.height}\" rx=\"${rx}\" ry=\"${rx}\" />\n <text x=\"${node.width / 2}\" y=\"${node.height / 2 - (timing ? 4 : 0)}\">${escapeXml(truncate(node.name, node.name.includes(\"[key:\") ? 40 : 20))}</text>\n ${timing ? `<text class=\"wv-node-timing\" x=\"${node.width / 2}\" y=\"${node.height / 2 + 12}\">${timing}</text>` : \"\"}\n </g>\n `;\n}\n\n/**\n * Render a container (parallel/race/decision) as SVG.\n */\nfunction renderContainerSVG(node: LayoutNode, showTimings: boolean): string {\n const rx = 12;\n const childrenSVG =\n node.children?.map((c) => renderLayoutNodeSVG(c, showTimings)).join(\"\\n\") ??\n \"\";\n\n return `\n <g class=\"wv-container wv-container--${node.containerType}\" data-node-id=\"${escapeAttr(node.id)}\" transform=\"translate(${node.x}, ${node.y})\">\n <rect width=\"${node.width}\" height=\"${node.height}\" rx=\"${rx}\" ry=\"${rx}\" />\n <text class=\"wv-container-label\" x=\"${CONTAINER_PADDING}\" y=\"16\">${node.containerLabel}</text>\n <g transform=\"translate(${-node.x}, ${-node.y})\">\n ${childrenSVG}\n </g>\n </g>\n `;\n}\n\n/**\n * Find a flow node by id in the IR tree (for decision branch boundaries).\n */\nfunction findFlowNodeById(nodes: FlowNode[], id: string): FlowNode | undefined {\n for (const node of nodes) {\n if (node.id === id) return node;\n if (\"children\" in node && node.children) {\n const found = findFlowNodeById(node.children, id);\n if (found) return found;\n }\n if (\"branches\" in node && node.branches) {\n for (const branch of node.branches) {\n const found = findFlowNodeById(branch.children, id);\n if (found) return found;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Render edges between sequential nodes.\n * Recursively handles edges inside container nodes.\n * For decisions, only draws sequential edges within each branch (not between branches).\n */\nfunction renderEdgesSVG(nodes: LayoutNode[], ir: WorkflowIR): string {\n const edges: string[] = [];\n\n function drawEdge(from: LayoutNode, to: LayoutNode): void {\n // Determine if nodes are horizontally or vertically arranged\n const isHorizontal = Math.abs(from.y - to.y) < from.height;\n\n if (isHorizontal) {\n // Horizontal edge (left to right)\n const x1 = from.x + from.width;\n const y1 = from.y + from.height / 2;\n const x2 = to.x;\n const y2 = to.y + to.height / 2;\n\n edges.push(`\n <path class=\"wv-edge\" d=\"M ${x1} ${y1} L ${x2 - 8} ${y2}\" />\n <polygon class=\"wv-edge-arrow\" points=\"${x2 - 8},${y2 - 4} ${x2 - 8},${y2 + 4} ${x2},${y2}\" />\n `);\n } else {\n // Vertical edge (top to bottom)\n const x1 = from.x + from.width / 2;\n const y1 = from.y + from.height;\n const x2 = to.x + to.width / 2;\n const y2 = to.y;\n\n edges.push(`\n <path class=\"wv-edge\" d=\"M ${x1} ${y1} L ${x2} ${y2 - 8}\" />\n <polygon class=\"wv-edge-arrow\" points=\"${x2 - 4},${y2 - 8} ${x2 + 4},${y2 - 8} ${x2},${y2}\" />\n `);\n }\n }\n\n function collectEdges(nodeList: LayoutNode[]): void {\n // Draw sequential edges between top-level nodes\n for (let i = 0; i < nodeList.length - 1; i++) {\n drawEdge(nodeList[i], nodeList[i + 1]);\n }\n\n // Recursively process container nodes\n for (const node of nodeList) {\n if (node.children && node.children.length > 0) {\n if (node.containerType === \"decision\") {\n // Decision: draw edge from container to first step of each branch only; then sequential edges within each branch\n const decisionIR = findFlowNodeById(ir.root.children, node.id);\n if (decisionIR && decisionIR.type === \"decision\" && decisionIR.branches) {\n let idx = 0;\n for (const branch of decisionIR.branches) {\n const count = branch.children.length;\n if (count > 0) {\n drawEdge(node, node.children![idx]);\n }\n for (let i = 0; i < count - 1; i++) {\n drawEdge(node.children![idx + i], node.children![idx + i + 1]);\n }\n idx += count;\n }\n }\n } else if (node.containerType === \"parallel\" || node.containerType === \"race\") {\n // Parallel/race: no edges between or from container to branches (siblings are parallel)\n for (const child of node.children) {\n if (child.children && child.children.length > 0) {\n collectEdges([child]);\n }\n }\n } else {\n // For other containers, treat children as sequential\n collectEdges(node.children);\n }\n }\n }\n }\n\n collectEdges(nodes);\n return edges.join(\"\\n\");\n}\n\n// =============================================================================\n// HTML Generation\n// =============================================================================\n\n/**\n * Generate complete HTML page.\n */\nfunction generateHTML(\n ir: WorkflowIR,\n options: HTMLRenderOptions\n): string {\n const layout = layoutWorkflow(ir.root.children, options.layout, options);\n const svgWidth = Math.max(layout.width, 400);\n const svgHeight = Math.max(layout.height, 300);\n\n const nodesSVG = layout.nodes\n .map((n) => renderLayoutNodeSVG(n, options.showTimings))\n .join(\"\\n\");\n const edgesSVG = renderEdgesSVG(layout.nodes, ir);\n\n const workflowName = ir.root.name ?? \"Workflow\";\n const css = generateStyles(options.theme);\n const js = generateClientScript({\n wsUrl: options.wsUrl,\n interactive: options.interactive,\n timeTravel: options.timeTravel,\n heatmap: options.heatmap,\n });\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${escapeXml(workflowName)} - Workflow Visualizer</title>\n <style>${css}</style>\n</head>\n<body>\n <div class=\"workflow-visualizer\">\n <header class=\"wv-header\">\n <h1>${escapeXml(workflowName)}</h1>\n <div class=\"wv-controls\">\n ${options.interactive ? `\n <button id=\"load-json-btn\" class=\"wv-btn\" title=\"Load workflow state from JSON\">Load JSON</button>\n ` : \"\"}\n ${options.wsUrl ? `<div id=\"live-indicator\" class=\"wv-live\" style=\"display:none\"><span class=\"wv-live-dot\"></span>LIVE</div>` : \"\"}\n ${options.heatmap && options.wsUrl ? `\n <button id=\"heatmap-toggle\" class=\"wv-btn\">Heatmap</button>\n <select id=\"heatmap-metric\" class=\"wv-btn\">\n <option value=\"duration\">Duration</option>\n <option value=\"retryRate\">Retry Rate</option>\n <option value=\"errorRate\">Error Rate</option>\n </select>\n ` : \"\"}\n ${options.interactive ? `\n <button id=\"zoom-out\" class=\"wv-btn wv-btn--icon\" title=\"Zoom out (-)\">−</button>\n <button id=\"zoom-reset\" class=\"wv-btn wv-btn--icon\" title=\"Reset zoom (0)\">⟲</button>\n <button id=\"zoom-in\" class=\"wv-btn wv-btn--icon\" title=\"Zoom in (+)\">+</button>\n ` : \"\"}\n </div>\n </header>\n\n <div class=\"wv-main\">\n <div id=\"diagram\" class=\"wv-diagram\">\n <svg viewBox=\"0 0 ${svgWidth} ${svgHeight}\" preserveAspectRatio=\"xMidYMid meet\">\n <g class=\"wv-root\">\n ${edgesSVG}\n ${nodesSVG}\n </g>\n </svg>\n </div>\n\n ${options.interactive ? `\n <aside id=\"inspector\" class=\"wv-inspector\">\n <div class=\"wv-inspector-header\">\n <h2>Inspector</h2>\n </div>\n <div id=\"inspector-content\" class=\"wv-inspector-content\">\n <p class=\"wv-empty\">Select a node to inspect</p>\n </div>\n </aside>\n ` : \"\"}\n </div>\n\n ${options.timeTravel ? `\n <div id=\"timeline\" class=\"wv-timeline\">\n <div class=\"wv-timeline-track\">\n <input type=\"range\" id=\"tt-slider\" min=\"0\" max=\"0\" value=\"0\" style=\"width:100%\">\n </div>\n <div class=\"wv-timeline-controls\">\n <button id=\"tt-prev\" class=\"wv-btn wv-btn--icon\" title=\"Step backward (←)\">⏮</button>\n <button id=\"tt-play\" class=\"wv-btn wv-btn--icon\" title=\"Play (Space)\">▶</button>\n <button id=\"tt-pause\" class=\"wv-btn wv-btn--icon\" style=\"display:none\" title=\"Pause (Space)\">⏸</button>\n <button id=\"tt-next\" class=\"wv-btn wv-btn--icon\" title=\"Step forward (→)\">⏭</button>\n <select id=\"tt-speed\" class=\"wv-btn\">\n <option value=\"0.5\">0.5x</option>\n <option value=\"1\" selected>1x</option>\n <option value=\"2\">2x</option>\n <option value=\"4\">4x</option>\n <option value=\"10\">10x</option>\n </select>\n <span id=\"tt-time\" class=\"wv-timeline-time\">0 / 0</span>\n </div>\n </div>\n ` : \"\"}\n </div>\n\n ${options.interactive ? `\n <div id=\"load-json-modal\" class=\"wv-modal\" style=\"display:none\">\n <div class=\"wv-modal-content\">\n <div class=\"wv-modal-header\">\n <h2>Load Workflow State</h2>\n <button id=\"load-json-close\" class=\"wv-btn wv-btn--icon\" title=\"Close\">×</button>\n </div>\n <div class=\"wv-modal-body\">\n <p>Paste the workflow IR JSON (from <code>viz.getIR()</code> or <code>viz.renderAs('json')</code>):</p>\n <textarea id=\"load-json-input\" class=\"wv-textarea\" rows=\"15\" placeholder='{\"root\":{\"type\":\"workflow\",\"id\":\"...\",\"children\":[...]}}'></textarea>\n <div id=\"load-json-error\" class=\"wv-error\" style=\"display:none\"></div>\n </div>\n <div class=\"wv-modal-footer\">\n <button id=\"load-json-submit\" class=\"wv-btn wv-btn--primary\">Load</button>\n <button id=\"load-json-cancel\" class=\"wv-btn\">Cancel</button>\n </div>\n </div>\n </div>\n ` : \"\"}\n\n <script>\n // Check if we have a saved IR in sessionStorage (from Load JSON)\n (function() {\n let initialIR = ${safeJsonStringify(serializeIRForScript(ir))};\n try {\n const savedIR = sessionStorage.getItem('workflow_ir');\n if (savedIR) {\n initialIR = JSON.parse(savedIR);\n sessionStorage.removeItem('workflow_ir'); // Clear after use\n }\n } catch (e) {\n console.warn('Failed to load saved IR:', e);\n }\n\n const irObj = typeof initialIR === 'string' ? JSON.parse(initialIR) : initialIR;\n window.__WORKFLOW_IR__ = irObj;\n\n // Build workflow data from IR (nodes include input/output; hooks serialized for inspector)\n function buildWorkflowDataFromIR(ir) {\n const nodes = {};\n function collectNodes(flowNodes) {\n for (const node of flowNodes || []) {\n nodes[node.id] = {\n id: node.id,\n name: node.name,\n type: node.type,\n state: node.state,\n key: node.key,\n durationMs: node.durationMs,\n startTs: node.startTs,\n error: node.error !== undefined && node.error !== null ? String(node.error) : undefined,\n retryCount: node.retryCount,\n input: node.input,\n output: node.output,\n ...(node.type === \"stream\" && {\n namespace: node.namespace,\n writeCount: node.writeCount,\n readCount: node.readCount,\n finalPosition: node.finalPosition,\n streamState: node.streamState,\n backpressureOccurred: node.backpressureOccurred,\n }),\n ...(node.type === \"decision\" && {\n condition: node.condition,\n decisionValue: node.decisionValue,\n branchTaken: node.branchTaken,\n branches: node.branches,\n }),\n };\n if (node.children) collectNodes(node.children);\n if (node.branches) {\n for (const branch of node.branches) {\n collectNodes(branch.children);\n }\n }\n }\n }\n collectNodes(ir.root.children);\n const hooks = ir.hooks\n ? {\n ...ir.hooks,\n onAfterStep:\n ir.hooks.onAfterStep instanceof Map\n ? Object.fromEntries(ir.hooks.onAfterStep)\n : ir.hooks.onAfterStep,\n }\n : undefined;\n return { nodes, hooks };\n }\n\n window.__WORKFLOW_DATA__ = buildWorkflowDataFromIR(irObj);\n\n ${options.heatmapData ? `window.__PERFORMANCE_DATA__ = ${safeJsonStringify({ heat: Object.fromEntries(options.heatmapData.heat), metric: options.heatmapData.metric, stats: options.heatmapData.stats })};` : \"\"}\n })();\n </script>\n <script>${js}</script>\n</body>\n</html>`;\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n}\n\nfunction escapeAttr(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n}\n\nfunction truncate(str: string, maxLen: number): string {\n if (str.length <= maxLen) return str;\n return str.slice(0, maxLen - 1) + \"…\";\n}\n\n/**\n * Convert IR to a JSON-serializable shape (Map → plain object for hooks.onAfterStep).\n * Handles both Map (from builder) and plain object (from JSON.parse after renderAs(\"json\")).\n */\nfunction serializeIRForScript(ir: WorkflowIR): unknown {\n if (!ir.hooks) return ir;\n const onAfterStep =\n ir.hooks.onAfterStep instanceof Map\n ? Object.fromEntries(ir.hooks.onAfterStep)\n : ir.hooks.onAfterStep;\n return {\n ...ir,\n hooks: {\n ...ir.hooks,\n onAfterStep,\n },\n };\n}\n\n/**\n * Safely stringify an object to JSON, handling BigInt, circular references,\n * and other non-serializable types. Also escapes characters that could break script injection.\n */\nfunction safeJsonStringify(obj: unknown): string {\n const seen = new WeakSet<object>();\n const json = JSON.stringify(obj, (key, value) => {\n if (typeof value === \"bigint\") {\n return value.toString();\n }\n if (key === \"error\" && value !== undefined && value !== null) {\n return String(value);\n }\n if (typeof value === \"object\" && value !== null) {\n if (seen.has(value)) {\n return \"[Circular]\";\n }\n seen.add(value);\n }\n return value;\n });\n // Escape characters that could break inline script\n return json\n .replace(/</g, \"\\\\u003c\")\n .replace(/>/g, \"\\\\u003e\")\n .replace(/&/g, \"\\\\u0026\")\n .replace(/\\u2028/g, \"\\\\u2028\")\n .replace(/\\u2029/g, \"\\\\u2029\");\n}\n\n// =============================================================================\n// Renderer Export\n// =============================================================================\n\n/**\n * Default HTML render options.\n */\nconst defaultHTMLOptions: Omit<HTMLRenderOptions, keyof RenderOptions> = {\n interactive: true,\n timeTravel: true,\n heatmap: true,\n animationDuration: 200,\n theme: \"auto\",\n layout: \"TB\",\n};\n\n/**\n * Create the HTML renderer.\n */\nexport function htmlRenderer(): Renderer {\n return {\n name: \"html\",\n supportsLive: true,\n\n render(ir: WorkflowIR, options: RenderOptions): string {\n const htmlOptions: HTMLRenderOptions = {\n ...options,\n ...defaultHTMLOptions,\n ...(options as Partial<HTMLRenderOptions>),\n };\n\n return generateHTML(ir, htmlOptions);\n },\n };\n}\n\n/**\n * Render workflow IR to HTML with custom options.\n */\nexport function renderToHTML(\n ir: WorkflowIR,\n options: Partial<HTMLRenderOptions> = {}\n): string {\n const fullOptions: HTMLRenderOptions = {\n showTimings: true,\n showKeys: false,\n colors: {\n pending: \"#6c757d\",\n running: \"#ffc107\",\n success: \"#198754\",\n error: \"#dc3545\",\n aborted: \"#6c757d\",\n cached: \"#0dcaf0\",\n skipped: \"#adb5bd\",\n },\n ...defaultHTMLOptions,\n ...options,\n };\n\n return generateHTML(ir, fullOptions);\n}\n","/**\n * Decision Tracker - Helper for tracking conditional logic in workflows.\n *\n * This module provides utilities to track decision points (if/switch statements)\n * so they can be visualized in workflow diagrams.\n *\n * @example\n * ```typescript\n * import { trackDecision } from 'awaitly-visualizer';\n *\n * const workflow = createWorkflow(deps, {\n * onEvent: (event) => {\n * // Pass events to visualizer\n * viz.handleEvent(event);\n * }\n * });\n *\n * await workflow(async (step) => {\n * const user = await step(fetchUser(id));\n *\n * // Track a decision point\n * const decision = trackDecision('user-role-check', {\n * condition: 'user.role === \"admin\"',\n * value: user.role\n * });\n *\n * if (user.role === 'admin') {\n * decision.takeBranch('admin', true);\n * await step(processAdminAction(user));\n * } else {\n * decision.takeBranch('user', false);\n * await step(processUserAction(user));\n * }\n *\n * decision.end();\n * });\n * ```\n */\n\nimport type {\n DecisionStartEvent,\n DecisionBranchEvent,\n DecisionEndEvent,\n} from \"./types\";\n\n// =============================================================================\n// Decision Tracker\n// =============================================================================\n\n/**\n * Options for creating a decision tracker.\n */\nexport interface DecisionTrackerOptions {\n /** Condition being evaluated (e.g., \"user.role === 'admin'\") */\n condition?: string;\n /** Value being evaluated */\n value?: unknown;\n /** Name/label for this decision point */\n name?: string;\n /** Workflow ID (auto-generated if not provided) */\n workflowId?: string;\n /** Event emitter function */\n emit?: (event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent) => void;\n}\n\n/**\n * Track a decision point in your workflow.\n *\n * Use this to annotate conditional logic (if/switch) so it appears\n * in workflow visualizations.\n *\n * @param decisionId - Unique identifier for this decision\n * @param options - Decision tracking options\n * @returns A tracker object with methods to track branches\n *\n * @example\n * ```typescript\n * const decision = trackDecision('check-role', {\n * condition: 'user.role === \"admin\"',\n * value: user.role,\n * emit: (event) => viz.handleDecisionEvent(event)\n * });\n *\n * if (user.role === 'admin') {\n * decision.takeBranch('admin', true);\n * // ... admin logic\n * } else {\n * decision.takeBranch('user', false);\n * // ... user logic\n * }\n *\n * decision.end();\n * ```\n */\nexport function trackDecision(\n decisionId: string,\n options: DecisionTrackerOptions = {}\n): DecisionTracker {\n const {\n condition,\n value,\n name,\n workflowId = crypto.randomUUID(),\n emit,\n } = options;\n\n const startTs = Date.now();\n let branchTaken: string | boolean | undefined;\n const branches: Array<{ label: string; condition?: string; taken: boolean }> = [];\n\n function takeBranch(\n label: string,\n taken: boolean,\n branchCondition?: string\n ): void {\n branches.push({ label, condition: branchCondition, taken });\n if (taken) {\n branchTaken = label;\n }\n\n emit?.({\n type: \"decision_branch\",\n workflowId,\n decisionId,\n branchLabel: label,\n condition: branchCondition,\n taken,\n ts: Date.now(),\n });\n }\n\n function end(): void {\n const durationMs = Date.now() - startTs;\n emit?.({\n type: \"decision_end\",\n workflowId,\n decisionId,\n branchTaken,\n ts: Date.now(),\n durationMs,\n });\n }\n\n // Emit start event immediately\n emit?.({\n type: \"decision_start\",\n workflowId,\n decisionId,\n condition,\n decisionValue: value,\n name,\n ts: startTs,\n });\n\n return {\n takeBranch,\n end,\n getBranchTaken: () => branchTaken,\n getBranches: () => [...branches],\n };\n}\n\n/**\n * Decision tracker instance.\n */\nexport interface DecisionTracker {\n /**\n * Mark that a branch was taken or skipped.\n * @param label - Label for this branch (e.g., \"if\", \"else\", \"case 'admin'\")\n * @param taken - Whether this branch was executed\n * @param branchCondition - Optional condition for this specific branch\n */\n takeBranch(label: string, taken: boolean, branchCondition?: string): void;\n\n /**\n * End the decision tracking.\n * Call this after all branches have been evaluated.\n */\n end(): void;\n\n /**\n * Get which branch was taken.\n */\n getBranchTaken(): string | boolean | undefined;\n\n /**\n * Get all branches (taken and skipped).\n */\n getBranches(): Array<{ label: string; condition?: string; taken: boolean }>;\n}\n\n// =============================================================================\n// Convenience Helpers\n// =============================================================================\n\n/**\n * Track a simple if/else decision.\n *\n * @example\n * ```typescript\n * const decision = trackIf('check-admin', user.role === 'admin', {\n * condition: 'user.role === \"admin\"',\n * value: user.role,\n * emit: (e) => viz.handleDecisionEvent(e)\n * });\n *\n * if (decision.condition) {\n * decision.then();\n * // admin logic\n * } else {\n * decision.else();\n * // user logic\n * }\n *\n * decision.end();\n * ```\n */\nexport function trackIf(\n decisionId: string,\n condition: boolean,\n options: Omit<DecisionTrackerOptions, \"value\"> & { value?: unknown } = {}\n): IfTracker {\n const tracker = trackDecision(decisionId, {\n ...options,\n condition: options.condition ?? String(condition),\n value: options.value ?? condition,\n });\n\n // Track if branches have been emitted to avoid re-emitting\n let branchesEmitted = false;\n\n return {\n ...tracker,\n condition,\n then: () => {\n if (branchesEmitted) return;\n branchesEmitted = true;\n // Emit both branches so the skipped \"else\" is visible in diagrams\n tracker.takeBranch(\"if\", true);\n tracker.takeBranch(\"else\", false);\n },\n else: () => {\n if (branchesEmitted) return;\n branchesEmitted = true;\n // Emit both branches so the skipped \"if\" is visible in diagrams\n tracker.takeBranch(\"if\", false);\n tracker.takeBranch(\"else\", true);\n },\n };\n}\n\n/**\n * If tracker with convenience methods.\n */\nexport interface IfTracker extends DecisionTracker {\n /** The condition value */\n condition: boolean;\n /** Mark the \"if\" branch as taken */\n then(): void;\n /** Mark the \"else\" branch as taken */\n else(): void;\n}\n\n/**\n * Track a switch statement decision.\n *\n * @example\n * ```typescript\n * const decision = trackSwitch('process-type', user.type, {\n * emit: (e) => viz.handleDecisionEvent(e)\n * });\n *\n * switch (user.type) {\n * case 'admin':\n * decision.case('admin', true);\n * // admin logic\n * break;\n * case 'user':\n * decision.case('user', true);\n * // user logic\n * break;\n * default:\n * decision.default(true);\n * // default logic\n * }\n *\n * decision.end();\n * ```\n */\nexport function trackSwitch(\n decisionId: string,\n value: unknown,\n options: Omit<DecisionTrackerOptions, \"value\"> = {}\n): SwitchTracker {\n const tracker = trackDecision(decisionId, {\n ...options,\n condition: options.condition ?? `switch(${String(value)})`,\n value,\n });\n\n return {\n ...tracker,\n value,\n case: (caseValue: string | number, taken: boolean) => {\n tracker.takeBranch(`case '${caseValue}'`, taken, `value === '${caseValue}'`);\n },\n default: (taken: boolean) => {\n tracker.takeBranch(\"default\", taken);\n },\n };\n}\n\n/**\n * Switch tracker with convenience methods.\n */\nexport interface SwitchTracker extends DecisionTracker {\n /** The value being switched on */\n value: unknown;\n /** Mark a case branch */\n case(caseValue: string | number, taken: boolean): void;\n /** Mark the default branch */\n default(taken: boolean): void;\n}\n","/**\n * Time Travel Debugger\n *\n * Records IR snapshots at each event, enabling:\n * - Step-by-step forward/backward navigation\n * - Seeking to any point in execution\n * - Playback at various speeds\n * - State inspection at any moment\n */\n\nimport type { WorkflowEvent } from \"awaitly/workflow\";\nimport type {\n IRSnapshot,\n TimeTravelState,\n WorkflowIR,\n} from \"./types\";\nimport { createIRBuilder, type IRBuilder, type IRBuilderOptions } from \"./ir-builder\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Options for the time-travel controller.\n */\nexport interface TimeTravelOptions {\n /** Maximum snapshots to store (ring buffer) */\n maxSnapshots?: number;\n /** Automatically record snapshots on each event */\n autoRecord?: boolean;\n /** IR builder options (passed through) */\n builderOptions?: Omit<IRBuilderOptions, \"enableSnapshots\" | \"maxSnapshots\">;\n}\n\n/**\n * Time-travel controller interface.\n */\nexport interface TimeTravelController {\n /** Handle a workflow event (records snapshot if recording) */\n handleEvent: (event: WorkflowEvent<unknown>) => void;\n\n /** Navigate to specific snapshot index */\n seek: (index: number) => WorkflowIR | undefined;\n\n /** Move one step forward */\n stepForward: () => WorkflowIR | undefined;\n\n /** Move one step backward */\n stepBackward: () => WorkflowIR | undefined;\n\n /** Start playback at given speed (1.0 = realtime) */\n play: (speed?: number) => void;\n\n /** Pause playback */\n pause: () => void;\n\n /** Get current IR state */\n getCurrentIR: () => WorkflowIR;\n\n /** Get IR at specific snapshot index */\n getIRAt: (index: number) => WorkflowIR | undefined;\n\n /** Get all snapshots */\n getSnapshots: () => IRSnapshot[];\n\n /** Get snapshot at specific index */\n getSnapshotAt: (index: number) => IRSnapshot | undefined;\n\n /** Get current time-travel state */\n getState: () => TimeTravelState;\n\n /** Subscribe to state changes */\n onStateChange: (callback: (state: TimeTravelState) => void) => () => void;\n\n /** Start/resume recording */\n startRecording: () => void;\n\n /** Stop recording */\n stopRecording: () => void;\n\n /** Reset to initial state */\n reset: () => void;\n\n /** Get the underlying IR builder */\n getBuilder: () => IRBuilder;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Create a time-travel controller for workflow debugging.\n *\n * @example\n * ```typescript\n * const controller = createTimeTravelController();\n *\n * // Feed events from workflow execution\n * workflow.onEvent(event => controller.handleEvent(event));\n *\n * // After execution, explore the timeline\n * controller.seek(0); // Go to start\n * controller.stepForward(); // Step through\n * controller.play(2); // Playback at 2x speed\n * ```\n */\nexport function createTimeTravelController(\n options: TimeTravelOptions = {}\n): TimeTravelController {\n const { maxSnapshots = 1000, autoRecord = true, builderOptions = {} } = options;\n\n // Create IR builder with snapshots enabled\n const builder = createIRBuilder({\n ...builderOptions,\n enableSnapshots: true,\n maxSnapshots,\n });\n\n // Controller state\n let state: TimeTravelState = {\n snapshots: [],\n currentIndex: -1,\n isPlaying: false,\n playbackSpeed: 1.0,\n isRecording: autoRecord,\n };\n\n // State change listeners\n const listeners = new Set<(state: TimeTravelState) => void>();\n\n // Playback timer\n let playbackTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Notify all listeners of state change.\n */\n function notifyListeners(): void {\n const currentState = getState();\n for (const listener of listeners) {\n listener(currentState);\n }\n }\n\n /**\n * Sync state from builder snapshots.\n */\n function syncFromBuilder(): void {\n state.snapshots = builder.getSnapshots();\n if (state.isRecording && state.snapshots.length > 0) {\n state.currentIndex = state.snapshots.length - 1;\n }\n }\n\n /**\n * Handle a workflow event.\n */\n function handleEvent(event: WorkflowEvent<unknown>): void {\n // Always forward to builder\n builder.handleEvent(event);\n\n // Sync snapshots if recording\n if (state.isRecording) {\n syncFromBuilder();\n notifyListeners();\n }\n }\n\n /**\n * Seek to a specific snapshot index.\n */\n function seek(index: number): WorkflowIR | undefined {\n const snapshots = builder.getSnapshots();\n if (index < 0 || index >= snapshots.length) {\n return undefined;\n }\n\n state.currentIndex = index;\n state.snapshots = snapshots;\n notifyListeners();\n\n return snapshots[index].ir;\n }\n\n /**\n * Move one step forward.\n */\n function stepForward(): WorkflowIR | undefined {\n return seek(state.currentIndex + 1);\n }\n\n /**\n * Move one step backward.\n */\n function stepBackward(): WorkflowIR | undefined {\n return seek(state.currentIndex - 1);\n }\n\n /**\n * Start playback at given speed.\n */\n function play(speed = 1.0): void {\n const snapshots = builder.getSnapshots();\n\n // If no snapshots, nothing to play\n if (snapshots.length === 0) {\n return;\n }\n\n // If currentIndex is -1 (no seek yet), start from the beginning\n if (state.currentIndex === -1) {\n state.currentIndex = 0;\n state.snapshots = snapshots;\n }\n\n state.playbackSpeed = speed;\n state.isPlaying = true;\n notifyListeners();\n\n const playNext = (): void => {\n if (!state.isPlaying) return;\n\n const currentSnapshots = builder.getSnapshots();\n if (state.currentIndex < currentSnapshots.length - 1) {\n const current = currentSnapshots[state.currentIndex];\n const next = currentSnapshots[state.currentIndex + 1];\n\n // Calculate delay based on actual event timing\n const realDelay = next.timestamp - current.timestamp;\n const scaledDelay = realDelay / state.playbackSpeed;\n\n playbackTimer = setTimeout(() => {\n stepForward();\n playNext();\n }, Math.max(16, scaledDelay)); // Minimum 16ms (~60fps) for smooth updates\n } else {\n // Reached end of timeline\n pause();\n }\n };\n\n playNext();\n }\n\n /**\n * Pause playback.\n */\n function pause(): void {\n state.isPlaying = false;\n if (playbackTimer) {\n clearTimeout(playbackTimer);\n playbackTimer = null;\n }\n notifyListeners();\n }\n\n /**\n * Get current IR state.\n */\n function getCurrentIR(): WorkflowIR {\n const snapshots = builder.getSnapshots();\n if (state.currentIndex >= 0 && state.currentIndex < snapshots.length) {\n return snapshots[state.currentIndex].ir;\n }\n // Return live IR if no valid snapshot\n return builder.getIR();\n }\n\n /**\n * Get IR at specific snapshot index.\n */\n function getIRAt(index: number): WorkflowIR | undefined {\n return builder.getIRAt(index);\n }\n\n /**\n * Get all snapshots.\n */\n function getSnapshots(): IRSnapshot[] {\n return builder.getSnapshots();\n }\n\n /**\n * Get snapshot at specific index.\n */\n function getSnapshotAt(index: number): IRSnapshot | undefined {\n return builder.getSnapshotAt(index);\n }\n\n /**\n * Get current time-travel state.\n */\n function getState(): TimeTravelState {\n return {\n snapshots: builder.getSnapshots(),\n currentIndex: state.currentIndex,\n isPlaying: state.isPlaying,\n playbackSpeed: state.playbackSpeed,\n isRecording: state.isRecording,\n };\n }\n\n /**\n * Subscribe to state changes.\n */\n function onStateChange(\n callback: (state: TimeTravelState) => void\n ): () => void {\n listeners.add(callback);\n return () => listeners.delete(callback);\n }\n\n /**\n * Start or resume recording.\n */\n function startRecording(): void {\n state.isRecording = true;\n builder.setSnapshotsEnabled(true);\n // Sync currentIndex to latest snapshot when recording starts\n syncFromBuilder();\n notifyListeners();\n }\n\n /**\n * Stop recording.\n */\n function stopRecording(): void {\n state.isRecording = false;\n builder.setSnapshotsEnabled(false);\n notifyListeners();\n }\n\n /**\n * Reset to initial state.\n */\n function reset(): void {\n pause();\n builder.reset();\n builder.setSnapshotsEnabled(autoRecord);\n state = {\n snapshots: [],\n currentIndex: -1,\n isPlaying: false,\n playbackSpeed: 1.0,\n isRecording: autoRecord,\n };\n notifyListeners();\n }\n\n /**\n * Get the underlying IR builder.\n */\n function getBuilder(): IRBuilder {\n return builder;\n }\n\n return {\n handleEvent,\n seek,\n stepForward,\n stepBackward,\n play,\n pause,\n getCurrentIR,\n getIRAt,\n getSnapshots,\n getSnapshotAt,\n getState,\n onStateChange,\n startRecording,\n stopRecording,\n reset,\n getBuilder,\n };\n}\n","/**\n * Kroki Encoder\n *\n * Encodes Mermaid diagram text for Kroki URLs using pako deflate + base64url.\n * Uses Buffer in Node (btoa/atob not available) and btoa/atob in browsers.\n */\n\nimport pako from \"pako\";\n\n/** True when Buffer is available (Node). */\nconst hasBuffer = typeof globalThis !== \"undefined\" && \"Buffer\" in globalThis && typeof (globalThis as { Buffer?: unknown }).Buffer === \"function\";\n\n/**\n * Base64URL encode bytes (URL-safe base64).\n * Uses `-` and `_` instead of `+` and `/`, and omits padding.\n * Node-safe: uses Buffer when available, otherwise btoa.\n */\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let base64: string;\n if (hasBuffer) {\n const B = (globalThis as unknown as { Buffer: { from: (u: Uint8Array) => { toString: (enc: string) => string } } }).Buffer;\n base64 = B.from(bytes).toString(\"base64\");\n } else {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n base64 = btoa(binary);\n }\n return base64\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\"); // Remove padding\n}\n\n/**\n * Decode standard base64 to bytes.\n * Node-safe: uses Buffer when available, otherwise atob.\n */\nfunction base64ToBytes(base64: string): Uint8Array {\n if (hasBuffer) {\n const B = (globalThis as unknown as { Buffer: { from: (s: string, enc: string) => Uint8Array } }).Buffer;\n return B.from(base64, \"base64\");\n }\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n/**\n * Encode text for Kroki URL.\n * Uses pako deflate compression + base64url encoding.\n *\n * @param text - The text to encode (e.g., Mermaid diagram)\n * @returns URL-safe encoded string\n *\n * @example\n * ```typescript\n * const encoded = encodeForKroki('flowchart TD\\n A-->B');\n * // => \"eNpLzs8tyc9NTgQADsMDmA\"\n * ```\n */\nexport function encodeForKroki(text: string): string {\n // Convert string to UTF-8 bytes\n const textEncoder = new TextEncoder();\n const textBytes = textEncoder.encode(text);\n\n // Compress with deflate\n const compressed = pako.deflate(textBytes);\n\n // Base64URL encode\n return base64UrlEncode(compressed);\n}\n\n/**\n * Decode Kroki URL payload back to text.\n * Uses base64url decoding + pako inflate.\n *\n * @param encoded - The encoded string from a Kroki URL\n * @returns The original text\n *\n * @example\n * ```typescript\n * const text = decodeFromKroki('eNpLzs8tyc9NTgQADsMDmA');\n * // => \"flowchart TD\\n A-->B\"\n * ```\n */\nexport function decodeFromKroki(encoded: string): string {\n // Convert URL-safe base64 to standard base64\n let base64 = encoded.replace(/-/g, \"+\").replace(/_/g, \"/\");\n\n // Add padding if needed\n const padding = 4 - (base64.length % 4);\n if (padding !== 4) {\n base64 += \"=\".repeat(padding);\n }\n\n const bytes = base64ToBytes(base64);\n const decompressed = pako.inflate(bytes);\n const textDecoder = new TextDecoder();\n return textDecoder.decode(decompressed);\n}\n","/**\n * Kroki URL Generation\n *\n * Generates shareable URLs for Kroki diagram rendering service.\n * Works in both browser and Node.js environments.\n */\n\nimport type { WorkflowIR, RenderOptions, KrokiExportOptions } from \"../types\";\nimport { mermaidRenderer, defaultColorScheme } from \"../renderers\";\nimport { encodeForKroki } from \"./encoder\";\n\n/**\n * Supported Kroki output formats.\n */\nexport type KrokiFormat = \"svg\" | \"png\" | \"pdf\" | \"jpeg\";\n\n/**\n * Options for URL generator.\n */\nexport interface UrlGeneratorOptions {\n /** Base URL for Kroki service (default: https://kroki.io) */\n baseUrl?: string;\n}\n\n/**\n * Default Kroki base URL.\n */\nconst DEFAULT_KROKI_URL = \"https://kroki.io\";\n\n/**\n * Build a Kroki URL for the given diagram.\n *\n * @param diagramType - Diagram type (e.g., \"mermaid\", \"plantuml\", \"graphviz\")\n * @param format - Output format (svg, png, pdf, jpeg)\n * @param text - The diagram text\n * @param options - URL generator options (KrokiExportOptions or legacy UrlGeneratorOptions)\n * @returns The Kroki URL\n *\n * @example\n * ```typescript\n * const url = buildKrokiUrl('mermaid', 'svg', 'flowchart TD\\n A-->B');\n * // => \"https://kroki.io/mermaid/svg/eNpLzs8tyc9NTgQADsMDmA\"\n *\n * // With explicit KrokiExportOptions\n * const url2 = buildKrokiUrl('mermaid', 'svg', 'flowchart TD\\n A-->B', {\n * provider: 'kroki',\n * baseUrl: 'https://kroki.internal'\n * });\n * ```\n */\nexport function buildKrokiUrl(\n diagramType: string,\n format: KrokiFormat,\n text: string,\n options: KrokiExportOptions | UrlGeneratorOptions = {}\n): string {\n const baseUrl = options.baseUrl ?? DEFAULT_KROKI_URL;\n const encoded = encodeForKroki(text);\n return `${baseUrl}/${diagramType}/${format}/${encoded}`;\n}\n\n/**\n * Generate a Kroki URL from workflow IR.\n *\n * @param ir - Workflow intermediate representation\n * @param format - Output format (default: 'svg')\n * @param options - Optional URL generator options\n * @returns The Kroki URL\n *\n * @example\n * ```typescript\n * const url = toKrokiUrl(workflowIR, 'svg');\n * // Share this URL - image renders when viewed\n * ```\n */\nexport function toKrokiUrl(\n ir: WorkflowIR,\n format: KrokiFormat = \"svg\",\n options: UrlGeneratorOptions = {}\n): string {\n const renderer = mermaidRenderer();\n const renderOptions: RenderOptions = {\n showTimings: true,\n showKeys: false,\n terminalWidth: 80,\n colors: defaultColorScheme,\n };\n\n const mermaidText = renderer.render(ir, renderOptions);\n return buildKrokiUrl(\"mermaid\", format, mermaidText, options);\n}\n\n/**\n * Generate a Kroki SVG URL from workflow IR.\n *\n * @param ir - Workflow intermediate representation\n * @param options - Optional URL generator options\n * @returns The Kroki SVG URL\n *\n * @example\n * ```typescript\n * const svgUrl = toKrokiSvgUrl(workflowIR);\n * // => \"https://kroki.io/mermaid/svg/eNp...\"\n * ```\n */\nexport function toKrokiSvgUrl(\n ir: WorkflowIR,\n options: UrlGeneratorOptions = {}\n): string {\n return toKrokiUrl(ir, \"svg\", options);\n}\n\n/**\n * Generate a Kroki PNG URL from workflow IR.\n *\n * @param ir - Workflow intermediate representation\n * @param options - Optional URL generator options\n * @returns The Kroki PNG URL\n *\n * @example\n * ```typescript\n * const pngUrl = toKrokiPngUrl(workflowIR);\n * // => \"https://kroki.io/mermaid/png/eNp...\"\n * ```\n */\nexport function toKrokiPngUrl(\n ir: WorkflowIR,\n options: UrlGeneratorOptions = {}\n): string {\n return toKrokiUrl(ir, \"png\", options);\n}\n\n/**\n * URL Generator with configured base URL.\n */\nexport interface UrlGenerator {\n /** Generate URL with specified format */\n toUrl(ir: WorkflowIR, format: KrokiFormat): string;\n /** Generate SVG URL */\n toSvgUrl(ir: WorkflowIR): string;\n /** Generate PNG URL */\n toPngUrl(ir: WorkflowIR): string;\n /** Generate PDF URL */\n toPdfUrl(ir: WorkflowIR): string;\n /** Get the configured base URL */\n getBaseUrl(): string;\n}\n\n/**\n * Create a URL generator with a custom base URL.\n * Useful for self-hosted Kroki instances.\n *\n * @param options - URL generator options\n * @returns A URL generator instance\n *\n * @example\n * ```typescript\n * // Use self-hosted Kroki\n * const generator = createUrlGenerator({ baseUrl: 'https://my-kroki.internal' });\n * const url = generator.toSvgUrl(workflowIR);\n *\n * // Default public Kroki\n * const defaultGenerator = createUrlGenerator();\n * const publicUrl = defaultGenerator.toSvgUrl(workflowIR);\n * ```\n */\nexport function createUrlGenerator(options: UrlGeneratorOptions = {}): UrlGenerator {\n const baseUrl = options.baseUrl ?? DEFAULT_KROKI_URL;\n\n return {\n toUrl(ir: WorkflowIR, format: KrokiFormat): string {\n return toKrokiUrl(ir, format, { baseUrl });\n },\n\n toSvgUrl(ir: WorkflowIR): string {\n return toKrokiUrl(ir, \"svg\", { baseUrl });\n },\n\n toPngUrl(ir: WorkflowIR): string {\n return toKrokiUrl(ir, \"png\", { baseUrl });\n },\n\n toPdfUrl(ir: WorkflowIR): string {\n return toKrokiUrl(ir, \"pdf\", { baseUrl });\n },\n\n getBaseUrl(): string {\n return baseUrl;\n },\n };\n}\n","/**\n * Mermaid.ink URL Generation\n *\n * Generates shareable URLs for mermaid.ink diagram rendering service.\n * Alternative to Kroki with additional features like themes, background colors, and sizing.\n *\n * @see https://mermaid.ink/\n */\n\nimport type { WorkflowIR, RenderOptions, MermaidInkExportOptions } from \"../types\";\nimport { mermaidRenderer, defaultColorScheme } from \"../renderers\";\nimport { encodeForKroki } from \"./encoder\";\n\n/**\n * Supported mermaid.ink output formats.\n */\nexport type MermaidInkFormat = \"svg\" | \"img\" | \"pdf\";\n\n/**\n * Image type for /img endpoint.\n */\nexport type MermaidInkImageType = \"jpeg\" | \"png\" | \"webp\";\n\n/**\n * Mermaid.ink built-in themes.\n */\nexport type MermaidInkTheme = \"default\" | \"neutral\" | \"dark\" | \"forest\";\n\n/**\n * PDF paper sizes.\n */\nexport type MermaidInkPaperSize =\n | \"letter\"\n | \"legal\"\n | \"tabloid\"\n | \"ledger\"\n | \"a0\"\n | \"a1\"\n | \"a2\"\n | \"a3\"\n | \"a4\"\n | \"a5\"\n | \"a6\";\n\n/**\n * Options for mermaid.ink URL generation.\n */\nexport interface MermaidInkOptions {\n /** Base URL for mermaid.ink service (default: https://mermaid.ink) */\n baseUrl?: string;\n\n /**\n * Background color.\n * - Hex color without #: \"FF0000\" for red\n * - Named color with ! prefix: \"!white\", \"!black\"\n */\n bgColor?: string;\n\n /** Mermaid theme */\n theme?: MermaidInkTheme;\n\n /** Image width in pixels */\n width?: number;\n\n /** Image height in pixels */\n height?: number;\n\n /** Image scale (1-3). Only applies if width or height is set */\n scale?: number;\n\n /** Image type for /img endpoint (default: jpeg) */\n imageType?: MermaidInkImageType;\n\n // PDF-specific options\n\n /** Fit PDF size to diagram size */\n fit?: boolean;\n\n /** Paper size for PDF (default: a4) */\n paper?: MermaidInkPaperSize;\n\n /** Landscape orientation for PDF */\n landscape?: boolean;\n}\n\n/**\n * Default mermaid.ink base URL.\n */\nconst DEFAULT_MERMAID_INK_URL = \"https://mermaid.ink\";\n\n/**\n * Encode text for mermaid.ink URL.\n * Uses pako deflate compression + base64 encoding with \"pako:\" prefix.\n *\n * @param text - The Mermaid diagram text\n * @returns Encoded string with \"pako:\" prefix\n */\nexport function encodeForMermaidInk(text: string): string {\n const encoded = encodeForKroki(text);\n return `pako:${encoded}`;\n}\n\n/**\n * Normalize export options to internal MermaidInkOptions.\n * Maps MermaidInkExportOptions fields to MermaidInkOptions fields.\n */\nfunction normalizeOptions(\n options: MermaidInkOptions | MermaidInkExportOptions\n): MermaidInkOptions {\n // If it's MermaidInkExportOptions (has \"provider\" field), normalize it\n if (\"provider\" in options) {\n const exportOpts = options as MermaidInkExportOptions;\n return {\n theme: exportOpts.mermaidTheme,\n bgColor: exportOpts.background,\n scale: exportOpts.scale,\n fit: exportOpts.fit,\n width: exportOpts.width,\n height: exportOpts.height,\n paper: exportOpts.paper as MermaidInkPaperSize | undefined,\n // MermaidInkExportOptions uses \"png\" format via toExportUrl, set imageType\n imageType: \"png\",\n };\n }\n // Already MermaidInkOptions\n return options;\n}\n\n/**\n * Build query string from options.\n */\nfunction buildQueryString(\n format: MermaidInkFormat,\n options: MermaidInkOptions\n): string {\n const params: string[] = [];\n\n // Common options\n if (options.bgColor) {\n params.push(`bgColor=${encodeURIComponent(options.bgColor)}`);\n }\n if (options.theme) {\n params.push(`theme=${options.theme}`);\n }\n if (options.width !== undefined) {\n params.push(`width=${options.width}`);\n }\n if (options.height !== undefined) {\n params.push(`height=${options.height}`);\n }\n if (options.scale !== undefined && (options.width !== undefined || options.height !== undefined)) {\n params.push(`scale=${options.scale}`);\n }\n\n // Image-specific options\n if (format === \"img\" && options.imageType && options.imageType !== \"jpeg\") {\n params.push(`type=${options.imageType}`);\n }\n\n // PDF-specific options\n if (format === \"pdf\") {\n if (options.fit) {\n params.push(\"fit\");\n }\n if (options.paper && !options.fit) {\n params.push(`paper=${options.paper}`);\n }\n if (options.landscape && !options.fit) {\n params.push(\"landscape\");\n }\n }\n\n return params.length > 0 ? `?${params.join(\"&\")}` : \"\";\n}\n\n/**\n * Build a mermaid.ink URL for the given Mermaid diagram text.\n *\n * @param format - Output format (svg, img, pdf)\n * @param text - The Mermaid diagram text\n * @param options - Optional mermaid.ink options (MermaidInkOptions or MermaidInkExportOptions)\n * @returns The mermaid.ink URL\n *\n * @example\n * ```typescript\n * const url = buildMermaidInkUrl('svg', 'flowchart TD\\n A-->B');\n * // => \"https://mermaid.ink/svg/pako:eNpLzs8tyc9NTgQADsMDmA\"\n *\n * const darkUrl = buildMermaidInkUrl('svg', 'flowchart TD\\n A-->B', {\n * theme: 'dark',\n * bgColor: '1b1b1f'\n * });\n * // => \"https://mermaid.ink/svg/pako:eNp...?theme=dark&bgColor=1b1b1f\"\n *\n * // With MermaidInkExportOptions\n * const exportUrl = buildMermaidInkUrl('svg', 'flowchart TD\\n A-->B', {\n * provider: 'mermaid-ink',\n * mermaidTheme: 'dark',\n * background: '1b1b1f'\n * });\n * ```\n */\nexport function buildMermaidInkUrl(\n format: MermaidInkFormat,\n text: string,\n options: MermaidInkOptions | MermaidInkExportOptions = {}\n): string {\n const normalized = normalizeOptions(options);\n const baseUrl = normalized.baseUrl ?? DEFAULT_MERMAID_INK_URL;\n const encoded = encodeForMermaidInk(text);\n const queryString = buildQueryString(format, normalized);\n return `${baseUrl}/${format}/${encoded}${queryString}`;\n}\n\n/**\n * Generate a mermaid.ink URL from workflow IR.\n *\n * @param ir - Workflow intermediate representation\n * @param format - Output format (default: 'svg')\n * @param options - Optional mermaid.ink options\n * @returns The mermaid.ink URL\n *\n * @example\n * ```typescript\n * const url = toMermaidInkUrl(workflowIR, 'svg');\n * // Share this URL - image renders when viewed\n *\n * const darkUrl = toMermaidInkUrl(workflowIR, 'svg', { theme: 'dark' });\n * ```\n */\nexport function toMermaidInkUrl(\n ir: WorkflowIR,\n format: MermaidInkFormat = \"svg\",\n options: MermaidInkOptions = {}\n): string {\n const renderer = mermaidRenderer();\n const renderOptions: RenderOptions = {\n showTimings: true,\n showKeys: false,\n terminalWidth: 80,\n colors: defaultColorScheme,\n };\n\n const mermaidText = renderer.render(ir, renderOptions);\n return buildMermaidInkUrl(format, mermaidText, options);\n}\n\n/**\n * Generate a mermaid.ink SVG URL from workflow IR.\n *\n * @param ir - Workflow intermediate representation\n * @param options - Optional mermaid.ink options\n * @returns The mermaid.ink SVG URL\n *\n * @example\n * ```typescript\n * const svgUrl = toMermaidInkSvgUrl(workflowIR);\n * // => \"https://mermaid.ink/svg/pako:eNp...\"\n *\n * const darkSvg = toMermaidInkSvgUrl(workflowIR, { theme: 'dark' });\n * ```\n */\nexport function toMermaidInkSvgUrl(\n ir: WorkflowIR,\n options: MermaidInkOptions = {}\n): string {\n return toMermaidInkUrl(ir, \"svg\", options);\n}\n\n/**\n * Generate a mermaid.ink PNG URL from workflow IR.\n *\n * @param ir - Workflow intermediate representation\n * @param options - Optional mermaid.ink options\n * @returns The mermaid.ink PNG URL\n *\n * @example\n * ```typescript\n * const pngUrl = toMermaidInkPngUrl(workflowIR);\n * // => \"https://mermaid.ink/img/pako:eNp...?type=png\"\n *\n * const scaledPng = toMermaidInkPngUrl(workflowIR, { width: 800, scale: 2 });\n * ```\n */\nexport function toMermaidInkPngUrl(\n ir: WorkflowIR,\n options: MermaidInkOptions = {}\n): string {\n return toMermaidInkUrl(ir, \"img\", { ...options, imageType: \"png\" });\n}\n\n/**\n * Generate a mermaid.ink JPEG URL from workflow IR.\n *\n * @param ir - Workflow intermediate representation\n * @param options - Optional mermaid.ink options\n * @returns The mermaid.ink JPEG URL\n */\nexport function toMermaidInkJpegUrl(\n ir: WorkflowIR,\n options: MermaidInkOptions = {}\n): string {\n return toMermaidInkUrl(ir, \"img\", { ...options, imageType: \"jpeg\" });\n}\n\n/**\n * Generate a mermaid.ink WebP URL from workflow IR.\n *\n * @param ir - Workflow intermediate representation\n * @param options - Optional mermaid.ink options\n * @returns The mermaid.ink WebP URL\n */\nexport function toMermaidInkWebpUrl(\n ir: WorkflowIR,\n options: MermaidInkOptions = {}\n): string {\n return toMermaidInkUrl(ir, \"img\", { ...options, imageType: \"webp\" });\n}\n\n/**\n * Generate a mermaid.ink PDF URL from workflow IR.\n *\n * @param ir - Workflow intermediate representation\n * @param options - Optional mermaid.ink options (fit, paper, landscape)\n * @returns The mermaid.ink PDF URL\n *\n * @example\n * ```typescript\n * // Fit PDF to diagram size\n * const fitPdf = toMermaidInkPdfUrl(workflowIR, { fit: true });\n *\n * // A3 landscape\n * const a3Pdf = toMermaidInkPdfUrl(workflowIR, { paper: 'a3', landscape: true });\n * ```\n */\nexport function toMermaidInkPdfUrl(\n ir: WorkflowIR,\n options: MermaidInkOptions = {}\n): string {\n return toMermaidInkUrl(ir, \"pdf\", options);\n}\n\n/**\n * Mermaid.ink URL Generator interface.\n */\nexport interface MermaidInkGenerator {\n /** Generate URL with specified format */\n toUrl(ir: WorkflowIR, format: MermaidInkFormat): string;\n /** Generate SVG URL */\n toSvgUrl(ir: WorkflowIR): string;\n /** Generate PNG URL */\n toPngUrl(ir: WorkflowIR): string;\n /** Generate JPEG URL */\n toJpegUrl(ir: WorkflowIR): string;\n /** Generate WebP URL */\n toWebpUrl(ir: WorkflowIR): string;\n /** Generate PDF URL */\n toPdfUrl(ir: WorkflowIR): string;\n /** Get the configured base URL */\n getBaseUrl(): string;\n /** Get the configured options */\n getOptions(): MermaidInkOptions;\n}\n\n/**\n * Create a mermaid.ink URL generator with default options.\n * Useful for consistent theming across all generated URLs.\n *\n * @param options - Default mermaid.ink options applied to all URLs\n * @returns A mermaid.ink URL generator instance\n *\n * @example\n * ```typescript\n * // Create generator with dark theme defaults\n * const generator = createMermaidInkGenerator({\n * theme: 'dark',\n * bgColor: '1b1b1f',\n * });\n *\n * // All URLs will use dark theme\n * const svgUrl = generator.toSvgUrl(workflowIR);\n * const pngUrl = generator.toPngUrl(workflowIR);\n *\n * // Self-hosted mermaid.ink\n * const privateGenerator = createMermaidInkGenerator({\n * baseUrl: 'https://mermaid.internal.company.com',\n * });\n * ```\n */\nexport function createMermaidInkGenerator(\n options: MermaidInkOptions = {}\n): MermaidInkGenerator {\n const baseUrl = options.baseUrl ?? DEFAULT_MERMAID_INK_URL;\n\n return {\n toUrl(ir: WorkflowIR, format: MermaidInkFormat): string {\n return toMermaidInkUrl(ir, format, options);\n },\n\n toSvgUrl(ir: WorkflowIR): string {\n return toMermaidInkUrl(ir, \"svg\", options);\n },\n\n toPngUrl(ir: WorkflowIR): string {\n return toMermaidInkUrl(ir, \"img\", { ...options, imageType: \"png\" });\n },\n\n toJpegUrl(ir: WorkflowIR): string {\n return toMermaidInkUrl(ir, \"img\", { ...options, imageType: \"jpeg\" });\n },\n\n toWebpUrl(ir: WorkflowIR): string {\n return toMermaidInkUrl(ir, \"img\", { ...options, imageType: \"webp\" });\n },\n\n toPdfUrl(ir: WorkflowIR): string {\n return toMermaidInkUrl(ir, \"pdf\", options);\n },\n\n getBaseUrl(): string {\n return baseUrl;\n },\n\n getOptions(): MermaidInkOptions {\n return { ...options };\n },\n };\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAmjBV,CCjqBO,SAASC,GAAqBC,EAK1B,CACT,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA2BHA,EAAQ,YAAc,yCAA2C,EAAE;AAAA;AAAA,MAEnEA,EAAQ,MAAQ,mBAAmBA,EAAQ,KAAK,MAAQ,EAAE;AAAA,MAC1DA,EAAQ,WAAa,qDAAuD,EAAE;AAAA,MAC9EA,EAAQ,QAAU,kBAAoB,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QA+jBtCA,EAAQ,WAAa,yFAA2F,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","names":["index_browser_exports","__export","asciiRenderer","buildMermaidInkUrl","combineEventHandlers","createEventCollector","createIRBuilder","createLiveVisualizer","createMermaidInkGenerator","createParallelDetector","createPerformanceAnalyzer","createTimeTravelController","createUrlGenerator","createVisualizer","defaultColorScheme","detectParallelGroups","encodeForMermaidInk","flowchartRenderer","getHeatLevel","hasChildren","htmlRenderer","isDecisionNode","isParallelNode","isRaceNode","isSequenceNode","isStepNode","isStreamNode","loggerRenderer","mermaidRenderer","renderToHTML","toKrokiPngUrl","toKrokiSvgUrl","toKrokiUrl","toMermaidInkJpegUrl","toMermaidInkPdfUrl","toMermaidInkPngUrl","toMermaidInkSvgUrl","toMermaidInkUrl","toMermaidInkWebpUrl","trackDecision","trackIf","trackSwitch","visualizeEvents","__toCommonJS","formatDuration","ms","minutes","seconds","generateId","hasRealScopeNodes","nodes","node","detectParallelGroups","options","minOverlapMs","maxGapMs","stepsWithTiming","nonStepNodes","i","a","b","groups","currentGroup","step","groupStart","s","groupEnd","startedTogether","hasTrueOverlap","overlapDuration","groupedNodes","group","position","children","startTs","endTs","parallelNode","deriveGroupState","originalIndex","g","c","createParallelDetector","createIRBuilder","options","detectParallel","parallelDetection","initialEnableSnapshots","maxSnapshots","enableSnapshots","defaultWorkflowId","generateId","workflowId","workflowStartTs","workflowEndTs","workflowState","workflowError","workflowDurationMs","activeSteps","scopeStack","decisionStack","activeStreams","currentNodes","createdAt","lastUpdatedAt","hookState","preStartHookWorkflowId","snapshots","eventIndex","getStepId","event","addNode","node","decision","branch","captureSnapshot","ir","getIR","activeStepsCopy","id","step","snapshot","handleEvent","active","hookExec","streamKey","stream","handleScopeEvent","scopeIndex","s","nestedScope","nestedNode","deriveState","scope","handleDecisionEvent","d","branchKey","existing","children","index","toRestore","branches","inferredBranchTaken","b","c","getCurrentNodes","nodes","detectParallelGroups","root","hasHooks","reset","getSnapshots","getSnapshotAt","getIRAt","clearSnapshots","enabled","import_awaitly","isStepNode","node","isSequenceNode","isParallelNode","isRaceNode","isDecisionNode","isStreamNode","hasChildren","RESET","BOLD","DIM","FG_RED","FG_GREEN","FG_YELLOW","FG_BLUE","FG_GRAY","FG_WHITE","colorize","text","color","bold","dim","defaultColorScheme","getStateSymbol","state","getColoredSymbol","colors","symbol","colorByState","stripAnsi","str","BOX","HEAT_COLORS","RESET","getHeatColor","heat","applyHeatColor","text","color","safeStringify","value","_key","v","n","getStringified","result","SPARK_CHARS","renderSparkline","values","width","subset","min","range","normalized","index","padEnd","str","visibleLen","stripAnsi","padding","horizontalLine","title","titleText","visibleTitleLen","remainingWidth","leftPad","rightPad","renderHookExecution","hook","label","colors","symbol","colorize","timing","dim","formatDuration","context","error","renderHooks","hooks","lines","asciiRenderer","ir","options","defaultColorScheme","innerWidth","workflowName","headerTitle","bold","hookLines","line","childLines","renderNodes","status","footer","colorByState","nodes","depth","node","isStepNode","renderStepNode","isParallelNode","renderParallelNode","isRaceNode","renderRaceNode","isDecisionNode","renderDecisionNode","isStreamNode","renderStreamNode","getColoredSymbol","name","enhanced","nameColored","inputStr","outputStr","timingStr","timingDisplay","history","timeoutInfo","hookKey","hookExec","hookSymbol","hookTiming","stateSymbol","counts","indent","mode","i","child","prefix","nestedLines","winnerSuffix","condition","decisionValue","branchTaken","branch","branchSymbol","branchColor","branchLabel","branchCondition","import_awaitly","flattenNodes","nodes","result","node","branch","percentile","sortedValues","p","index","getHeatLevel","heat","createPerformanceAnalyzer","timingData","retryData","errorData","timeoutData","currentRunEvents","getNodeId","event","processEvents","events","stepState","id","state","timings","retry","timeout","error","addRun","run","addEvent","finalizeRun","_runId","computePerformance","nodeId","sorted","a","b","mean","variance","acc","t","getNodePerformance","getHeatmap","ir","metric","allNodes","values","lookupKey","perf","value","vals","v","min","max","range","getAllPerformance","getSlowestNodes","limit","getErrorProneNodes","getRetryProneNodes","exportData","importData","json","data","k","clear","getStyleDefinitions","getHeatmapStyleDefinitions","getHeatClass","level","getHookStyleDefinitions","safeStringify","value","_key","v","n","getStringified","result","renderHooks","hooks","lines","options","lastHookId","hookId","state","icon","timing","formatDuration","context","nodeCounter","usedDecisionIds","usedStepIds","generateNodeId","prefix","resetNodeCounter","escapeMermaidText","text","escapeSubgraphName","mermaidRenderer","ir","enhanced","hookExitId","startId","prevNodeId","child","renderNode","endId","endIcon","endLabel","endShape","endClass","getStyleDefinitions","getHeatmapStyleDefinitions","node","isStepNode","renderStepNode","isParallelNode","renderParallelNode","isRaceNode","renderRaceNode","isDecisionNode","renderDecisionNode","isStreamNode","renderStreamNode","id","mermaidOpts","showRetryEdges","showErrorEdges","showTimeoutEdges","suffix","baseLabel","labelText","label","stateIcon","ioInfo","inputStr","outputStr","hookInfo","hookKey","hookExec","hookIcon","hookTiming","escapedLabel","nodeClass","heat","level","getHeatLevel","getHeatClass","shape","retryLabel","errorNodeId","errorLabel","timeoutNodeId","timeoutMs","subgraphId","forkId","joinId","name","modeLabel","note","childExitIds","exitId","stateClass","winnerExitId","isWinner","decisionId","condition","decisionValue","decisionLabel","branchExitIds","takenBranchExitId","usedBranchIds","branch","branchId","branchLabelText","branchLabel","branchClass","edgeLabel","prevId","counts","backpressure","CHARS","HEAT_COLORS","RESET","createCanvas","width","height","cells","colors","y","setChar","canvas","x","char","color","getChar","drawBox","i","j","drawText","text","chars","stripAnsi","drawVerticalLine","startY","endY","minY","maxY","existing","drawHorizontalLine","startX","endX","minX","maxX","drawArrow","canvasToString","lines","line","MIN_BOX_WIDTH","BOX_PADDING","VERTICAL_GAP","HORIZONTAL_GAP","wrapText","maxWidth","words","current","word","getSymbol","state","layoutWorkflow","ir","options","canvasWidth","showStartEnd","enhanced","centerX","nodes","currentY","startNode","createSimpleNode","child","layoutResult","layoutFlowNode","endLabel","endNode","id","type","label","maxLabelLen","node","isStepNode","layoutStepNode","isParallelNode","isRaceNode","layoutBranchingNode","isDecisionNode","layoutDecisionNode","isStreamNode","layoutStreamNode","fallback","name","symbol","mainLabel","innerWidth","formatDuration","history","renderSparkline","labelWidth","l","heat","lookupKey","layoutNode","_enhanced","isRace","headerLabel","result","childMaxWidth","childWidths","measured","measureFlowNode","totalChildWidth","a","b","useVerticalLayout","headerWidth","headerHeight","headerX","children","childX","childCenterX","childrenBottomY","c","totalBottomY","condition","labelLines","takenBranch","branchToRender","bottomY","lineCount","totalWidth","maxHeight","m","childHeight","renderNodes","defaultColorScheme","isLast","renderNode","nextNode","fromX","fromY","toX","toY","hasTopConnector","hasBottomConnector","getNodeColor","lineX","lineY","renderBranchingChildren","renderSequentialChildren","parent","forkY","forkX","firstChild","nextChild","childCenters","cx","childBottoms","maxChildBottom","joinY","STREAM_COLORS","level","getHeatLevel","flowchartRenderer","totalHeight","stripAnsi","str","collectSteps","nodes","steps","walk","nodeList","node","isStepNode","isSequenceNode","isParallelNode","isRaceNode","isDecisionNode","branch","stepToLog","step","log","calculateSummary","successCount","errorCount","cacheHits","skippedCount","totalRetries","slowestStep","hooksToLog","hooks","stepKey","hook","entry","buildLoggerOutput","ir","options","root","includeDiagram","stripColors","output","hookLog","diagram","flowchartRenderer","asciiRenderer","loggerRenderer","generateStyles","theme","lightColors","darkColors","generateThemeVars","colors","themeCSS","generateClientScript","options","NODE_WIDTH","NODE_HEIGHT","NODE_SPACING_H","NODE_SPACING_V","CONTAINER_PADDING","layoutWorkflow","nodes","direction","options","isVertical","isReversed","layoutNodes","currentX","currentY","maxWidth","maxHeight","node","result","layoutFlowNode","totalWidth","totalHeight","mirrorNode","child","x","y","_isVertical","isStepNode","baseName","name","isStreamNode","streamLabel","isParallelNode","isRaceNode","containerType","label","children","innerX","innerY","innerMaxWidth","innerMaxHeight","containerWidth","containerHeight","isDecisionNode","branch","renderLayoutNodeSVG","showTimings","renderContainerSVG","renderStepSVG","timing","formatDuration","escapeAttr","escapeXml","truncate","childrenSVG","c","findFlowNodeById","id","found","renderEdgesSVG","ir","edges","drawEdge","from","to","x1","y1","x2","y2","collectEdges","nodeList","i","decisionIR","idx","count","generateHTML","layout","svgWidth","svgHeight","nodesSVG","n","edgesSVG","workflowName","css","generateStyles","js","generateClientScript","safeJsonStringify","serializeIRForScript","str","maxLen","onAfterStep","obj","seen","key","value","defaultHTMLOptions","htmlRenderer","htmlOptions","renderToHTML","fullOptions","trackDecision","decisionId","options","condition","value","name","workflowId","emit","startTs","branchTaken","branches","takeBranch","label","taken","branchCondition","end","durationMs","trackIf","tracker","branchesEmitted","trackSwitch","caseValue","createTimeTravelController","options","maxSnapshots","autoRecord","builderOptions","builder","createIRBuilder","state","listeners","playbackTimer","notifyListeners","currentState","getState","listener","syncFromBuilder","handleEvent","event","seek","index","snapshots","stepForward","stepBackward","play","speed","playNext","currentSnapshots","current","scaledDelay","pause","getCurrentIR","getIRAt","getSnapshots","getSnapshotAt","onStateChange","callback","startRecording","stopRecording","reset","getBuilder","import_pako","hasBuffer","base64UrlEncode","bytes","base64","binary","i","encodeForKroki","text","textBytes","compressed","pako","base64UrlEncode","DEFAULT_KROKI_URL","buildKrokiUrl","diagramType","format","text","options","baseUrl","encoded","encodeForKroki","toKrokiUrl","ir","renderer","mermaidRenderer","renderOptions","defaultColorScheme","mermaidText","toKrokiSvgUrl","toKrokiPngUrl","createUrlGenerator","DEFAULT_MERMAID_INK_URL","encodeForMermaidInk","text","encodeForKroki","normalizeOptions","options","exportOpts","buildQueryString","format","params","buildMermaidInkUrl","normalized","baseUrl","encoded","queryString","toMermaidInkUrl","ir","renderer","mermaidRenderer","renderOptions","defaultColorScheme","mermaidText","toMermaidInkSvgUrl","toMermaidInkPngUrl","toMermaidInkJpegUrl","toMermaidInkWebpUrl","toMermaidInkPdfUrl","createMermaidInkGenerator","createLiveVisualizer","createVisualizer","options","workflowName","detectParallel","showTimings","showKeys","customColors","builder","createIRBuilder","updateCallbacks","ascii","asciiRenderer","mermaid","mermaidRenderer","logger","loggerRenderer","flowchart","flowchartRenderer","renderOptions","defaultColorScheme","notifyUpdate","ir","callback","handleEvent","event","handleScopeEvent","handleDecisionEvent","getIR","render","renderAs","format","toSerialize","_key","value","reset","onUpdate","combineEventHandlers","handlers","ctx","handler","visualizeEvents","events","viz","createEventCollector","e"]}