{"version":3,"sources":["../src/devtools-entry.ts","../src/utils/timing.ts","../src/parallel-detector.ts","../src/ir-builder/index.ts","../src/renderers/ascii.ts","../src/types.ts","../src/renderers/colors.ts","../src/renderers/mermaid.ts","../src/performance-analyzer.ts","../src/renderers/flowchart/index.ts","../src/renderers/logger.ts","../src/export/to-url.ts","../src/kroki/encoder.ts","../src/kroki/url.ts","../src/kroki/mermaid-ink.ts","../src/index.ts","../src/devtools.ts"],"sourcesContent":["/**\n * awaitly/devtools\n *\n * Debugging and development tools: timeline visualization, run comparison,\n * and console logging for workflow execution.\n *\n * @example\n * ```typescript\n * import { createDevtools, quickVisualize, createConsoleLogger } from 'awaitly/devtools';\n *\n * const devtools = createDevtools();\n * const workflow = createWorkflow(deps, {\n *   onEvent: devtools.handleEvent,\n * });\n *\n * await workflow.run(async ({ step }) => { ... });\n *\n * // View timeline\n * console.log(devtools.getTimeline());\n *\n * // Quick visualization\n * quickVisualize(events);\n * ```\n */\n\nexport {\n  // Types\n  type WorkflowRun,\n  type RunDiff,\n  type StepDiff,\n  type TimelineEntry,\n  type DevtoolsOptions,\n  type Devtools,\n\n  // Factory\n  createDevtools,\n\n  // Helpers\n  renderDiff,\n  quickVisualize,\n  createConsoleLogger,\n} from \"./devtools\";","/**\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\";\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  metadata?: import(\"awaitly\").StepMetadata;\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   * With the current awaitly API (ID-first for all step types), stepId is always set.\n   * Falls back to stepKey, then name, then a generated 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          metadata: (event as any).metadata,\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            ...(active.metadata && { metadata: active.metadata }),\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            ...(active.metadata && { metadata: active.metadata }),\n            ...((event as any).diagnostics && { errorDiagnostics: (event as any).diagnostics }),\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            ...(active.metadata && { metadata: active.metadata }),\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          ...((event as any).metadata && { metadata: (event as any).metadata }),\n        };\n        addNode(node);\n        break;\n      }\n\n      // Core decision event (emitted automatically by step.if / step.branch).\n      case \"decision\": {\n        const e = event as unknown as {\n          decisionId: string;\n          label?: string;\n          branch: string;\n          value: unknown;\n          phase?: \"start\" | \"end\";\n          durationMs?: number;\n          ts: number;\n        };\n\n        // step.branch owns arm execution and emits a scoped decision\n        // (phase start/end). Route it through the decision machinery so the\n        // arm's steps nest inside the taken branch, exactly like trackIf.\n        if (e.phase === \"start\") {\n          handleDecisionEvent({\n            type: \"decision_start\",\n            workflowId: (event as { workflowId: string }).workflowId,\n            decisionId: e.decisionId,\n            name: e.label ?? e.decisionId,\n            condition: e.label,\n            decisionValue: e.value,\n            ts: e.ts,\n          });\n          handleDecisionEvent({\n            type: \"decision_branch\",\n            workflowId: (event as { workflowId: string }).workflowId,\n            decisionId: e.decisionId,\n            branchLabel: \"then\",\n            condition: e.label,\n            taken: e.branch === \"then\",\n            ts: e.ts,\n          });\n          handleDecisionEvent({\n            type: \"decision_branch\",\n            workflowId: (event as { workflowId: string }).workflowId,\n            decisionId: e.decisionId,\n            branchLabel: \"else\",\n            taken: e.branch === \"else\",\n            ts: e.ts,\n          });\n          break;\n        }\n        if (e.phase === \"end\") {\n          handleDecisionEvent({\n            type: \"decision_end\",\n            workflowId: (event as { workflowId: string }).workflowId,\n            decisionId: e.decisionId,\n            branchTaken: e.branch,\n            ts: e.ts,\n            durationMs: e.durationMs ?? 0,\n          });\n          break;\n        }\n\n        // step.if is instantaneous — core cannot see the extent of a raw\n        // `if` block, so this is a decision marker: the branch taken is\n        // recorded, but steps executed inside the branch appear as siblings\n        // (the static graph carries the nesting).\n        const node: DecisionNode = {\n          type: \"decision\",\n          id: e.decisionId,\n          name: e.label ?? e.decisionId,\n          state: \"success\",\n          startTs: e.ts,\n          endTs: e.ts,\n          durationMs: 0,\n          condition: e.label,\n          decisionValue: e.value,\n          branchTaken: e.branch,\n          branches: [\n            { label: \"then\", condition: e.label, taken: e.branch === \"then\", children: [] },\n            { label: \"else\", taken: e.branch === \"else\", children: [] },\n          ],\n        };\n        addNode(node);\n        lastUpdatedAt = Date.now();\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  /** Agent metadata from step options (domain, intent, owner, etc.) */\n  metadata?: import(\"awaitly\").StepMetadata;\n  /** Error diagnostics (tag, classification, origin) */\n  errorDiagnostics?: import(\"awaitly\").StepErrorDiagnostics;\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\";\nimport type { ScopeType, WorkflowEvent, WorkflowOptions } from \"awaitly\";\nimport type { UnexpectedError } from \"awaitly\";\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\";\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 (for grouping and metrics).\n   * Prioritizes stepKey (cache/instance identity), then stepId (step label), then name\n   * so distinct steps (e.g. same id in a loop, different key) are not merged.\n   * With the current awaitly API, stepId is always set; stepKey is set when key option is used.\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  // Agent metadata\n  domain?: string;\n  owner?: string;\n  intent?: string;\n  calls?: readonly string[];\n  // Error diagnostics summary\n  errorDiagnostics?: {\n    tag: string;\n    severity?: string;\n    retryable?: boolean;\n    origin?: string;\n  };\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  byDomain?: Record<string, { total: number; errors: number; avgDurationMs: 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  // Add metadata fields\n  if (step.metadata) {\n    if (step.metadata.domain) log.domain = step.metadata.domain;\n    if (step.metadata.owner) log.owner = step.metadata.owner;\n    if (step.metadata.intent) log.intent = step.metadata.intent;\n    if (step.metadata.calls?.length) log.calls = step.metadata.calls;\n  }\n\n  // Add error diagnostics summary\n  if (step.errorDiagnostics) {\n    log.errorDiagnostics = {\n      tag: step.errorDiagnostics.tag,\n      origin: step.errorDiagnostics.origin,\n    };\n    if (step.errorDiagnostics.classification?.severity) {\n      log.errorDiagnostics.severity = step.errorDiagnostics.classification.severity;\n    }\n    if (step.errorDiagnostics.classification?.retryable !== undefined) {\n      log.errorDiagnostics.retryable = step.errorDiagnostics.classification.retryable;\n    }\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  // Calculate byDomain grouping\n  const domainMap = new Map<string, { total: number; errors: number; totalDuration: number }>();\n  for (const step of steps) {\n    const domain = step.metadata?.domain;\n    if (domain && (step.state === \"success\" || step.state === \"error\")) {\n      const entry = domainMap.get(domain) ?? { total: 0, errors: 0, totalDuration: 0 };\n      entry.total++;\n      if (step.state === \"error\") entry.errors++;\n      if (step.durationMs !== undefined) entry.totalDuration += step.durationMs;\n      domainMap.set(domain, entry);\n    }\n  }\n\n  let byDomain: Record<string, { total: number; errors: number; avgDurationMs: number }> | undefined;\n  if (domainMap.size > 0) {\n    byDomain = {};\n    for (const [domain, entry] of domainMap) {\n      byDomain[domain] = {\n        total: entry.total,\n        errors: entry.errors,\n        avgDurationMs: entry.total > 0 ? Math.round(entry.totalDuration / entry.total) : 0,\n      };\n    }\n  }\n\n  return {\n    totalSteps: steps.length,\n    successCount,\n    errorCount,\n    cacheHits,\n    skippedCount,\n    totalRetries,\n    slowestStep,\n    ...(byDomain && { byDomain }),\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 * Unified Export URL Generation\n *\n * Generates export URLs from diagram sources using configured providers.\n * Decoupled from IR - takes already-rendered diagram text.\n */\n\nimport { ok, err, type Result } from \"awaitly\";\nimport { buildKrokiUrl } from \"../kroki/url\";\nimport { buildMermaidInkUrl } from \"../kroki/mermaid-ink\";\nimport type {\n  DiagramSource,\n  ExportFormat,\n  ExportOptions,\n  KrokiExportOptions,\n  MermaidInkExportOptions,\n} from \"../types\";\n\n/**\n * Error types for export URL generation.\n */\nexport type ExportUrlError =\n  | \"UNSUPPORTED_DIAGRAM_KIND\"\n  | \"UNSUPPORTED_FORMAT\"\n  | \"UNKNOWN_PROVIDER\";\n\n/**\n * Internal context for export operations.\n * Used to provide caller context in error messages.\n */\ninterface ExportContext {\n  /** The calling method name (e.g., \"toSvgUrl\") */\n  caller?: string;\n}\n\n/**\n * Validate that the provider supports the requested format for the diagram kind.\n */\nfunction validateFormatSupported(\n  provider: ExportOptions[\"provider\"],\n  diagramKind: DiagramSource[\"kind\"],\n  format: ExportFormat\n): Result<void, ExportUrlError> {\n  // mermaid-ink supports all formats for mermaid diagrams\n  if (provider === \"mermaid-ink\" && diagramKind === \"mermaid\") return ok(undefined);\n\n  // Kroki supports svg/png for mermaid diagrams (not PDF)\n  if (provider === \"kroki\" && diagramKind === \"mermaid\") {\n    if (format === \"pdf\") {\n      return err(\"UNSUPPORTED_FORMAT\");\n    }\n    return ok(undefined); // svg/png are supported\n  }\n\n  // Future-proof default: reject unsupported combinations\n  return err(\"UNSUPPORTED_FORMAT\");\n}\n\n/**\n * Map internal diagram kind to Kroki's diagramType param.\n * Explicit map prevents breakage if internal names diverge from Kroki's API.\n */\nfunction toKrokiDiagramType(\n  kind: DiagramSource[\"kind\"]\n): \"mermaid\" | \"graphviz\" | \"plantuml\" {\n  switch (kind) {\n    case \"mermaid\":\n      return \"mermaid\";\n    case \"graphviz\":\n      return \"graphviz\";\n    case \"plantuml\":\n      return \"plantuml\";\n  }\n}\n\n/**\n * Map ExportFormat to mermaid.ink format.\n * mermaid.ink uses \"img\" for PNG, not \"png\".\n */\nfunction toMermaidInkFormat(format: ExportFormat): \"svg\" | \"img\" | \"pdf\" {\n  switch (format) {\n    case \"svg\":\n      return \"svg\";\n    case \"png\":\n      return \"img\";\n    case \"pdf\":\n      return \"pdf\";\n  }\n}\n\n/**\n * Generate export URL from diagram source.\n * Decoupled from IR - takes already-rendered diagram text.\n *\n * @param diagram - The diagram source (kind + text)\n * @param format - Export format (svg, png, pdf)\n * @param options - Provider-specific options\n * @param ctx - Optional context for error messages\n * @returns Result with export URL or ExportUrlError\n *\n * @example\n * ```typescript\n * const result = toExportUrl(\n *   { kind: \"mermaid\", source: \"flowchart TD\\n  A-->B\" },\n *   \"svg\",\n *   { provider: \"kroki\" }\n * );\n * if (result.ok) {\n *   console.log(result.value);\n * }\n * ```\n */\nexport function toExportUrl(\n  diagram: DiagramSource,\n  format: ExportFormat,\n  options: ExportOptions,\n  ctx: ExportContext = {}\n): Result<string, ExportUrlError> {\n  // Validate diagram kind (only mermaid supported currently)\n  switch (diagram.kind) {\n    case \"mermaid\":\n      break;\n    case \"graphviz\":\n    case \"plantuml\":\n      return err(\"UNSUPPORTED_DIAGRAM_KIND\");\n    default: {\n      const _exhaustive: never = diagram;\n      return err(\"UNSUPPORTED_DIAGRAM_KIND\");\n    }\n  }\n\n  // Validate format is supported by provider + diagram kind\n  const formatResult = validateFormatSupported(options.provider, diagram.kind, format);\n  if (!formatResult.ok) {\n    return formatResult;\n  }\n\n  // Generate URL via provider-specific helper (explicit option types)\n  switch (options.provider) {\n    case \"kroki\":\n      return ok(buildKrokiUrl(\n        toKrokiDiagramType(diagram.kind),\n        format as \"svg\" | \"png\", // PDF already rejected above\n        diagram.source,\n        options as KrokiExportOptions\n      ));\n    case \"mermaid-ink\":\n      return ok(buildMermaidInkUrl(\n        toMermaidInkFormat(format),\n        diagram.source,\n        options as MermaidInkExportOptions\n      ));\n    default: {\n      const _exhaustive: never = options;\n      return err(\"UNKNOWN_PROVIDER\");\n    }\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","/**\n * Workflow Visualization Module\n *\n * Provides tools for visualizing workflow execution with color-coded\n * step states and support for parallel/race operations.\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.run(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\";\nimport type { UnexpectedError } from \"awaitly\";\nimport type {\n  OutputFormat,\n  RenderOptions,\n  ScopeEndEvent,\n  ScopeStartEvent,\n  DecisionStartEvent,\n  DecisionBranchEvent,\n  DecisionEndEvent,\n  VisualizerOptions,\n  WorkflowIR,\n  ExportFormat,\n  ExportOptions,\n  DiagramSource,\n} from \"./types\";\nimport { createIRBuilder } from \"./ir-builder\";\nimport { asciiRenderer, mermaidRenderer, loggerRenderer, flowchartRenderer, defaultColorScheme } from \"./renderers\";\nimport { toExportUrl } from \"./export/to-url\";\n\n// =============================================================================\n// Re-exports\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 { createLiveVisualizer, type LiveVisualizer } from \"./live-visualizer\";\nexport { devEvents } from \"./dev-events\";\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 + Node 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 + Node 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// Export URL generation\nexport { toExportUrl } from \"./export/to-url\";\n\n// Interactive Mermaid CDN HTML generation\nexport {\n  generateInteractiveHTML,\n  escapeHtml,\n  type NodeMetadata,\n  type WorkflowMetadata,\n  type InteractiveHTMLOptions,\n  type MermaidHTMLSourceLocation,\n  type MermaidHTMLRetryConfig,\n  type MermaidHTMLTimeoutConfig,\n} from \"./mermaid-html\";\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   * Generate export URL for the current workflow diagram.\n   * Requires explicit provider unless export.default is configured.\n   *\n   * @param format - Export format (svg, png, pdf)\n   * @param options - Provider options (required unless default configured)\n   * @returns The export URL\n   * @throws If no provider configured and none passed\n   * @throws If format not supported by provider\n   */\n  toUrl: (format: ExportFormat, options?: ExportOptions) => string;\n\n  /**\n   * Generate SVG export URL for the current workflow diagram.\n   * Requires explicit provider unless export.default is configured.\n   *\n   * @param options - Provider options (required unless default configured)\n   * @returns The SVG export URL\n   * @throws If no provider configured and none passed\n   */\n  toSvgUrl: (options?: ExportOptions) => string;\n\n  /**\n   * Generate PNG export URL for the current workflow diagram.\n   * Requires explicit provider unless export.default is configured.\n   *\n   * @param options - Provider options (required unless default configured)\n   * @returns The PNG export URL\n   * @throws If no provider configured and none passed\n   */\n  toPngUrl: (options?: ExportOptions) => string;\n\n  /**\n   * Generate PDF export URL for the current workflow diagram.\n   * Requires explicit provider unless export.default is configured.\n   * Note: Kroki does not support PDF for mermaid diagrams.\n   *\n   * @param options - Provider options (required unless default configured)\n   * @returns The PDF export URL\n   * @throws If no provider configured and none passed\n   * @throws If provider doesn't support PDF (e.g., Kroki for mermaid)\n   */\n  toPdfUrl: (options?: ExportOptions) => string;\n}\n\n// =============================================================================\n// Create Visualizer\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.run(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    export: exportConfig,\n  } = options;\n\n  const builder = createIRBuilder({ detectParallel });\n  const updateCallbacks: Set<(ir: WorkflowIR) => void> = new Set();\n  let nameFromEvent: string | undefined;\n\n  // Renderers\n  const ascii = asciiRenderer();\n  const mermaid = mermaidRenderer();\n  const logger = loggerRenderer();\n  const flowchart = flowchartRenderer();\n\n  // Build render options\n  const renderOptions: RenderOptions = {\n    showTimings,\n    showKeys,\n    terminalWidth: process.stdout?.columns ?? 80,\n    colors: { ...defaultColorScheme, ...customColors },\n  };\n\n  function notifyUpdate(): void {\n    if (updateCallbacks.size > 0) {\n      const ir = 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    if (\"workflowName\" in event && typeof (event as { workflowName?: string }).workflowName === \"string\") {\n      nameFromEvent = (event as { workflowName: string }).workflowName;\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    const name = workflowName ?? nameFromEvent ?? ir.root.name;\n    if (name) {\n      ir.root.name = name;\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        // Convert Map (hooks.onAfterStep) to plain object so JSON.stringify serializes it; accept plain object (e.g. from 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        // Replacer: BigInt and other non-JSON values (e.g. decisionValue) so output is robust\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  // ==========================================================================\n  // Export URL Methods\n  // ==========================================================================\n\n  function resolveExportOptions(\n    opts: ExportOptions | undefined,\n    methodName: string\n  ): ExportOptions {\n    if (opts) return opts;\n    if (exportConfig?.default) return exportConfig.default;\n    throw new Error(\n      `${methodName}(): No export provider configured. ` +\n        `Pass { provider: 'kroki' } or { provider: 'mermaid-ink' }, ` +\n        `or set export.default in createVisualizer().`\n    );\n  }\n\n  function getDiagramSource(): DiagramSource {\n    const ir = getIR();\n    const source = mermaid.render(ir, renderOptions);\n    return { kind: \"mermaid\", source };\n  }\n\n  function toSvgUrl(opts?: ExportOptions): string {\n    const result = toExportUrl(\n      getDiagramSource(),\n      \"svg\",\n      resolveExportOptions(opts, \"toSvgUrl\"),\n      { caller: \"toSvgUrl\" }\n    );\n    if (!result.ok) {\n      throw new Error(`toSvgUrl: Export failed - ${result.error}`);\n    }\n    return result.value;\n  }\n\n  function toPngUrl(opts?: ExportOptions): string {\n    const result = toExportUrl(\n      getDiagramSource(),\n      \"png\",\n      resolveExportOptions(opts, \"toPngUrl\"),\n      { caller: \"toPngUrl\" }\n    );\n    if (!result.ok) {\n      throw new Error(`toPngUrl: Export failed - ${result.error}`);\n    }\n    return result.value;\n  }\n\n  function toPdfUrl(opts?: ExportOptions): string {\n    const result = toExportUrl(\n      getDiagramSource(),\n      \"pdf\",\n      resolveExportOptions(opts, \"toPdfUrl\"),\n      { caller: \"toPdfUrl\" }\n    );\n    if (!result.ok) {\n      throw new Error(`toPdfUrl: Export failed - ${result.error}`);\n    }\n    return result.value;\n  }\n\n  function toUrl(format: ExportFormat, opts?: ExportOptions): string {\n    switch (format) {\n      case \"svg\":\n        return toSvgUrl(opts);\n      case \"png\":\n        return toPngUrl(opts);\n      case \"pdf\":\n        return toPdfUrl(opts);\n      default: {\n        const _exhaustive: never = format;\n        return _exhaustive;\n      }\n    }\n  }\n\n  return {\n    handleEvent,\n    handleScopeEvent,\n    handleDecisionEvent,\n    getIR,\n    render,\n    renderAs,\n    reset,\n    onUpdate,\n    toUrl,\n    toSvgUrl,\n    toPngUrl,\n    toPdfUrl,\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.run(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.run(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 * awaitly/devtools\n *\n * Developer tools for workflow debugging, visualization, and analysis.\n * Provides timeline rendering, run diffing, and live visualization.\n */\n\nimport type { WorkflowEvent } from \"awaitly\";\nimport type {\n  OutputFormat,\n  VisualizerOptions,\n  DecisionStartEvent,\n  DecisionBranchEvent,\n  DecisionEndEvent,\n} from \"./types\";\nimport type { CollectableEvent } from \"./index\";\nimport { createVisualizer } from \"./index\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * A recorded workflow run with events and metadata.\n */\nexport interface WorkflowRun {\n  /** Unique identifier for this run */\n  id: string;\n  /** Workflow name */\n  name?: string;\n  /** Start timestamp */\n  startTime: number;\n  /** End timestamp (undefined if still running) */\n  endTime?: number;\n  /** Duration in milliseconds */\n  durationMs?: number;\n  /** Whether the workflow succeeded */\n  success?: boolean;\n  /** Error if the workflow failed */\n  error?: unknown;\n  /** All events from this run */\n  events: CollectableEvent[];\n  /** Custom metadata */\n  metadata?: Record<string, unknown>;\n}\n\n/**\n * Difference between two workflow runs.\n */\nexport interface RunDiff {\n  /** Steps that were added in the new run */\n  added: StepDiff[];\n  /** Steps that were removed from the new run */\n  removed: StepDiff[];\n  /** Steps that changed between runs */\n  changed: StepDiff[];\n  /** Steps that are identical */\n  unchanged: string[];\n  /** Overall status change */\n  statusChange?: {\n    from: \"success\" | \"error\" | \"running\";\n    to: \"success\" | \"error\" | \"running\";\n  };\n  /** Duration change in milliseconds */\n  durationChange?: number;\n}\n\n/**\n * Information about a step difference.\n */\nexport interface StepDiff {\n  /** Step name or key */\n  step: string;\n  /** Type of change */\n  type: \"added\" | \"removed\" | \"status\" | \"duration\" | \"error\";\n  /** Previous value (for changes) */\n  from?: unknown;\n  /** New value (for changes) */\n  to?: unknown;\n}\n\n/**\n * Timeline entry for a step.\n */\nexport interface TimelineEntry {\n  /** Step name */\n  name: string;\n  /** Step key (if any) */\n  key?: string;\n  /** Start time (relative to workflow start) */\n  startMs: number;\n  /** End time (relative to workflow start) */\n  endMs?: number;\n  /** Duration in milliseconds */\n  durationMs?: number;\n  /** Step status */\n  status: \"pending\" | \"running\" | \"success\" | \"error\" | \"skipped\" | \"cached\";\n  /** Error if failed */\n  error?: unknown;\n  /** Parent scope (for nested steps) */\n  parent?: string;\n  /** Retry attempt number */\n  attempt?: number;\n}\n\n/**\n * Devtools configuration options.\n */\nexport interface DevtoolsOptions extends VisualizerOptions {\n  /** Enable console logging of events */\n  logEvents?: boolean;\n  /** Maximum number of runs to keep in history */\n  maxHistory?: number;\n  /** Custom logger function */\n  logger?: (message: string) => void;\n}\n\n// =============================================================================\n// Devtools Interface\n// =============================================================================\n\n/**\n * Devtools instance for workflow debugging.\n */\nexport interface Devtools {\n  /** Handle a workflow event */\n  handleEvent: (event: WorkflowEvent<unknown>) => void;\n\n  /** Handle a decision event */\n  handleDecisionEvent: (event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent) => void;\n\n  /** Get the current run */\n  getCurrentRun: () => WorkflowRun | undefined;\n\n  /** Get run history */\n  getHistory: () => WorkflowRun[];\n\n  /** Get a specific run by ID */\n  getRun: (id: string) => WorkflowRun | undefined;\n\n  /** Compare two runs */\n  diff: (runId1: string, runId2: string) => RunDiff | undefined;\n\n  /** Compare current run with a previous run */\n  diffWithPrevious: () => RunDiff | undefined;\n\n  /** Render current state */\n  render: () => string;\n\n  /** Render to a specific format */\n  renderAs: (format: OutputFormat) => string;\n\n  /** Render as Mermaid diagram */\n  renderMermaid: () => string;\n\n  /** Render as ASCII timeline */\n  renderTimeline: () => string;\n\n  /** Get timeline data for current run */\n  getTimeline: () => TimelineEntry[];\n\n  /** Clear all history */\n  clearHistory: () => void;\n\n  /** Reset current run */\n  reset: () => void;\n\n  /** Export run data as JSON */\n  exportRun: (runId?: string) => string;\n\n  /** Import run data from JSON */\n  importRun: (json: string) => WorkflowRun;\n}\n\n// =============================================================================\n// Create Devtools\n// =============================================================================\n\n/**\n * Create a devtools instance for workflow debugging.\n *\n * @example\n * ```typescript\n * const devtools = createDevtools({ workflowName: 'checkout' });\n *\n * const workflow = createWorkflow(deps, {\n *   onEvent: devtools.handleEvent,\n * });\n *\n * await workflow.run(async ({ step }) => { ... });\n *\n * // Visualize\n * console.log(devtools.render());\n * console.log(devtools.renderMermaid());\n *\n * // Compare with previous run\n * const diff = devtools.diffWithPrevious();\n * ```\n */\nexport function createDevtools(options: DevtoolsOptions = {}): Devtools {\n  const { logEvents = false, maxHistory = 10, logger = console.log } = options;\n\n  const visualizer = createVisualizer(options);\n  const history: WorkflowRun[] = [];\n  let currentRun: WorkflowRun | undefined;\n  let workflowStartTime = 0;\n\n  function startNewRun(workflowId: string): void {\n    // Save current run to history if it exists\n    if (currentRun) {\n      history.push(currentRun);\n      // Trim history if needed\n      while (history.length > maxHistory) {\n        history.shift();\n      }\n    }\n\n    workflowStartTime = Date.now();\n    currentRun = {\n      id: workflowId,\n      name: options.workflowName,\n      startTime: workflowStartTime,\n      events: [],\n    };\n\n    visualizer.reset();\n  }\n\n  function endCurrentRun(success: boolean, error?: unknown): void {\n    if (currentRun) {\n      currentRun.endTime = Date.now();\n      currentRun.durationMs = currentRun.endTime - currentRun.startTime;\n      currentRun.success = success;\n      currentRun.error = error;\n    }\n  }\n\n  function handleEvent(event: WorkflowEvent<unknown>): void {\n    if (logEvents) {\n      logger(`[devtools] ${event.type}: ${JSON.stringify(event)}`);\n    }\n\n    // Start new run on workflow_start\n    if (event.type === \"workflow_start\") {\n      startNewRun(event.workflowId);\n    }\n\n    // Record event\n    if (currentRun) {\n      currentRun.events.push(event);\n    }\n\n    // Forward to visualizer\n    visualizer.handleEvent(event);\n\n    // End run on workflow_success or workflow_error\n    if (event.type === \"workflow_success\") {\n      endCurrentRun(true);\n    } else if (event.type === \"workflow_error\") {\n      endCurrentRun(false, event.error);\n    }\n  }\n\n  function handleDecisionEvent(\n    event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent\n  ): void {\n    if (logEvents) {\n      logger(`[devtools] ${event.type}: ${JSON.stringify(event)}`);\n    }\n\n    if (currentRun) {\n      currentRun.events.push(event);\n    }\n\n    visualizer.handleDecisionEvent(event);\n  }\n\n  function getCurrentRun(): WorkflowRun | undefined {\n    return currentRun;\n  }\n\n  function getHistory(): WorkflowRun[] {\n    return [...history];\n  }\n\n  function getRun(id: string): WorkflowRun | undefined {\n    if (currentRun?.id === id) return currentRun;\n    return history.find((run) => run.id === id);\n  }\n\n  function diff(runId1: string, runId2: string): RunDiff | undefined {\n    const run1 = getRun(runId1);\n    const run2 = getRun(runId2);\n\n    if (!run1 || !run2) return undefined;\n\n    return diffRuns(run1, run2);\n  }\n\n  function diffWithPrevious(): RunDiff | undefined {\n    if (!currentRun || history.length === 0) return undefined;\n    const previousRun = history[history.length - 1];\n    return diffRuns(previousRun, currentRun);\n  }\n\n  function render(): string {\n    return visualizer.render();\n  }\n\n  function renderAs(format: OutputFormat): string {\n    return visualizer.renderAs(format);\n  }\n\n  function renderMermaid(): string {\n    return visualizer.renderAs(\"mermaid\");\n  }\n\n  function renderTimeline(): string {\n    const timeline = getTimeline();\n    return formatTimeline(timeline);\n  }\n\n  function getTimeline(): TimelineEntry[] {\n    if (!currentRun) return [];\n    return buildTimeline(currentRun.events, workflowStartTime);\n  }\n\n  function clearHistory(): void {\n    history.length = 0;\n  }\n\n  function reset(): void {\n    currentRun = undefined;\n    visualizer.reset();\n  }\n\n  function exportRun(runId?: string): string {\n    const run = runId ? getRun(runId) : currentRun;\n    if (!run) return \"{}\";\n    return JSON.stringify(run, null, 2);\n  }\n\n  function importRun(json: string): WorkflowRun {\n    const run = JSON.parse(json) as WorkflowRun;\n    history.push(run);\n    return run;\n  }\n\n  return {\n    handleEvent,\n    handleDecisionEvent,\n    getCurrentRun,\n    getHistory,\n    getRun,\n    diff,\n    diffWithPrevious,\n    render,\n    renderAs,\n    renderMermaid,\n    renderTimeline,\n    getTimeline,\n    clearHistory,\n    reset,\n    exportRun,\n    importRun,\n  };\n}\n\n// =============================================================================\n// Diff Helpers\n// =============================================================================\n\nfunction diffRuns(run1: WorkflowRun, run2: WorkflowRun): RunDiff {\n  const steps1 = extractSteps(run1.events);\n  const steps2 = extractSteps(run2.events);\n\n  const added: StepDiff[] = [];\n  const removed: StepDiff[] = [];\n  const changed: StepDiff[] = [];\n  const unchanged: string[] = [];\n\n  // Find added and changed steps\n  for (const [name, step2] of steps2) {\n    const step1 = steps1.get(name);\n\n    if (!step1) {\n      added.push({ step: name, type: \"added\", to: step2.status });\n    } else if (step1.status !== step2.status) {\n      changed.push({\n        step: name,\n        type: \"status\",\n        from: step1.status,\n        to: step2.status,\n      });\n    } else if (step1.durationMs !== step2.durationMs) {\n      changed.push({\n        step: name,\n        type: \"duration\",\n        from: step1.durationMs,\n        to: step2.durationMs,\n      });\n    } else {\n      unchanged.push(name);\n    }\n  }\n\n  // Find removed steps\n  for (const [name] of steps1) {\n    if (!steps2.has(name)) {\n      removed.push({ step: name, type: \"removed\", from: steps1.get(name)?.status });\n    }\n  }\n\n  // Calculate status change\n  let statusChange: RunDiff[\"statusChange\"];\n  const status1 = run1.success === undefined ? \"running\" : run1.success ? \"success\" : \"error\";\n  const status2 = run2.success === undefined ? \"running\" : run2.success ? \"success\" : \"error\";\n\n  if (status1 !== status2) {\n    statusChange = { from: status1, to: status2 };\n  }\n\n  // Calculate duration change\n  let durationChange: number | undefined;\n  if (run1.durationMs !== undefined && run2.durationMs !== undefined) {\n    durationChange = run2.durationMs - run1.durationMs;\n  }\n\n  return {\n    added,\n    removed,\n    changed,\n    unchanged,\n    statusChange,\n    durationChange,\n  };\n}\n\ninterface StepInfo {\n  name: string;\n  key?: string;\n  status: string;\n  durationMs?: number;\n  error?: unknown;\n}\n\nfunction extractSteps(events: CollectableEvent[]): Map<string, StepInfo> {\n  const steps = new Map<string, StepInfo>();\n\n  for (const event of events) {\n    if (event.type === \"step_start\") {\n      const e = event as WorkflowEvent<unknown> & { stepId: string; name?: string; stepKey?: string };\n      const name = e.name || e.stepKey || e.stepId;\n      steps.set(name, {\n        name,\n        key: e.stepKey,\n        status: \"running\",\n      });\n    } else if (event.type === \"step_success\") {\n      const e = event as WorkflowEvent<unknown> & { stepId: string; name?: string; stepKey?: string; durationMs: number };\n      const name = e.name || e.stepKey || e.stepId;\n      const existing = steps.get(name);\n      if (existing) {\n        existing.status = \"success\";\n        existing.durationMs = e.durationMs;\n      }\n    } else if (event.type === \"step_error\") {\n      const e = event as WorkflowEvent<unknown> & { stepId: string; name?: string; stepKey?: string; durationMs: number; error: unknown };\n      const name = e.name || e.stepKey || e.stepId;\n      const existing = steps.get(name);\n      if (existing) {\n        existing.status = \"error\";\n        existing.durationMs = e.durationMs;\n        existing.error = e.error;\n      }\n    } else if (event.type === \"step_cache_hit\") {\n      const e = event as WorkflowEvent<unknown> & { stepKey: string; name?: string };\n      const name = e.name || e.stepKey;\n      steps.set(name, {\n        name,\n        key: e.stepKey,\n        status: \"cached\",\n      });\n    } else if (event.type === \"step_skipped\") {\n      const e = event as WorkflowEvent<unknown> & { stepKey?: string; name?: string };\n      const name = e.name || e.stepKey || \"unknown\";\n      steps.set(name, {\n        name,\n        key: e.stepKey,\n        status: \"skipped\",\n      });\n    }\n  }\n\n  return steps;\n}\n\n// =============================================================================\n// Timeline Helpers\n// =============================================================================\n\nfunction buildTimeline(events: CollectableEvent[], startTime: number): TimelineEntry[] {\n  const timeline: TimelineEntry[] = [];\n  const stepStarts = new Map<string, number>();\n\n  for (const event of events) {\n    if (event.type === \"step_start\") {\n      const e = event as WorkflowEvent<unknown> & { stepId: string; name?: string; stepKey?: string; ts: number };\n      const name = e.name || e.stepKey || e.stepId;\n      stepStarts.set(name, e.ts);\n      timeline.push({\n        name,\n        key: e.stepKey,\n        startMs: e.ts - startTime,\n        status: \"running\",\n      });\n    } else if (event.type === \"step_success\") {\n      const e = event as WorkflowEvent<unknown> & { stepId: string; name?: string; stepKey?: string; ts: number; durationMs: number };\n      const name = e.name || e.stepKey || e.stepId;\n      const entry = timeline.find((t) => t.name === name && t.status === \"running\");\n      if (entry) {\n        entry.endMs = e.ts - startTime;\n        entry.durationMs = e.durationMs;\n        entry.status = \"success\";\n      }\n    } else if (event.type === \"step_error\") {\n      const e = event as WorkflowEvent<unknown> & { stepId: string; name?: string; stepKey?: string; ts: number; durationMs: number; error: unknown };\n      const name = e.name || e.stepKey || e.stepId;\n      const entry = timeline.find((t) => t.name === name && t.status === \"running\");\n      if (entry) {\n        entry.endMs = e.ts - startTime;\n        entry.durationMs = e.durationMs;\n        entry.status = \"error\";\n        entry.error = e.error;\n      }\n    } else if (event.type === \"step_cache_hit\") {\n      const e = event as WorkflowEvent<unknown> & { stepKey: string; name?: string; ts: number };\n      const name = e.name || e.stepKey;\n      timeline.push({\n        name,\n        key: e.stepKey,\n        startMs: e.ts - startTime,\n        endMs: e.ts - startTime,\n        durationMs: 0,\n        status: \"cached\",\n      });\n    } else if (event.type === \"step_skipped\") {\n      const e = event as WorkflowEvent<unknown> & { stepKey?: string; name?: string; ts: number };\n      const name = e.name || e.stepKey || \"unknown\";\n      timeline.push({\n        name,\n        key: e.stepKey,\n        startMs: e.ts - startTime,\n        endMs: e.ts - startTime,\n        durationMs: 0,\n        status: \"skipped\",\n      });\n    }\n  }\n\n  return timeline;\n}\n\nfunction formatTimeline(timeline: TimelineEntry[]): string {\n  if (timeline.length === 0) return \"No timeline data\";\n\n  const lines: string[] = [];\n  lines.push(\"Timeline:\");\n  lines.push(\"─\".repeat(60));\n\n  // Find max duration for scaling\n  const maxEnd = Math.max(...timeline.map((t) => t.endMs ?? t.startMs + 100));\n  const barWidth = 40;\n\n  for (const entry of timeline) {\n    const startPos = Math.floor((entry.startMs / maxEnd) * barWidth);\n    const endPos = Math.floor(((entry.endMs ?? entry.startMs + 10) / maxEnd) * barWidth);\n    const width = Math.max(1, endPos - startPos);\n\n    const statusChar = getStatusChar(entry.status);\n    const bar = \" \".repeat(startPos) + statusChar.repeat(width);\n\n    const duration = entry.durationMs !== undefined ? `${entry.durationMs}ms` : \"?\";\n    lines.push(`${entry.name.padEnd(20)} |${bar.padEnd(barWidth)}| ${duration}`);\n  }\n\n  lines.push(\"─\".repeat(60));\n  return lines.join(\"\\n\");\n}\n\nfunction getStatusChar(status: TimelineEntry[\"status\"]): string {\n  switch (status) {\n    case \"success\":\n      return \"█\";\n    case \"error\":\n      return \"░\";\n    case \"running\":\n      return \"▒\";\n    case \"cached\":\n      return \"▓\";\n    case \"skipped\":\n      return \"·\";\n    default:\n      return \"?\";\n  }\n}\n\n// =============================================================================\n// Diff Renderer\n// =============================================================================\n\n/**\n * Render a run diff as a string.\n */\nexport function renderDiff(diff: RunDiff): string {\n  const lines: string[] = [];\n\n  if (diff.statusChange) {\n    lines.push(`Status: ${diff.statusChange.from} → ${diff.statusChange.to}`);\n  }\n\n  if (diff.durationChange !== undefined) {\n    const sign = diff.durationChange >= 0 ? \"+\" : \"\";\n    lines.push(`Duration: ${sign}${diff.durationChange}ms`);\n  }\n\n  if (diff.added.length > 0) {\n    lines.push(\"\\nAdded steps:\");\n    for (const step of diff.added) {\n      lines.push(`  + ${step.step}`);\n    }\n  }\n\n  if (diff.removed.length > 0) {\n    lines.push(\"\\nRemoved steps:\");\n    for (const step of diff.removed) {\n      lines.push(`  - ${step.step}`);\n    }\n  }\n\n  if (diff.changed.length > 0) {\n    lines.push(\"\\nChanged steps:\");\n    for (const step of diff.changed) {\n      lines.push(`  ~ ${step.step}: ${step.from} → ${step.to}`);\n    }\n  }\n\n  if (diff.unchanged.length > 0) {\n    lines.push(`\\nUnchanged: ${diff.unchanged.length} steps`);\n  }\n\n  return lines.join(\"\\n\");\n}\n\n// =============================================================================\n// Quick Visualization Helpers\n// =============================================================================\n\n/**\n * Quick visualization helper for a single workflow run.\n */\nexport function quickVisualize(\n  workflowFn: (handleEvent: (event: WorkflowEvent<unknown>) => void) => Promise<unknown>,\n  options: DevtoolsOptions = {}\n): Promise<string> {\n  const devtools = createDevtools(options);\n\n  return workflowFn(devtools.handleEvent).then(() => devtools.render());\n}\n\n/**\n * Create an event handler that logs to console with pretty formatting.\n */\nexport function createConsoleLogger(options: { prefix?: string; colors?: boolean } = {}): (\n  event: WorkflowEvent<unknown>\n) => void {\n  const { prefix = \"[workflow]\", colors = true } = options;\n\n  const colorize = colors\n    ? {\n        reset: \"\\x1b[0m\",\n        dim: \"\\x1b[2m\",\n        green: \"\\x1b[32m\",\n        red: \"\\x1b[31m\",\n        yellow: \"\\x1b[33m\",\n        blue: \"\\x1b[34m\",\n        cyan: \"\\x1b[36m\",\n      }\n    : { reset: \"\", dim: \"\", green: \"\", red: \"\", yellow: \"\", blue: \"\", cyan: \"\" };\n\n  return (event: WorkflowEvent<unknown>) => {\n    const timestamp = new Date().toISOString().slice(11, 23);\n    let message: string;\n\n    switch (event.type) {\n      case \"workflow_start\":\n        message = `${colorize.blue}⏵ Workflow started${colorize.reset}`;\n        break;\n      case \"workflow_success\":\n        message = `${colorize.green}✓ Workflow completed${colorize.reset} ${colorize.dim}(${event.durationMs}ms)${colorize.reset}`;\n        break;\n      case \"workflow_error\":\n        message = `${colorize.red}✗ Workflow failed${colorize.reset}`;\n        break;\n      case \"step_start\":\n        message = `${colorize.cyan}→ ${event.name || event.stepKey || event.stepId}${colorize.reset}`;\n        break;\n      case \"step_success\":\n        message = `${colorize.green}✓ ${event.name || event.stepKey || event.stepId}${colorize.reset} ${colorize.dim}(${event.durationMs}ms)${colorize.reset}`;\n        break;\n      case \"step_error\":\n        message = `${colorize.red}✗ ${event.name || event.stepKey || event.stepId}${colorize.reset}`;\n        break;\n      case \"step_cache_hit\":\n        message = `${colorize.yellow}⚡ ${event.name || event.stepKey} (cached)${colorize.reset}`;\n        break;\n      case \"step_retry\":\n        message = `${colorize.yellow}↻ ${event.name || event.stepKey || event.stepId} retry ${event.attempt}/${event.maxAttempts}${colorize.reset}`;\n        break;\n      default:\n        message = `${colorize.dim}${event.type}${colorize.reset}`;\n    }\n\n    console.log(`${colorize.dim}${timestamp}${colorize.reset} ${prefix} ${message}`);\n  };\n}"],"mappings":"+kBAAA,IAAAA,GAAA,GAAAC,GAAAD,GAAA,yBAAAE,GAAA,mBAAAC,GAAA,mBAAAC,GAAA,eAAAC,KAAA,eAAAC,GAAAN,ICYO,SAASO,EAAeC,EAAoB,CACjD,GAAIA,EAAK,IACP,MAAO,GAAG,KAAK,MAAMA,CAAE,CAAC,KAG1B,GAAIA,EAAK,IAGP,MAAO,IAFSA,EAAK,KAEH,QAAQ,CAAC,EAAE,QAAQ,OAAQ,EAAE,CAAC,IAGlD,IAAIC,EAAU,KAAK,MAAMD,EAAK,GAAK,EAC/BE,EAAU,KAAK,MAAOF,EAAK,IAAS,GAAI,EAM5C,OALIE,GAAW,KACbD,GAAW,EACXC,EAAU,GAGRA,IAAY,EACP,GAAGD,CAAO,IAGZ,GAAGA,CAAO,KAAKC,CAAO,GAC/B,CAKO,SAASC,IAAqB,CACnC,MAAO,QAAQ,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,EAAG,CAAC,CAAC,EACrE,CCDA,SAASC,GAAkBC,EAA4B,CACrD,QAAWC,KAAQD,EAUjB,IANGC,EAAK,OAAS,YAAcA,EAAK,OAAS,QAAUA,EAAK,OAAS,aACnE,CAACA,EAAK,GAAG,WAAW,WAAW,GAK7BA,EAAK,OAAS,WAChB,MAAO,GAGX,MAAO,EACT,CAcO,SAASC,GACdF,EACAG,EAAmC,CAAC,EACxB,CAGZ,GAAIJ,GAAkBC,CAAK,EACzB,OAAOA,EAGT,GAAM,CAAE,aAAAI,EAAe,EAAG,SAAAC,EAAW,CAAE,EAAIF,EAGrCG,EAA8D,CAAC,EAC/DC,EAA4D,CAAC,EAEnE,QAASC,EAAI,EAAGA,EAAIR,EAAM,OAAQQ,IAAK,CACrC,IAAMP,EAAOD,EAAMQ,CAAC,EAChBP,EAAK,OAAS,QAAUA,EAAK,UAAY,OAC3CK,EAAgB,KAAK,CACnB,KAAAL,EACA,QAASA,EAAK,QACd,MAAOA,EAAK,OAASA,EAAK,SAAWA,EAAK,YAAc,GACxD,cAAeO,CACjB,CAAC,EAGDD,EAAa,KAAK,CAAE,KAAAN,EAAM,cAAeO,CAAE,CAAC,CAEhD,CAEA,GAAIF,EAAgB,QAAU,EAC5B,OAAON,EAITM,EAAgB,KAAK,CAAC,EAAGG,IAAM,EAAE,QAAUA,EAAE,OAAO,EAIpD,IAAMC,EAAkC,CAAC,EACrCC,EAAsC,CAACL,EAAgB,CAAC,CAAC,EAE7D,QAASE,EAAI,EAAGA,EAAIF,EAAgB,OAAQE,IAAK,CAC/C,IAAMI,EAAON,EAAgBE,CAAC,EACxBK,EAAa,KAAK,IAAI,GAAGF,EAAa,IAAKG,GAAMA,EAAE,OAAO,CAAC,EAC3DC,EAAW,KAAK,IAAI,GAAGJ,EAAa,IAAKG,GAAMA,EAAE,KAAK,CAAC,EAKvDE,EAAkBJ,EAAK,SAAWC,EAAaR,EAC/CY,EAAiBL,EAAK,QAAUG,EAEtC,GAAI,CAACC,GAAmB,CAACC,EAAgB,CAEvCP,EAAO,KAAKC,CAAY,EACxBA,EAAe,CAACC,CAAI,EACpB,QACF,CAKA,IAAMM,EAAkBD,EACpB,KAAK,IAAIL,EAAK,MAAOG,CAAQ,EAAIH,EAAK,QACtC,EAIAI,GAAmBE,GAAmBd,EACxCO,EAAa,KAAKC,CAAI,GAGtBF,EAAO,KAAKC,CAAY,EACxBA,EAAe,CAACC,CAAI,EAExB,CACAF,EAAO,KAAKC,CAAY,EAGxB,IAAMQ,EAAuD,CAAC,EAE9D,QAAWC,KAASV,EAAQ,CAE1B,IAAMW,EAAW,KAAK,IAAI,GAAGD,EAAM,IAAKN,GAAMA,EAAE,aAAa,CAAC,EAE9D,GAAIM,EAAM,SAAW,EAEnBD,EAAa,KAAK,CAAE,KAAMC,EAAM,CAAC,EAAE,KAAM,SAAAC,CAAS,CAAC,MAC9C,CAEL,IAAMC,EAAWF,EAAM,IAAKN,GAAMA,EAAE,IAAI,EAClCS,EAAU,KAAK,IAAI,GAAGH,EAAM,IAAKN,GAAMA,EAAE,OAAO,CAAC,EACjDU,EAAQ,KAAK,IAAI,GAAGJ,EAAM,IAAKN,GAAMA,EAAE,KAAK,CAAC,EAE7CW,EAA6B,CACjC,KAAM,WACN,GAAI,qBAAqBF,CAAO,GAChC,KAAM,GAAGD,EAAS,MAAM,kBACxB,MAAOI,GAAiBJ,CAAQ,EAChC,KAAM,MACN,SAAAA,EACA,QAAAC,EACA,MAAAC,EACA,WAAYA,EAAQD,CACtB,EAEAJ,EAAa,KAAK,CAAE,KAAMM,EAAc,SAAAJ,CAAS,CAAC,CACpD,CACF,CAGA,OAAW,CAAE,KAAApB,EAAM,cAAA0B,CAAc,IAAKpB,EACpCY,EAAa,KAAK,CAAE,KAAAlB,EAAM,SAAU0B,CAAc,CAAC,EAIrD,OAAAR,EAAa,KAAK,CAAC,EAAGV,IAAM,EAAE,SAAWA,EAAE,QAAQ,EAE5CU,EAAa,IAAKS,GAAMA,EAAE,IAAI,CACvC,CAKA,SAASF,GACPJ,EACoE,CAEpE,OADiBA,EAAS,KAAMO,GAAMA,EAAE,QAAU,OAAO,EACpC,QAEFP,EAAS,KAAMO,GAAMA,EAAE,QAAU,SAAS,EACtC,UAEJP,EAAS,KAAMO,GAAMA,EAAE,QAAU,SAAS,EACtC,WAEJP,EAAS,MACzBO,GAAMA,EAAE,QAAU,WAAaA,EAAE,QAAU,QAC9C,EACuB,UAGzB,CC9FO,SAASC,GAAgBC,EAA4B,CAAC,EAAG,CAC9D,GAAM,CACJ,eAAAC,EAAiB,GACjB,kBAAAC,EACA,gBAAiBC,EAAyB,GAC1C,aAAAC,EAAe,GACjB,EAAIJ,EAGAK,EAAkBF,EAIhBG,EAAoBC,GAAW,EACjCC,EACAC,EACAC,EACAC,EAA2B,UAC3BC,EACAC,EAGEC,EAAc,IAAI,IAGlBC,EAA4B,CAAC,EAG7BC,EAAkC,CAAC,EAGnCC,EAAgB,IAAI,IAGtBC,EAA2B,CAAC,EAG5BC,EAAY,KAAK,IAAI,EACrBC,EAAgBD,EAGhBE,EAA2B,CAC7B,YAAa,IAAI,GACnB,EAEIC,EAGEC,EAA0B,CAAC,EAC7BC,EAAa,EAOjB,SAASC,EAAUC,EAAqE,CACtF,OAAOA,EAAM,QAAUA,EAAM,SAAWA,EAAM,MAAQnB,GAAW,CACnE,CAQA,SAASoB,EAAQC,EAAsB,CAGrC,GAAIb,EAAW,OAAS,EAAG,CACzBA,EAAWA,EAAW,OAAS,CAAC,EAAE,SAAS,KAAKa,CAAI,EACpDR,EAAgB,KAAK,IAAI,EACzB,MACF,CAGA,GAAIJ,EAAc,OAAS,EAAG,CAC5B,IAAMa,EAAWb,EAAcA,EAAc,OAAS,CAAC,EAEvD,QAAWc,KAAUD,EAAS,SAAS,OAAO,EAC5C,GAAIC,EAAO,MAAO,CAChBA,EAAO,SAAS,KAAKF,CAAI,EACzBR,EAAgB,KAAK,IAAI,EACzB,MACF,CAGFS,EAAS,gBAAgB,KAAKD,CAAI,EAClCR,EAAgB,KAAK,IAAI,EACzB,MACF,CAGAF,EAAa,KAAKU,CAAI,EACtBR,EAAgB,KAAK,IAAI,CAC3B,CAMA,SAASW,EAAgBL,EAAqC,CAC5D,GAAI,CAACrB,EAAiB,OAGtB,IAAM2B,EAAKC,GAAM,EAGXC,EAAkB,IAAI,IAC5B,OAAW,CAACC,EAAIC,CAAI,IAAKtB,EACvBoB,EAAgB,IAAIC,EAAI,CACtB,GAAIC,EAAK,GACT,KAAMA,EAAK,KACX,IAAKA,EAAK,IACV,QAASA,EAAK,QACd,WAAYA,EAAK,WACjB,SAAUA,EAAK,SACf,UAAWA,EAAK,SAClB,CAAC,EAGH,IAAMC,EAAuB,CAC3B,GAAI,YAAYb,CAAU,GAC1B,WAAAA,EACA,MAAO,gBAAgBE,CAAK,EAC5B,GAAI,gBAAgBM,CAAE,EACtB,UAAW,KAAK,IAAI,EACpB,YAAaE,CACf,EAEAX,EAAU,KAAKc,CAAQ,EAGnBd,EAAU,OAASnB,GACrBmB,EAAU,MAAM,EAGlBC,GACF,CAKA,SAASc,EAAYZ,EAAqC,CACxD,OAAQA,EAAM,KAAM,CAClB,IAAK,iBAAkB,CAErBR,EAAe,CAAC,EAChBR,EAAgB,OAChBG,EAAqB,OACrBD,EAAgB,OAChBE,EAAY,MAAM,EAClBC,EAAW,OAAS,EACpBC,EAAc,OAAS,EACvBC,EAAc,MAAM,EAEpBT,EAAakB,EAAM,WACnBjB,EAAkBiB,EAAM,GACxBf,EAAgB,UAChBQ,EAAY,KAAK,IAAI,EACrBC,EAAgBD,EAEZG,IAA2B,QAAaA,IAA2BI,EAAM,aAC3EL,EAAU,UAAY,OACtBA,EAAU,cAAgB,QAE5BC,EAAyBI,EAAM,WAC/BL,EAAU,YAAc,IAAI,IAC5B,KACF,CAEA,IAAK,mBACHV,EAAgB,UAChBD,EAAgBgB,EAAM,GACtBb,EAAqBa,EAAM,WAC3BN,EAAgB,KAAK,IAAI,EACzB,MAEF,IAAK,iBACHT,EAAgB,QAChBD,EAAgBgB,EAAM,GACtBd,EAAgBc,EAAM,MACtBb,EAAqBa,EAAM,WAC3BN,EAAgB,KAAK,IAAI,EACzB,MAEF,IAAK,qBACHT,EAAgB,UAChBD,EAAgBgB,EAAM,GACtBb,EAAqBa,EAAM,WAC3BN,EAAgB,KAAK,IAAI,EACzB,MAEF,IAAK,aAAc,CACjB,IAAMe,EAAKV,EAAUC,CAAK,EAC1BZ,EAAY,IAAIqB,EAAI,CAClB,GAAAA,EACA,KAAMT,EAAM,KACZ,IAAKA,EAAM,QACX,QAASA,EAAM,GACf,WAAY,EACZ,SAAU,GACV,SAAWA,EAAc,QAC3B,CAAC,EACDN,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,eAAgB,CACnB,IAAMe,EAAKV,EAAUC,CAAK,EACpBa,EAASzB,EAAY,IAAIqB,CAAE,EACjC,GAAII,EAAQ,CACV,IAAMX,EAAiB,CACrB,KAAM,OACN,GAAIW,EAAO,GACX,KAAMA,EAAO,KACb,IAAKA,EAAO,IACZ,MAAO,UACP,QAASA,EAAO,QAChB,MAAOb,EAAM,GACb,WAAYA,EAAM,WAClB,GAAIa,EAAO,WAAa,GAAK,CAAE,WAAYA,EAAO,UAAW,EAC7D,GAAIA,EAAO,UAAY,CAAE,SAAU,GAAM,UAAWA,EAAO,SAAU,EACrE,GAAIA,EAAO,UAAY,CAAE,SAAUA,EAAO,QAAS,CACrD,EACAZ,EAAQC,CAAI,EACZd,EAAY,OAAOqB,CAAE,CACvB,CACA,KACF,CAEA,IAAK,aAAc,CACjB,IAAMA,EAAKV,EAAUC,CAAK,EACpBa,EAASzB,EAAY,IAAIqB,CAAE,EACjC,GAAII,EAAQ,CACV,IAAMX,EAAiB,CACrB,KAAM,OACN,GAAIW,EAAO,GACX,KAAMA,EAAO,KACb,IAAKA,EAAO,IACZ,MAAO,QACP,QAASA,EAAO,QAChB,MAAOb,EAAM,GACb,WAAYA,EAAM,WAClB,MAAOA,EAAM,MACb,GAAIa,EAAO,WAAa,GAAK,CAAE,WAAYA,EAAO,UAAW,EAC7D,GAAIA,EAAO,UAAY,CAAE,SAAU,GAAM,UAAWA,EAAO,SAAU,EACrE,GAAIA,EAAO,UAAY,CAAE,SAAUA,EAAO,QAAS,EACnD,GAAKb,EAAc,aAAe,CAAE,iBAAmBA,EAAc,WAAY,CACnF,EACAC,EAAQC,CAAI,EACZd,EAAY,OAAOqB,CAAE,CACvB,CACA,KACF,CAEA,IAAK,eAAgB,CACnB,IAAMA,EAAKV,EAAUC,CAAK,EACpBa,EAASzB,EAAY,IAAIqB,CAAE,EACjC,GAAII,EAAQ,CACV,IAAMX,EAAiB,CACrB,KAAM,OACN,GAAIW,EAAO,GACX,KAAMA,EAAO,KACb,IAAKA,EAAO,IACZ,MAAO,UACP,QAASA,EAAO,QAChB,MAAOb,EAAM,GACb,WAAYA,EAAM,WAClB,GAAIa,EAAO,WAAa,GAAK,CAAE,WAAYA,EAAO,UAAW,EAC7D,GAAIA,EAAO,UAAY,CAAE,SAAU,GAAM,UAAWA,EAAO,SAAU,EACrE,GAAIA,EAAO,UAAY,CAAE,SAAUA,EAAO,QAAS,CACrD,EACAZ,EAAQC,CAAI,EACZd,EAAY,OAAOqB,CAAE,CACvB,CACA,KACF,CAEA,IAAK,iBAAkB,CAErB,IAAMP,EAAiB,CACrB,KAAM,OACN,GAHSH,EAAUC,CAAK,EAIxB,KAAMA,EAAM,KACZ,IAAKA,EAAM,QACX,MAAO,SACP,QAASA,EAAM,GACf,MAAOA,EAAM,GACb,WAAY,CACd,EACAC,EAAQC,CAAI,EACZ,KACF,CAEA,IAAK,kBAGH,MAEF,IAAK,gBAGH,MAEF,IAAK,eAAgB,CAGnB,IAAMO,EAAKV,EAAUC,CAAK,EACpBa,EAASzB,EAAY,IAAIqB,CAAE,EAC7BI,IACFA,EAAO,SAAW,GAClBA,EAAO,UAAYb,EAAM,WAE3BN,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,aAAc,CAEjB,IAAMe,EAAKV,EAAUC,CAAK,EACpBa,EAASzB,EAAY,IAAIqB,CAAE,EAC7BI,IACFA,EAAO,YAAcb,EAAM,SAAW,GAAK,GAE7CN,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,yBAGHA,EAAgB,KAAK,IAAI,EACzB,MAEF,IAAK,eAAgB,CAEnB,IAAMQ,EAAiB,CACrB,KAAM,OACN,GAHSH,EAAUC,CAAK,EAIxB,KAAMA,EAAM,KACZ,IAAKA,EAAM,QACX,MAAO,UACP,QAASA,EAAM,GACf,MAAOA,EAAM,GACb,WAAY,EACZ,GAAKA,EAAc,UAAY,CAAE,SAAWA,EAAc,QAAS,CACrE,EACAC,EAAQC,CAAI,EACZ,KACF,CAGA,IAAK,WAAY,CACf,IAAMY,EAAId,EAaV,GAAIc,EAAE,QAAU,QAAS,CACvBC,EAAoB,CAClB,KAAM,iBACN,WAAaf,EAAiC,WAC9C,WAAYc,EAAE,WACd,KAAMA,EAAE,OAASA,EAAE,WACnB,UAAWA,EAAE,MACb,cAAeA,EAAE,MACjB,GAAIA,EAAE,EACR,CAAC,EACDC,EAAoB,CAClB,KAAM,kBACN,WAAaf,EAAiC,WAC9C,WAAYc,EAAE,WACd,YAAa,OACb,UAAWA,EAAE,MACb,MAAOA,EAAE,SAAW,OACpB,GAAIA,EAAE,EACR,CAAC,EACDC,EAAoB,CAClB,KAAM,kBACN,WAAaf,EAAiC,WAC9C,WAAYc,EAAE,WACd,YAAa,OACb,MAAOA,EAAE,SAAW,OACpB,GAAIA,EAAE,EACR,CAAC,EACD,KACF,CACA,GAAIA,EAAE,QAAU,MAAO,CACrBC,EAAoB,CAClB,KAAM,eACN,WAAaf,EAAiC,WAC9C,WAAYc,EAAE,WACd,YAAaA,EAAE,OACf,GAAIA,EAAE,GACN,WAAYA,EAAE,YAAc,CAC9B,CAAC,EACD,KACF,CAMA,IAAMZ,EAAqB,CACzB,KAAM,WACN,GAAIY,EAAE,WACN,KAAMA,EAAE,OAASA,EAAE,WACnB,MAAO,UACP,QAASA,EAAE,GACX,MAAOA,EAAE,GACT,WAAY,EACZ,UAAWA,EAAE,MACb,cAAeA,EAAE,MACjB,YAAaA,EAAE,OACf,SAAU,CACR,CAAE,MAAO,OAAQ,UAAWA,EAAE,MAAO,MAAOA,EAAE,SAAW,OAAQ,SAAU,CAAC,CAAE,EAC9E,CAAE,MAAO,OAAQ,MAAOA,EAAE,SAAW,OAAQ,SAAU,CAAC,CAAE,CAC5D,CACF,EACAb,EAAQC,CAAI,EACZR,EAAgB,KAAK,IAAI,EACzB,KACF,CAGA,IAAK,kBAAmB,CACtB,IAAMsB,EAA0B,CAC9B,KAAM,YACN,MAAO,UACP,GAAIhB,EAAM,GACV,WAAYA,EAAM,WAClB,QAAS,CACP,OAAQA,EAAM,OACd,QAASA,EAAM,OACjB,CACF,EACAL,EAAU,UAAYqB,EACtBpB,EAAyBI,EAAM,WAC/BN,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,wBAAyB,CAC5B,IAAMsB,EAA0B,CAC9B,KAAM,YACN,MAAO,QACP,GAAIhB,EAAM,GACV,WAAYA,EAAM,WAClB,MAAOA,EAAM,KACf,EACAL,EAAU,UAAYqB,EACtBpB,EAAyBI,EAAM,WAC/BN,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,oBAAqB,CACxB,IAAMsB,EAA0B,CAC9B,KAAM,gBACN,MAAO,UACP,GAAIhB,EAAM,GACV,WAAYA,EAAM,WAClB,QAAS,CACP,OAAQA,EAAM,OACd,QAASA,EAAM,OACjB,CACF,EACAL,EAAU,cAAgBqB,EAC1BpB,EAAyBI,EAAM,WAC/BN,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,0BAA2B,CAC9B,IAAMsB,EAA0B,CAC9B,KAAM,gBACN,MAAO,QACP,GAAIhB,EAAM,GACV,WAAYA,EAAM,WAClB,MAAOA,EAAM,KACf,EACAL,EAAU,cAAgBqB,EAC1BpB,EAAyBI,EAAM,WAC/BN,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,kBAAmB,CACtB,IAAMsB,EAA0B,CAC9B,KAAM,cACN,MAAO,UACP,GAAIhB,EAAM,GACV,WAAYA,EAAM,WAClB,QAAS,CACP,QAASA,EAAM,OACjB,CACF,EACAL,EAAU,YAAY,IAAIK,EAAM,QAASgB,CAAQ,EACjDtB,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,wBAAyB,CAC5B,IAAMsB,EAA0B,CAC9B,KAAM,cACN,MAAO,QACP,GAAIhB,EAAM,GACV,WAAYA,EAAM,WAClB,MAAOA,EAAM,MACb,QAAS,CACP,QAASA,EAAM,OACjB,CACF,EACAL,EAAU,YAAY,IAAIK,EAAM,QAASgB,CAAQ,EACjDtB,EAAgB,KAAK,IAAI,EACzB,KACF,CAGA,IAAK,iBAAkB,CACrB,IAAMuB,EAAY,GAAGjB,EAAM,UAAU,IAAIA,EAAM,SAAS,GACxDT,EAAc,IAAI0B,EAAW,CAC3B,GAAIpC,GAAW,EACf,UAAWmB,EAAM,UACjB,QAASA,EAAM,GACf,WAAY,EACZ,UAAW,EACX,YAAa,SACb,qBAAsB,GACtB,cAAe,CACjB,CAAC,EACDN,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,eAAgB,CACnB,IAAMuB,EAAY,GAAGjB,EAAM,UAAU,IAAIA,EAAM,SAAS,GAClDkB,EAAS3B,EAAc,IAAI0B,CAAS,EACtCC,IACFA,EAAO,aACPA,EAAO,cAAgB,KAAK,IAAIA,EAAO,cAAelB,EAAM,QAAQ,GAEtEN,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,cAAe,CAClB,IAAMuB,EAAY,GAAGjB,EAAM,UAAU,IAAIA,EAAM,SAAS,GAClDkB,EAAS3B,EAAc,IAAI0B,CAAS,EACtCC,GACFA,EAAO,YAETxB,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,eAAgB,CACnB,IAAMuB,EAAY,GAAGjB,EAAM,UAAU,IAAIA,EAAM,SAAS,GAClDkB,EAAS3B,EAAc,IAAI0B,CAAS,EAC1C,GAAIC,EAAQ,CACVA,EAAO,YAAc,SACrBA,EAAO,cAAgBlB,EAAM,cAE7B,IAAME,EAAmB,CACvB,KAAM,SACN,GAAIgB,EAAO,GACX,UAAWA,EAAO,UAClB,MAAO,UACP,QAASA,EAAO,QAChB,MAAOlB,EAAM,GACb,WAAYA,EAAM,GAAKkB,EAAO,QAC9B,WAAYA,EAAO,WACnB,UAAWA,EAAO,UAClB,cAAelB,EAAM,cACrB,YAAa,SACb,qBAAsBkB,EAAO,oBAC/B,EACAjB,EAAQC,CAAI,EACZX,EAAc,OAAO0B,CAAS,CAChC,CACAvB,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,eAAgB,CACnB,IAAMuB,EAAY,GAAGjB,EAAM,UAAU,IAAIA,EAAM,SAAS,GAClDkB,EAAS3B,EAAc,IAAI0B,CAAS,EAC1C,GAAIC,EAAQ,CACVA,EAAO,YAAc,QAErB,IAAMhB,EAAmB,CACvB,KAAM,SACN,GAAIgB,EAAO,GACX,UAAWA,EAAO,UAClB,MAAO,QACP,MAAOlB,EAAM,MACb,QAASkB,EAAO,QAChB,MAAOlB,EAAM,GACb,WAAYA,EAAM,GAAKkB,EAAO,QAC9B,WAAYA,EAAO,WACnB,UAAWA,EAAO,UAClB,cAAelB,EAAM,SACrB,YAAa,QACb,qBAAsBkB,EAAO,oBAC/B,EACAjB,EAAQC,CAAI,EACZX,EAAc,OAAO0B,CAAS,CAChC,CACAvB,EAAgB,KAAK,IAAI,EACzB,KACF,CAEA,IAAK,sBAAuB,CAC1B,IAAMuB,EAAY,GAAGjB,EAAM,UAAU,IAAIA,EAAM,SAAS,GAClDkB,EAAS3B,EAAc,IAAI0B,CAAS,EACtCC,IACFA,EAAO,qBAAuB,IAEhCxB,EAAgB,KAAK,IAAI,EACzB,KACF,CACF,CAGAW,EAAgBL,CAAK,CACvB,CAKA,SAASmB,EAAiBnB,EAA8C,CACtE,GAAIA,EAAM,OAAS,cACjBX,EAAW,KAAK,CACd,GAAIW,EAAM,QACV,KAAMA,EAAM,KACZ,KAAMA,EAAM,UACZ,QAASA,EAAM,GACf,SAAU,CAAC,CACb,CAAC,EACDN,EAAgB,KAAK,IAAI,UAChBM,EAAM,OAAS,YAAa,CAErC,IAAMoB,EAAa/B,EAAW,UAAWgC,GAAMA,EAAE,KAAOrB,EAAM,OAAO,EACrE,GAAIoB,IAAe,GAEjB,OAKF,KAAO/B,EAAW,OAAS+B,EAAa,GAAG,CACzC,IAAME,EAAcjC,EAAW,IAAI,EAC7BkC,EACJD,EAAY,OAAS,OACjB,CACE,KAAM,OACN,GAAIA,EAAY,GAChB,KAAMA,EAAY,KAClB,MAAOE,EAAYF,EAAY,QAAQ,EACvC,QAASA,EAAY,QACrB,MAAOtB,EAAM,GACb,SAAUsB,EAAY,QACxB,EACA,CACE,KAAM,WACN,GAAIA,EAAY,GAChB,KAAMA,EAAY,KAClB,MAAOE,EAAYF,EAAY,QAAQ,EACvC,QAASA,EAAY,QACrB,MAAOtB,EAAM,GACb,SAAUsB,EAAY,SACtB,KAAMA,EAAY,OAAS,aAAe,aAAe,KAC3D,EAENjC,EAAWA,EAAW,OAAS,CAAC,EAAE,SAAS,KAAKkC,CAAU,CAC5D,CAGA,GAAM,CAACE,CAAK,EAAIpC,EAAW,OAAO+B,EAAY,CAAC,EAEzClB,EACJuB,EAAM,OAAS,OACX,CACE,KAAM,OACN,GAAIA,EAAM,GACV,KAAMA,EAAM,KACZ,MAAOD,EAAYC,EAAM,QAAQ,EACjC,QAASA,EAAM,QACf,MAAOzB,EAAM,GACb,WAAYA,EAAM,WAClB,SAAUyB,EAAM,SAChB,SAAUzB,EAAM,QAClB,EACA,CACE,KAAM,WACN,GAAIyB,EAAM,GACV,KAAMA,EAAM,KACZ,MAAOD,EAAYC,EAAM,QAAQ,EACjC,QAASA,EAAM,QACf,MAAOzB,EAAM,GACb,WAAYA,EAAM,WAClB,SAAUyB,EAAM,SAChB,KAAMA,EAAM,OAAS,aAAe,aAAe,KACrD,EACNxB,EAAQC,CAAI,CACd,CACF,CAKA,SAASa,EACPf,EACM,CACN,GAAIA,EAAM,OAAS,iBACjBV,EAAc,KAAK,CACjB,GAAIU,EAAM,WACV,KAAMA,EAAM,KACZ,UAAWA,EAAM,UACjB,cAAeA,EAAM,cACrB,QAASA,EAAM,GACf,SAAU,IAAI,IACd,gBAAiB,CAAC,CACpB,CAAC,EACDN,EAAgB,KAAK,IAAI,UAChBM,EAAM,OAAS,kBAAmB,CAE3C,IAAMG,EAAWb,EAAc,KAAMoC,GAAMA,EAAE,KAAO1B,EAAM,UAAU,EACpE,GAAIG,EAAU,CAEZ,IAAMwB,EAAY3B,EAAM,YAClB4B,EAAWzB,EAAS,SAAS,IAAIwB,CAAS,EAChD,GAAIC,EAEFA,EAAS,MAAQ5B,EAAM,MAEnBA,EAAM,OAASG,EAAS,gBAAgB,OAAS,IACnDyB,EAAS,SAAS,QAAQ,GAAGzB,EAAS,eAAe,EACrDA,EAAS,gBAAkB,CAAC,OAEzB,CAEL,IAAM0B,EAAW7B,EAAM,MAAQ,CAAC,GAAGG,EAAS,eAAe,EAAI,CAAC,EAC5DH,EAAM,QACRG,EAAS,gBAAkB,CAAC,GAE9BA,EAAS,SAAS,IAAIwB,EAAW,CAC/B,MAAO3B,EAAM,YACb,UAAWA,EAAM,UACjB,MAAOA,EAAM,MACb,SAAA6B,CACF,CAAC,CACH,CACAnC,EAAgB,KAAK,IAAI,CAC3B,CACF,SAAWM,EAAM,OAAS,eAAgB,CAExC,IAAM8B,EAAQxC,EAAc,UAAWoC,GAAMA,EAAE,KAAO1B,EAAM,UAAU,EACtE,GAAI8B,IAAU,GAAI,CAChB,GAAM,CAAC3B,CAAQ,EAAIb,EAAc,OAAOwC,EAAO,CAAC,EAE1CC,EAAYzC,EAAc,OAAOwC,CAAK,EAGxCE,EAA6B,MAAM,KAAK7B,EAAS,SAAS,OAAO,CAAC,EAClE6B,EAAS,SAAW,GAAK7B,EAAS,gBAAgB,OAAS,IAC7D6B,EAAW,CACT,CACE,MAAO,UACP,MAAO,GACP,SAAU,CAAC,GAAG7B,EAAS,eAAe,CACxC,CACF,GAIF,IAAM8B,EAAsBD,EAAS,KAAME,IAAMA,GAAE,KAAK,GAAG,MAErDhC,GAAqB,CACzB,KAAM,WACN,GAAIC,EAAS,GACb,KAAMA,EAAS,KACf,MAAOqB,EACLQ,EAAS,QAASE,IAAOA,GAAE,MAAQA,GAAE,SAAW,CAAC,CAAE,CACrD,EACA,QAAS/B,EAAS,QAClB,MAAOH,EAAM,GACb,WAAYA,EAAM,WAClB,UAAWG,EAAS,UACpB,cAAeA,EAAS,cACxB,YAAaH,EAAM,aAAeiC,EAClC,SAAAD,CACF,EACA/B,EAAQC,EAAI,EACZZ,EAAc,KAAK,GAAGyC,CAAS,EAC/BrC,EAAgB,KAAK,IAAI,CAC3B,CACF,CACF,CAKA,SAAS8B,EAAYK,EAAiC,CACpD,OAAIA,EAAS,SAAW,EAAU,UAEjBA,EAAS,KAAMM,GAAMA,EAAE,QAAU,OAAO,EACpC,QAEFN,EAAS,MACzBM,GAAMA,EAAE,QAAU,WAAaA,EAAE,QAAU,QAC9C,EACuB,UAEJN,EAAS,KAAMM,GAAMA,EAAE,QAAU,SAAS,EACtC,UAEhB,SACT,CAKA,SAASC,GAA8B,CACrC,IAAMC,EAAQ,CAAC,GAAG7C,CAAY,EAG9B,OAAW,CAAC,CAAEqB,CAAM,IAAKzB,EACvBiD,EAAM,KAAK,CACT,KAAM,OACN,GAAIxB,EAAO,GACX,KAAMA,EAAO,KACb,IAAKA,EAAO,IACZ,MAAO,UACP,QAASA,EAAO,QAChB,GAAIA,EAAO,WAAa,GAAK,CAAE,WAAYA,EAAO,UAAW,EAC7D,GAAIA,EAAO,UAAY,CAAE,SAAU,GAAM,UAAWA,EAAO,SAAU,CACvE,CAAC,EAIH,OAAW,CAAC,CAAEK,CAAM,IAAK3B,EACvB8C,EAAM,KAAK,CACT,KAAM,SACN,GAAInB,EAAO,GACX,UAAWA,EAAO,UAClB,MAAO,UACP,QAASA,EAAO,QAChB,WAAYA,EAAO,WACnB,UAAWA,EAAO,UAClB,cAAeA,EAAO,cACtB,YAAaA,EAAO,YACpB,qBAAsBA,EAAO,oBAC/B,CAAsB,EAGxB,OAAOmB,CACT,CAKA,SAAS9B,IAAoB,CAC3B,IAAIsB,EAAWO,EAAgB,EAG3B7D,IACFsD,EAAWS,GAAqBT,EAAUrD,CAAiB,GAG7D,IAAM+D,EAAqB,CACzB,KAAM,WACN,GAAIzD,GAAcF,EAClB,WAAYE,GAAcF,EAC1B,MAAOK,EACP,QAASF,EACT,MAAOC,EACP,WAAYG,EACZ,SAAA0C,EACA,MAAO3C,CACT,EAGMsD,EACJ7C,EAAU,YAAc,QACxBA,EAAU,gBAAkB,QAC5BA,EAAU,YAAY,KAAO,EAE/B,MAAO,CACL,KAAA4C,EACA,SAAU,CACR,UAAA9C,EACA,cAAAC,CACF,EACA,GAAI8C,GAAY,CAAE,MAAO7C,CAAU,CACrC,CACF,CAKA,SAAS8C,IAAc,CACrB3D,EAAa,OACbC,EAAkB,OAClBC,EAAgB,OAChBC,EAAgB,UAChBC,EAAgB,OAChBC,EAAqB,OACrBC,EAAY,MAAM,EAClBC,EAAW,OAAS,EACpBC,EAAc,OAAS,EACvBC,EAAc,MAAM,EACpBC,EAAe,CAAC,EAChBC,EAAY,KAAK,IAAI,EACrBC,EAAgBD,EAEhBE,EAAY,CACV,YAAa,IAAI,GACnB,EACAC,EAAyB,OAEzBC,EAAU,OAAS,EACnBC,EAAa,CACf,CAKA,SAAS4C,IAA6B,CACpC,MAAO,CAAC,GAAG7C,CAAS,CACtB,CAKA,SAAS8C,GAAcb,EAAuC,CAC5D,OAAOjC,EAAUiC,CAAK,CACxB,CAKA,SAASc,GAAQd,EAAuC,CACtD,OAAOjC,EAAUiC,CAAK,GAAG,EAC3B,CAKA,SAASe,IAAuB,CAC9BhD,EAAU,OAAS,EACnBC,EAAa,CACf,CAEA,MAAO,CACL,YAAAc,EACA,iBAAAO,EACA,oBAAAJ,EACA,MAAAR,GACA,MAAAkC,GAEA,aAAAC,GACA,cAAAC,GACA,QAAAC,GACA,eAAAC,GAEA,IAAI,gBAAiB,CACnB,OAAOzD,EAAY,KAAO,CAC5B,EAEA,IAAI,OAAQ,CACV,OAAOH,CACT,EAEA,IAAI,eAAgB,CAClB,OAAOY,EAAU,MACnB,EAEA,IAAI,kBAAmB,CACrB,OAAOlB,CACT,EAEA,oBAAoBmE,EAAwB,CAC1CnE,EAAkBmE,CACpB,CACF,CACF,CCrlCA,IAAAC,GAAqC,mBCkb9B,SAASC,EAAWC,EAAkC,CAC3D,OAAOA,EAAK,OAAS,MACvB,CAKO,SAASC,GAAeD,EAAsC,CACnE,OAAOA,EAAK,OAAS,UACvB,CAKO,SAASE,EAAeF,EAAsC,CACnE,OAAOA,EAAK,OAAS,UACvB,CAKO,SAASG,EAAWH,EAAkC,CAC3D,OAAOA,EAAK,OAAS,MACvB,CAKO,SAASI,EAAeJ,EAAsC,CACnE,OAAOA,EAAK,OAAS,UACvB,CAKO,SAASK,GAAaL,EAAoC,CAC/D,OAAOA,EAAK,OAAS,QACvB,CCpdA,IAAMM,GAAQ,UACRC,GAAO,UACPC,GAAM,UAGNC,GAAS,WACTC,GAAW,WACXC,GAAY,WACZC,GAAU,WACVC,GAAU,WACVC,GAAW,WASV,SAASC,EAASC,EAAcC,EAAuB,CAC5D,OAAKA,EACE,GAAGA,CAAK,GAAGD,CAAI,GAAGV,EAAK,GADXU,CAErB,CAKO,SAASE,GAAKF,EAAsB,CACzC,MAAO,GAAGT,EAAI,GAAGS,CAAI,GAAGV,EAAK,EAC/B,CAKO,SAASa,EAAIH,EAAsB,CACxC,MAAO,GAAGR,EAAG,GAAGQ,CAAI,GAAGV,EAAK,EAC9B,CASO,IAAMc,GAAkC,CAC7C,QAASN,GACT,QAASH,GACT,QAASD,GACT,MAAOD,GACP,QAASI,GACT,OAAQD,GACR,QAASJ,GAAMK,EACjB,EASO,SAASQ,GAAeC,EAA0B,CACvD,OAAQA,EAAO,CACb,IAAK,UACH,MAAO,SACT,IAAK,UACH,MAAO,SACT,IAAK,UACH,MAAO,SACT,IAAK,QACH,MAAO,SACT,IAAK,UACH,MAAO,SACT,IAAK,SACH,MAAO,SACT,IAAK,UACH,MAAO,QACX,CACF,CAKO,SAASC,GAAiBD,EAAkBE,EAA6B,CAC9E,IAAMC,EAASJ,GAAeC,CAAK,EACnC,OAAOP,EAASU,EAAQD,EAAOF,CAAK,CAAC,CACvC,CAKO,SAASI,GACdV,EACAM,EACAE,EACQ,CACR,OAAOT,EAASC,EAAMQ,EAAOF,CAAK,CAAC,CACrC,CAUO,SAASK,EAAUC,EAAqB,CAE7C,OAAOA,EAAI,QAAQ,kBAAmB,EAAE,CAC1C,CF7EA,IAAMC,EAAM,CACV,QAAS,SACT,SAAU,SACV,WAAY,SACZ,YAAa,SACb,WAAY,SACZ,SAAU,SACV,SAAU,SACV,QAAS,SACT,QAAS,SACT,MAAO,SACP,MAAO,QACT,EASMC,GAAyC,CAC7C,KAAM,WACN,KAAM,WACN,QAAS,GACT,KAAM,WACN,IAAK,WACL,SAAU,UACZ,EAEMC,GAAQ,UAKd,SAASC,GAAaC,EAAsB,CAC1C,OAAIA,EAAO,GAAYH,GAAY,KAC/BG,EAAO,GAAYH,GAAY,KAC/BG,EAAO,GAAYH,GAAY,QAC/BG,EAAO,GAAYH,GAAY,KAC/BG,EAAO,IAAaH,GAAY,IAC7BA,GAAY,QACrB,CAKA,SAASI,GAAeC,EAAcF,EAAsB,CAC1D,IAAMG,EAAQJ,GAAaC,CAAI,EAC/B,OAAKG,EACE,GAAGA,CAAK,GAAGD,CAAI,GAAGJ,EAAK,GADXI,CAErB,CAMA,SAASE,GAAcC,EAAgD,CACrE,GAAI,CAMF,SAAO,OAAG,KAAK,UAAUA,EALR,CAACC,EAAcC,IAAwB,CACtD,GAAI,OAAOA,GAAM,SAAU,OAAOA,EAClC,IAAMC,EAAI,OAAOD,CAAC,EAClB,OAAO,OAAO,cAAcC,CAAC,EAAIA,EAAID,EAAE,SAAS,CAClD,CACwC,CAAC,CAC3C,MAAQ,CACN,SAAO,QAAI,iBAAiB,CAC9B,CACF,CAKA,SAASE,GAAeJ,EAAwB,CAC9C,IAAMK,EAASN,GAAcC,CAAK,EAClC,OAAOK,EAAO,GAAKA,EAAO,MAAQ,kBACpC,CAMA,IAAMC,GAAc,mDASb,SAASC,GAAgBC,EAAkBC,EAAQ,GAAY,CACpE,GAAID,EAAO,SAAW,EAAG,MAAO,GAGhC,IAAME,EAASF,EAAO,MAAM,CAACC,CAAK,EAC5BE,EAAM,KAAK,IAAI,GAAGD,CAAM,EAExBE,EADM,KAAK,IAAI,GAAGF,CAAM,EACVC,GAAO,EAE3B,OAAOD,EACJ,IAAKR,GAAM,CACV,IAAMW,GAAcX,EAAIS,GAAOC,EACzBE,EAAQ,KAAK,MAAMD,GAAcP,GAAY,OAAS,EAAE,EAC9D,OAAOA,GAAYQ,CAAK,CAC1B,CAAC,EACA,KAAK,EAAE,CACZ,CASA,SAASC,GAAOC,EAAaP,EAAuB,CAClD,IAAMQ,EAAaC,EAAUF,CAAG,EAAE,OAC5BG,EAAU,KAAK,IAAI,EAAGV,EAAQQ,CAAU,EAC9C,OAAOD,EAAM,IAAI,OAAOG,CAAO,CACjC,CAKA,SAASC,GAAeX,EAAeY,EAAwB,CAC7D,GAAI,CAACA,EACH,OAAO9B,EAAI,WAAW,OAAOkB,CAAK,EAGpC,IAAMa,EAAY,IAAID,CAAK,IAErBE,EAAkBL,EAAUI,CAAS,EAAE,OACvCE,EAAiBf,EAAQc,EAC/B,GAAIC,EAAiB,EACnB,OAAOjC,EAAI,WAAW,OAAOkB,CAAK,EAGpC,IAAMgB,EAAU,EACVC,EAAWF,EAAiBC,EAElC,OACElC,EAAI,WAAW,OAAOkC,CAAO,EAAIH,EAAY/B,EAAI,WAAW,OAAOmC,CAAQ,CAE/E,CASA,SAASC,GACPC,EACAC,EACAC,EACQ,CACR,IAAMC,EAASH,EAAK,QAAU,UAC1BI,EAAS,SAAKF,EAAO,OAAO,EAC5BE,EAAS,SAAKF,EAAO,KAAK,EAExBG,EAASL,EAAK,aAAe,OAC/BM,EAAI,KAAKC,EAAeP,EAAK,UAAU,CAAC,GAAG,EAC3C,GAEAQ,EAAU,GACVR,EAAK,OAAS,aAAeA,EAAK,SAAS,QAC7CQ,EAAUF,EAAI,0BAAqB,EAC1BN,EAAK,OAAS,aAAeA,EAAK,SAAS,SAAW,GAC/DQ,EAAUF,EAAI,iBAAY,EACjBN,EAAK,OAAS,iBAAmBA,EAAK,SAAS,QACxDQ,EAAUF,EAAI,0BAAqB,EAC1BN,EAAK,OAAS,eAAiBA,EAAK,SAAS,UACtDQ,EAAUF,EAAI,KAAKN,EAAK,QAAQ,OAAO,GAAG,GAG5C,IAAMS,EAAQT,EAAK,QAAU,SAAWA,EAAK,MACzCM,EAAI,WAAW,OAAON,EAAK,KAAK,CAAC,EAAE,EACnC,GAEJ,MAAO,GAAGG,CAAM,IAAIG,EAAIL,CAAK,CAAC,GAAGO,CAAO,GAAGH,CAAM,GAAGI,CAAK,EAC3D,CAKA,SAASC,GACPC,EACAT,EACU,CACV,IAAMU,EAAkB,CAAC,EAGzB,OAAID,EAAM,WACRC,EAAM,KAAKb,GAAoBY,EAAM,UAAW,YAAaT,CAAM,CAAC,EAIlES,EAAM,eACRC,EAAM,KAAKb,GAAoBY,EAAM,cAAe,gBAAiBT,CAAM,CAAC,EAK1EU,EAAM,OAAS,GACjBA,EAAM,KAAKN,EAAI,0HAAsB,CAAC,EAGjCM,CACT,CASO,SAASC,IAA0B,CACxC,MAAO,CACL,KAAM,QACN,aAAc,GAEd,OAAOC,EAAgBC,EAAgC,CACrD,IAAMb,EAAS,CAAE,GAAGc,GAAoB,GAAGD,EAAQ,MAAO,EAEpDlC,EAAQ,KAAK,IAAIkC,EAAQ,eAAiB,GAAI,CAAC,EAC/CE,EAAapC,EAAQ,EAErB+B,EAAkB,CAAC,EAGnBM,EAAeJ,EAAG,KAAK,MAAQ,WAC/BK,EAAcC,GAAKF,CAAY,EAOrC,GANAN,EAAM,KACJ,GAAGjD,EAAI,OAAO,GAAG6B,GAAeX,EAAQ,EAAGsC,CAAW,CAAC,GAAGxD,EAAI,QAAQ,EACxE,EACAiD,EAAM,KAAK,GAAGjD,EAAI,QAAQ,GAAG,IAAI,OAAOkB,EAAQ,CAAC,CAAC,GAAGlB,EAAI,QAAQ,EAAE,EAG/DmD,EAAG,MAAO,CACZ,IAAMO,EAAYX,GAAYI,EAAG,MAAOZ,CAAM,EAC9C,QAAWoB,KAAQD,EACjBT,EAAM,KACJ,GAAGjD,EAAI,QAAQ,KAAKwB,GAAOmC,EAAML,CAAU,CAAC,GAAGtD,EAAI,QAAQ,EAC7D,CAEJ,CAGA,IAAM4D,EAAaC,GAAYV,EAAG,KAAK,SAAUC,EAASb,EAAQ,EAAGY,EAAG,KAAK,EAC7E,QAAWQ,KAAQC,EACjBX,EAAM,KACJ,GAAGjD,EAAI,QAAQ,KAAKwB,GAAOmC,EAAML,CAAU,CAAC,GAAGtD,EAAI,QAAQ,EAC7D,EAMF,GAFAiD,EAAM,KAAK,GAAGjD,EAAI,QAAQ,GAAG,IAAI,OAAOkB,EAAQ,CAAC,CAAC,GAAGlB,EAAI,QAAQ,EAAE,EAE/DmD,EAAG,KAAK,aAAe,QAAaC,EAAQ,YAAa,CAC3D,IAAMU,EACJX,EAAG,KAAK,QAAU,UACd,YACAA,EAAG,KAAK,QAAU,UAChB,YACA,SAEFY,EAAS,GADOC,GAAaF,EAAQX,EAAG,KAAK,MAAOZ,CAAM,CACjC,OAAOK,EAAeO,EAAG,KAAK,UAAU,CAAC,GACxEF,EAAM,KACJ,GAAGjD,EAAI,QAAQ,KAAKwB,GAAOuC,EAAQT,CAAU,CAAC,GAAGtD,EAAI,QAAQ,EAC/D,EACAiD,EAAM,KAAK,GAAGjD,EAAI,QAAQ,GAAG,IAAI,OAAOkB,EAAQ,CAAC,CAAC,GAAGlB,EAAI,QAAQ,EAAE,CACrE,CAEA,OAAAiD,EAAM,KACJ,GAAGjD,EAAI,UAAU,GAAGA,EAAI,WAAW,OAAOkB,EAAQ,CAAC,CAAC,GAAGlB,EAAI,WAAW,EACxE,EAEOiD,EAAM,KAAK;AAAA,CAAI,CACxB,CACF,CACF,CAKA,SAASY,GACPI,EACAb,EACAb,EACA2B,EACAlB,EACU,CACV,IAAMC,EAAkB,CAAC,EAEzB,QAAWkB,KAAQF,EACbG,EAAWD,CAAI,EACjBlB,EAAM,KAAKoB,GAAeF,EAAMf,EAASb,EAAQS,CAAK,CAAC,EAC9CsB,EAAeH,CAAI,EAC5BlB,EAAM,KAAK,GAAGsB,GAAmBJ,EAAMf,EAASb,EAAQ2B,EAAOlB,CAAK,CAAC,EAC5DwB,EAAWL,CAAI,EACxBlB,EAAM,KAAK,GAAGwB,GAAeN,EAAMf,EAASb,EAAQ2B,EAAOlB,CAAK,CAAC,EACxD0B,EAAeP,CAAI,EAC5BlB,EAAM,KAAK,GAAG0B,GAAmBR,EAAMf,EAASb,EAAQ2B,EAAOlB,CAAK,CAAC,EAC5D4B,GAAaT,CAAI,GAC1BlB,EAAM,KAAK4B,GAAiBV,EAAMf,EAASb,CAAM,CAAC,EAItD,OAAOU,CACT,CAKA,SAASoB,GACPF,EACAf,EACAb,EACAS,EACQ,CACR,IAAMR,EAASsC,GAAiBX,EAAK,MAAO5B,CAAM,EAC5CwC,EAAOZ,EAAK,MAAQA,EAAK,KAAO,OAGhCa,EAAW5B,EAEXhD,EAAO4E,EAAS,aAAeA,EAAS,YAC1CA,EAAS,YAAY,KAAK,IAAIb,EAAK,KAAO,EAAE,GAC5Ca,EAAS,YAAY,KAAK,IAAIb,EAAK,MAAQ,EAAE,GAC7Ca,EAAS,YAAY,KAAK,IAAIb,EAAK,EAAE,EACrC,OAGAc,EACA7E,IAAS,OACX6E,EAAc5E,GAAe0E,EAAM3E,CAAI,EAEvC6E,EAAcjB,GAAae,EAAMZ,EAAK,MAAO5B,CAAM,EAGrD,IAAIoB,EAAO,GAAGnB,CAAM,IAAIyC,CAAW,GAQnC,GALI7B,EAAQ,UAAYe,EAAK,KAAOA,EAAK,OACvCR,GAAQhB,EAAI,UAAUwB,EAAK,GAAG,GAAG,GAI/BA,EAAK,QAAU,OAAW,CAC5B,IAAMe,EAAW,OAAOf,EAAK,OAAU,SACnCA,EAAK,MACLtD,GAAesD,EAAK,KAAK,EAAE,MAAM,EAAG,EAAE,EAC1CR,GAAQhB,EAAI,SAASuC,CAAQ,GAAGA,EAAS,QAAU,GAAK,MAAQ,EAAE,GAAG,CACvE,CACA,GAAIf,EAAK,SAAW,QAAaA,EAAK,QAAU,UAAW,CACzD,IAAMgB,EAAY,OAAOhB,EAAK,QAAW,SACrCA,EAAK,OACLtD,GAAesD,EAAK,MAAM,EAAE,MAAM,EAAG,EAAE,EAC3CR,GAAQhB,EAAI,UAAUwC,CAAS,GAAGA,EAAU,QAAU,GAAK,MAAQ,EAAE,GAAG,CAC1E,CAGA,GAAI/B,EAAQ,aAAee,EAAK,aAAe,OAAW,CAExD,IAAMiB,EAAYxC,EAAeuB,EAAK,UAAU,EAC1CkB,EAAgBjF,IAAS,OAC3BC,GAAe,IAAI+E,CAAS,IAAKhF,CAAI,EACrCuC,EAAI,IAAIyC,CAAS,GAAG,EACxBzB,GAAQ,IAAI0B,CAAa,EAC3B,CAGA,GAAIL,EAAS,gBAAkBA,EAAS,cAAe,CACrD,IAAMM,EACJN,EAAS,cAAc,IAAIb,EAAK,KAAO,EAAE,GACzCa,EAAS,cAAc,IAAIb,EAAK,MAAQ,EAAE,GAC1Ca,EAAS,cAAc,IAAIb,EAAK,EAAE,EAChCmB,GAAWA,EAAQ,OAAS,IAC9B3B,GAAQ,IAAIhB,EAAI3B,GAAgBsE,CAAO,CAAC,CAAC,GAE7C,CAQA,GALInB,EAAK,aAAe,QAAaA,EAAK,WAAa,IACrDR,GAAQhB,EAAI,KAAKwB,EAAK,UAAU,IAAIA,EAAK,aAAe,EAAI,QAAU,SAAS,GAAG,GAIhFA,EAAK,SAAU,CACjB,IAAMoB,EAAcpB,EAAK,YAAc,OAAY,IAAIA,EAAK,SAAS,KAAO,GAC5ER,GAAQhB,EAAI,YAAY4C,CAAW,GAAG,CACxC,CAGA,IAAMC,EAAUrB,EAAK,KAAOA,EAAK,GACjC,GAAInB,GAASwC,GAAWxC,EAAM,YAAY,IAAIwC,CAAO,EAAG,CACtD,IAAMC,EAAWzC,EAAM,YAAY,IAAIwC,CAAO,EACxCE,EAAaD,EAAS,QAAU,UAClChD,EAAS,SAAKF,EAAO,OAAO,EAC5BE,EAAS,SAAKF,EAAO,KAAK,EACxBoD,EAAaF,EAAS,aAAe,OACvC9C,EAAI,IAAIC,EAAe6C,EAAS,UAAU,CAAC,EAAE,EAC7C,GACJ9B,GAAQ,IAAI+B,CAAU,GAAGC,CAAU,EACrC,CAEA,OAAOhC,CACT,CAKA,SAASkB,GACPV,EACAf,EACAb,EACQ,CAER,IAAMqD,EAAczB,EAAK,cAAgB,SACrC1B,EAAS,SAAKF,EAAO,OAAO,EAC5B4B,EAAK,cAAgB,SACnB1B,EAAS,SAAKF,EAAO,OAAO,EAC5BE,EAAS,SAAKF,EAAO,KAAK,EAE1BwC,EAAO,UAAUZ,EAAK,SAAS,GAC/Bc,EAAcjB,GAAae,EAAMZ,EAAK,MAAO5B,CAAM,EAGnDsD,EAASlD,EAAI,MAAMwB,EAAK,UAAU,MAAMA,EAAK,SAAS,GAAG,EAE3DR,EAAO,GAAGiC,CAAW,IAAIX,CAAW,IAAIY,CAAM,GAGlD,OAAIzC,EAAQ,aAAee,EAAK,aAAe,SAC7CR,GAAQ,IAAIhB,EAAI,IAAIC,EAAeuB,EAAK,UAAU,CAAC,GAAG,CAAC,IAIrDA,EAAK,uBACPR,GAAQhB,EAAI,iBAAiB,GAI3BwB,EAAK,cAAgB,WACvBR,GAAQhB,EAAI,QAAQwB,EAAK,aAAa,EAAE,GAGnCR,CACT,CAKA,SAASY,GACPJ,EACAf,EACAb,EACA2B,EACAlB,EACU,CACV,IAAMC,EAAkB,CAAC,EACnB6C,EAAS,KAAK,OAAO5B,CAAK,EAG1B1B,EAASsC,GAAiBX,EAAK,MAAO5B,CAAM,EAC5CwC,EAAOZ,EAAK,MAAQ,WACpB4B,EAAO5B,EAAK,OAAS,aAAe,gBAAkB,GAI5D,GAHAlB,EAAM,KAAK,GAAG6C,CAAM,GAAG9F,EAAI,QAAQ,GAAGA,EAAI,OAAO,GAAGA,EAAI,UAAU,IAAIwC,CAAM,IAAIiB,GAAKsB,CAAI,CAAC,GAAGgB,CAAI,EAAE,EAG/F5B,EAAK,SAAS,SAAW,EAE3BlB,EAAM,KAAK,GAAG6C,CAAM,GAAG9F,EAAI,QAAQ,IAAI2C,EAAI,uCAAuC,CAAC,EAAE,EACrFM,EAAM,KAAK,GAAG6C,CAAM,GAAG9F,EAAI,QAAQ,IAAI2C,EAAI,2DAA2D,CAAC,EAAE,MAEzG,SAASqD,EAAI,EAAGA,EAAI7B,EAAK,SAAS,OAAQ6B,IAAK,CAC7C,IAAMC,EAAQ9B,EAAK,SAAS6B,CAAC,EAEvBE,EADSF,IAAM7B,EAAK,SAAS,OAAS,EACpB,GAAG2B,CAAM,GAAG9F,EAAI,QAAQ,IAAIA,EAAI,UAAU,GAAK,GAAG8F,CAAM,GAAG9F,EAAI,QAAQ,IAAIA,EAAI,QAAQ,GAE/G,GAAIoE,EAAW6B,CAAK,EAClBhD,EAAM,KAAK,GAAGiD,CAAM,IAAI7B,GAAe4B,EAAO7C,EAASb,EAAQS,CAAK,CAAC,EAAE,MAClE,CAEL,IAAMmD,EAActC,GAAY,CAACoC,CAAK,EAAG7C,EAASb,EAAQ2B,EAAQ,EAAGlB,CAAK,EAC1E,QAAWW,KAAQwC,EACjBlD,EAAM,KAAK,GAAG6C,CAAM,GAAG9F,EAAI,QAAQ,MAAM2D,CAAI,EAAE,CAEnD,CACF,CAIF,OAAIP,EAAQ,aAAee,EAAK,aAAe,QAC7ClB,EAAM,KAAK,GAAG6C,CAAM,GAAG9F,EAAI,UAAU,GAAGA,EAAI,UAAU,GAAGA,EAAI,UAAU,IAAI2C,EAAI,IAAIC,EAAeuB,EAAK,UAAU,CAAC,GAAG,CAAC,EAAE,EAGnHlB,CACT,CAKA,SAASwB,GACPN,EACAf,EACAb,EACA2B,EACAlB,EACU,CACV,IAAMC,EAAkB,CAAC,EACnB6C,EAAS,KAAK,OAAO5B,CAAK,EAG1B1B,EAASsC,GAAiBX,EAAK,MAAO5B,CAAM,EAC5CwC,EAAOZ,EAAK,MAAQ,OAI1B,GAHAlB,EAAM,KAAK,GAAG6C,CAAM,GAAG9F,EAAI,QAAQ,UAAKwC,CAAM,IAAIiB,GAAKsB,CAAI,CAAC,EAAE,EAG1DZ,EAAK,SAAS,SAAW,EAE3BlB,EAAM,KAAK,GAAG6C,CAAM,GAAG9F,EAAI,QAAQ,IAAI2C,EAAI,uCAAuC,CAAC,EAAE,EACrFM,EAAM,KAAK,GAAG6C,CAAM,GAAG9F,EAAI,QAAQ,IAAI2C,EAAI,2DAA2D,CAAC,EAAE,MAEzG,SAASqD,EAAI,EAAGA,EAAI7B,EAAK,SAAS,OAAQ6B,IAAK,CAC7C,IAAMC,EAAQ9B,EAAK,SAAS6B,CAAC,EAEvBE,EADSF,IAAM7B,EAAK,SAAS,OAAS,EACpB,GAAG2B,CAAM,GAAG9F,EAAI,QAAQ,IAAIA,EAAI,UAAU,GAAK,GAAG8F,CAAM,GAAG9F,EAAI,QAAQ,IAAIA,EAAI,QAAQ,GAIzGoG,EADWjC,EAAK,UAAY8B,EAAM,KAAO9B,EAAK,SACpBxB,EAAI,WAAW,EAAI,GAEnD,GAAIyB,EAAW6B,CAAK,EAClBhD,EAAM,KAAK,GAAGiD,CAAM,IAAI7B,GAAe4B,EAAO7C,EAASb,EAAQS,CAAK,CAAC,GAAGoD,CAAY,EAAE,MACjF,CACL,IAAMD,EAActC,GAAY,CAACoC,CAAK,EAAG7C,EAASb,EAAQ2B,EAAQ,EAAGlB,CAAK,EAC1E,QAAWW,KAAQwC,EACjBlD,EAAM,KAAK,GAAG6C,CAAM,GAAG9F,EAAI,QAAQ,MAAM2D,CAAI,EAAE,CAEnD,CACF,CAIF,OAAIP,EAAQ,aAAee,EAAK,aAAe,QAC7ClB,EAAM,KAAK,GAAG6C,CAAM,GAAG9F,EAAI,UAAU,GAAGA,EAAI,UAAU,GAAGA,EAAI,UAAU,IAAI2C,EAAI,IAAIC,EAAeuB,EAAK,UAAU,CAAC,GAAG,CAAC,EAAE,EAGnHlB,CACT,CAKA,SAAS0B,GACPR,EACAf,EACAb,EACA2B,EACAlB,EACU,CACV,IAAMC,EAAkB,CAAC,EACnB6C,EAAS,KAAK,OAAO5B,CAAK,EAG1B1B,EAASsC,GAAiBX,EAAK,MAAO5B,CAAM,EAC5CwC,EAAOZ,EAAK,MAAQ,WACpBkC,EAAYlC,EAAK,UACnBxB,EAAI,KAAKwB,EAAK,SAAS,GAAG,EAC1B,GACEmC,EAAgBnC,EAAK,gBAAkB,OACzCxB,EAAI,MAAM,OAAOwB,EAAK,aAAa,CAAC,EAAE,EACtC,GACEoC,EAAcpC,EAAK,cAAgB,OACrCxB,EAAI,WAAM,OAAOwB,EAAK,WAAW,CAAC,EAAE,EACpC,GAEJlB,EAAM,KACJ,GAAG6C,CAAM,GAAG9F,EAAI,QAAQ,GAAGA,EAAI,OAAO,GAAGA,EAAI,UAAU,IAAIwC,CAAM,IAAIiB,GAAKsB,CAAI,CAAC,GAAGsB,CAAS,GAAGC,CAAa,GAAGC,CAAW,EAC3H,EAGA,QAASP,EAAI,EAAGA,EAAI7B,EAAK,SAAS,OAAQ6B,IAAK,CAC7C,IAAMQ,EAASrC,EAAK,SAAS6B,CAAC,EAExBE,EADSF,IAAM7B,EAAK,SAAS,OAAS,EAExC,GAAG2B,CAAM,GAAG9F,EAAI,QAAQ,IAAIA,EAAI,UAAU,GAC1C,GAAG8F,CAAM,GAAG9F,EAAI,QAAQ,IAAIA,EAAI,QAAQ,GAGtCyG,EAAeD,EAAO,MAAQ,SAAM,SACpCE,EAAcF,EAAO,MAAQjE,EAAO,QAAUA,EAAO,QACrDoE,EAAclE,EAClB,GAAGgE,CAAY,IAAID,EAAO,KAAK,GAC/BE,CACF,EACME,EAAkBJ,EAAO,UAC3B7D,EAAI,KAAK6D,EAAO,SAAS,GAAG,EAC5B,GAKJ,GAHAvD,EAAM,KAAK,GAAGiD,CAAM,IAAIS,CAAW,GAAGC,CAAe,EAAE,EAGnDJ,EAAO,SAAS,OAAS,EAAG,CAC9B,IAAM5C,EAAaC,GAAY2C,EAAO,SAAUpD,EAASb,EAAQ2B,EAAQ,EAAGlB,CAAK,EACjF,QAAWW,KAAQC,EACjBX,EAAM,KAAK,GAAG6C,CAAM,GAAG9F,EAAI,QAAQ,MAAM2D,CAAI,EAAE,CAEnD,MAAY6C,EAAO,OAEjBvD,EAAM,KACJ,GAAG6C,CAAM,GAAG9F,EAAI,QAAQ,MAAM2C,EAAI,WAAW,CAAC,EAChD,CAEJ,CAGA,OAAIS,EAAQ,aAAee,EAAK,aAAe,QAC7ClB,EAAM,KACJ,GAAG6C,CAAM,GAAG9F,EAAI,UAAU,GAAGA,EAAI,UAAU,GAAGA,EAAI,UAAU,IAAI2C,EAAI,IAAIC,EAAeuB,EAAK,UAAU,CAAC,GAAG,CAAC,EAC7G,EAGKlB,CACT,CGzpBA,IAAA4D,GAAqC,mBC8G9B,SAASC,GAAaC,EAAyB,CACpD,OAAIA,EAAO,GAAY,OACnBA,EAAO,GAAY,OACnBA,EAAO,GAAY,UACnBA,EAAO,GAAY,OACnBA,EAAO,IAAa,MACjB,UACT,CDnFA,SAASC,IAAgC,CACvC,MAAO,CAEL,kFAEA,kFAEA,kFAEA,gFAEA,wGAEA,iFAEA,wGAEA,iFAEA,uFAEA,qFACF,CACF,CAKA,SAASC,IAAuC,CAC9C,MAAO,CAEL,oFACA,oFACA,uFACA,oFACA,mFACA,uFACF,CACF,CAKA,SAASC,GAAaC,EAA0B,CAC9C,MAAO,QAAQA,CAAK,EACtB,CAYA,SAASC,IAAoC,CAC3C,MAAO,CAEL,uFACA,oFACF,CACF,CAMA,SAASC,GAAcC,EAAgD,CACrE,GAAI,CAMF,SAAO,OAAG,KAAK,UAAUA,EALR,CAACC,EAAcC,IAAwB,CACtD,GAAI,OAAOA,GAAM,SAAU,OAAOA,EAClC,IAAMC,EAAI,OAAOD,CAAC,EAClB,OAAO,OAAO,cAAcC,CAAC,EAAIA,EAAID,EAAE,SAAS,CAClD,CACwC,CAAC,CAC3C,MAAQ,CACN,SAAO,QAAI,iBAAiB,CAC9B,CACF,CAKA,SAASE,GAAeJ,EAAwB,CAC9C,IAAMK,EAASN,GAAcC,CAAK,EAClC,OAAOK,EAAO,GAAKA,EAAO,MAAQ,kBACpC,CAMA,SAASC,GACPC,EACAC,EACAC,EACoC,CACpC,IAAIC,EAGJ,GAAIH,EAAM,UAAW,CACnB,IAAMI,EAAS,iBACTC,EAAQL,EAAM,UAAU,QAAU,UAAY,eAAiB,aAC/DM,EAAON,EAAM,UAAU,QAAU,UAAY,SAAM,SACnDO,EAASL,EAAQ,aAAeF,EAAM,UAAU,aAAe,OACjE,IAAIQ,EAAeR,EAAM,UAAU,UAAU,CAAC,GAC9C,GACES,EAAUT,EAAM,UAAU,SAAS,QACrC,sBACAA,EAAM,UAAU,SAAS,SAAW,GAClC,aACA,GAENC,EAAM,KAAK,OAAOG,CAAM,MAAME,CAAI,aAAaG,CAAO,GAAGF,CAAM,SAASF,CAAK,EAAE,EAC/EF,EAAaC,CACf,CAGA,GAAIJ,EAAM,cAAe,CACvB,IAAMI,EAAS,mBACTC,EAAQL,EAAM,cAAc,QAAU,UAAY,eAAiB,aACnEM,EAAON,EAAM,cAAc,QAAU,UAAY,SAAM,SACvDO,EAASL,EAAQ,aAAeF,EAAM,cAAc,aAAe,OACrE,IAAIQ,EAAeR,EAAM,cAAc,UAAU,CAAC,GAClD,GACES,EAAUT,EAAM,cAAc,SAAS,QACzC,sBACA,GAEJC,EAAM,KAAK,OAAOG,CAAM,MAAME,CAAI,iBAAiBG,CAAO,GAAGF,CAAM,SAASF,CAAK,EAAE,EAG/EF,GACFF,EAAM,KAAK,OAAOE,CAAU,QAAQC,CAAM,EAAE,EAE9CD,EAAaC,CACf,CAEA,MAAO,CAAE,WAAAD,CAAW,CACtB,CAMA,IAAIO,GAAc,EACZC,GAAkB,IAAI,IACtBC,GAAc,IAAI,IAExB,SAASC,GAAeC,EAAiB,OAAgB,CACvD,MAAO,GAAGA,CAAM,IAAI,EAAEJ,EAAW,EACnC,CAEA,SAASK,IAAyB,CAChCL,GAAc,EACdC,GAAgB,MAAM,EACtBC,GAAY,MAAM,CACpB,CAgBA,SAASI,EAAkBC,EAAsB,CAC/C,OAAOA,EACJ,QAAQ,KAAM,QAAQ,EACtB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,MAAM,EACpB,KAAK,CACV,CASA,SAASC,GAAmBD,EAAsB,CAChD,OAAOD,EAAkBC,CAAI,EAC1B,QAAQ,aAAc,EAAE,CAC7B,CASO,SAASE,IAA4B,CAC1C,MAAO,CACL,KAAM,UACN,aAAc,GAEd,OAAOC,EAAgBlB,EAAgC,CACrDa,GAAiB,EACjB,IAAMd,EAAkB,CAAC,EAGnBoB,EAAWnB,EAGjBD,EAAM,KAAK,cAAc,EAGzB,IAAIqB,EACAF,EAAG,QAELE,EADmBvB,GAAYqB,EAAG,MAAOnB,EAAOC,CAAO,EAC/B,YAI1B,IAAMqB,EAAU,QAChBtB,EAAM,KAAK,OAAOsB,CAAO,oBAAe,EAGpCD,GACFrB,EAAM,KAAK,OAAOqB,CAAU,QAAQC,CAAO,EAAE,EAI/C,IAAIC,EAAaD,EAGjB,QAAWE,KAASL,EAAG,KAAK,SAAU,CACpC,IAAMtB,EAAS4B,GAAWD,EAAOvB,EAASD,EAAOoB,EAAUD,EAAG,KAAK,EACnEnB,EAAM,KAAK,OAAOuB,CAAU,QAAQ1B,EAAO,OAAO,EAAE,EACpD0B,EAAa1B,EAAO,MACtB,CAIA,GADuB,CAAC,UAAW,QAAS,SAAS,EAClC,SAASsB,EAAG,KAAK,KAAwC,EAAG,CAC7E,IAAMO,EAAQ,SACRC,EACJR,EAAG,KAAK,QAAU,UAAY,SAC1BA,EAAG,KAAK,QAAU,QAAU,SAC1B,SACFS,EACJT,EAAG,KAAK,QAAU,UAAY,OAC1BA,EAAG,KAAK,QAAU,QAAU,SAC1B,YACFU,EAAW,MAAMF,CAAO,IAAIC,CAAQ,MACpCE,EACJX,EAAG,KAAK,QAAU,UAAY,aAC1BA,EAAG,KAAK,QAAU,QAAU,WAC1B,aACRnB,EAAM,KAAK,OAAO0B,CAAK,GAAGG,CAAQ,GAAGC,CAAQ,EAAE,EAC/C9B,EAAM,KAAK,OAAOuB,CAAU,QAAQG,CAAK,EAAE,CAC7C,CAGA,OAAA1B,EAAM,KAAK,EAAE,EACbA,EAAM,KAAK,GAAG+B,GAAoB,CAAC,EAG/BX,EAAS,aACXpB,EAAM,KAAK,GAAGgC,GAA2B,CAAC,EAIxCb,EAAG,OACLnB,EAAM,KAAK,GAAGV,GAAwB,CAAC,EAGlCU,EAAM,KAAK;AAAA,CAAI,CACxB,CACF,CACF,CAaA,SAASyB,GACPQ,EACAhC,EACAD,EACAoB,EACArB,EACc,CACd,GAAImC,EAAWD,CAAI,EACjB,OAAOE,GAAeF,EAAMhC,EAASD,EAAOoB,EAAUrB,CAAK,EACtD,GAAIqC,EAAeH,CAAI,EAC5B,OAAOI,GAAmBJ,EAAMhC,EAASD,EAAOoB,EAAUrB,CAAK,EAC1D,GAAIuC,EAAWL,CAAI,EACxB,OAAOM,GAAeN,EAAMhC,EAASD,EAAOoB,EAAUrB,CAAK,EACtD,GAAIyC,EAAeP,CAAI,EAC5B,OAAOQ,GAAmBR,EAAMhC,EAASD,EAAOoB,EAAUrB,CAAK,EAC1D,GAAI2C,GAAaT,CAAI,EAC1B,OAAOU,GAAiBV,EAAMhC,EAASD,CAAK,EAI9C,IAAM4C,EAAKhC,GAAe,SAAS,EACnC,OAAAZ,EAAM,KAAK,OAAO4C,CAAE,kBAAkB,EAC/B,CAAE,QAASA,EAAI,OAAQA,CAAG,CACnC,CAKA,SAAST,GACPF,EACAhC,EACAD,EACAoB,EACArB,EACc,CAEd,IAAM8C,EAAc5C,EACd6C,EAAiBD,EAAY,gBAAkB,GAC/CE,EAAiBF,EAAY,gBAAkB,GAC/CG,EAAmBH,EAAY,kBAAoB,GAGrDD,EAAKX,EAAK,IACV,QAAQA,EAAK,IAAI,QAAQ,gBAAiB,GAAG,CAAC,GAC9CrB,GAAe,MAAM,EAGzB,GAAID,GAAY,IAAIiC,CAAE,EAAG,CACvB,IAAIK,EAAS,EACb,KAAOtC,GAAY,IAAI,GAAGiC,CAAE,IAAIK,CAAM,EAAE,GACtCA,IAEFL,EAAK,GAAGA,CAAE,IAAIK,CAAM,EACtB,CACAtC,GAAY,IAAIiC,CAAE,EAElB,IAAMM,EAAYjB,EAAK,MAAQA,EAAK,KAAO,OACrCkB,EAAYlD,EAAQ,UAAYgC,EAAK,KAAOA,EAAK,KACnD,GAAGiB,CAAS,KAAKjB,EAAK,GAAG,IACzBiB,EACEE,EAAQrC,EAAkBoC,CAAS,EAGnC7C,EACJL,EAAQ,aAAegC,EAAK,aAAe,OACvC,IAAI1B,EAAe0B,EAAK,UAAU,CAAC,GACnC,GAGFoB,EAAY,GAChB,OAAQpB,EAAK,MAAO,CAClB,IAAK,UACHoB,EAAY,UACZ,MACF,IAAK,QACHA,EAAY,UACZ,MACF,IAAK,SACHA,EAAY,aACZ,MACF,IAAK,UACHA,EAAY,UACZ,MACF,IAAK,UACHA,EAAY,UACZ,KACJ,CAIA,IAAIC,EAAS,GACb,GAAIrB,EAAK,QAAU,OAAW,CAC5B,IAAMsB,EAAW,OAAOtB,EAAK,OAAU,SACnClB,EAAkBkB,EAAK,KAAK,EAC5BlB,EAAkBnB,GAAeqC,EAAK,KAAK,EAAE,MAAM,EAAG,EAAE,CAAC,EAC7DqB,GAAU,UAAUC,CAAQ,EAC9B,CACA,GAAItB,EAAK,SAAW,QAAaA,EAAK,QAAU,UAAW,CACzD,IAAMuB,EAAY,OAAOvB,EAAK,QAAW,SACrClB,EAAkBkB,EAAK,MAAM,EAC7BlB,EAAkBnB,GAAeqC,EAAK,MAAM,EAAE,MAAM,EAAG,EAAE,CAAC,EAC9DqB,GAAU,WAAWE,CAAS,EAChC,CAGA,IAAIC,EAAW,GACTC,EAAUzB,EAAK,KAAOA,EAAK,GACjC,GAAIlC,GAAS2D,GAAW3D,EAAM,YAAY,IAAI2D,CAAO,EAAG,CACtD,IAAMC,EAAW5D,EAAM,YAAY,IAAI2D,CAAO,EACxCE,EAAWD,EAAS,QAAU,UAAY,SAAM,SAChDE,EAAa5D,EAAQ,aAAe0D,EAAS,aAAe,OAC9D,IAAIpD,EAAeoD,EAAS,UAAU,CAAC,GACvC,GACJF,EAAW,MAAMG,CAAQ,QAAQC,CAAU,EAC7C,CAGA,IAAMC,GAAgBT,EAAYD,EAAQE,EAASG,EAAWnD,GAAQ,KAAK,EAIvEyD,EACEC,EAAO5C,GAAU,aAAeA,EAAS,YAC3CA,EAAS,YAAY,KAAK,IAAIa,EAAK,KAAO,EAAE,GAC5Cb,EAAS,YAAY,KAAK,IAAIa,EAAK,MAAQ,EAAE,GAC7Cb,EAAS,YAAY,KAAK,IAAIa,EAAK,EAAE,EACrC,OAEJ,GAAI+B,IAAS,OAAW,CACtB,IAAMC,EAAQC,GAAaF,CAAI,EAC/BD,EAAYI,GAAaF,CAAK,CAChC,MACEF,EAA0B9B,EAAK,MAIjC,IAAImC,EACJ,OAAQnC,EAAK,MAAO,CAClB,IAAK,QAEHmC,EAAQ,MAAMN,CAAY,MAC1B,MACF,IAAK,SAEHM,EAAQ,MAAMN,CAAY,MAC1B,MACF,IAAK,UAEHM,EAAQ,KAAKN,CAAY,KACzB,MACF,QAEEM,EAAQ,KAAKN,CAAY,IAC7B,CAKA,GAHA9D,EAAM,KAAK,OAAO4C,CAAE,GAAGwB,CAAK,MAAML,CAAS,EAAE,EAGzCjB,GAAkBb,EAAK,aAAe,QAAaA,EAAK,WAAa,EAAG,CAC1E,IAAMoC,EAAa,UAAKpC,EAAK,UAAU,QAAQA,EAAK,aAAe,EAAI,IAAM,KAAK,GAClFjC,EAAM,KAAK,OAAO4C,CAAE,UAAUyB,CAAU,MAAMzB,CAAE,EAAE,CACpD,CAGA,GAAIG,GAAkBd,EAAK,QAAU,SAAWA,EAAK,QAAU,OAAW,CACxE,IAAMqC,EAAc,OAAO1B,CAAE,GACvB2B,EAAaxD,EAAkB,OAAOkB,EAAK,KAAK,CAAC,EAAE,MAAM,EAAG,EAAE,EACpEjC,EAAM,KAAK,OAAOsE,CAAW,MAAMC,CAAU,KAAK,EAClDvE,EAAM,KAAK,OAAO4C,CAAE,eAAe0B,CAAW,EAAE,EAChDtE,EAAM,KAAK,aAAasE,CAAW,8BAA8B,CACnE,CAGA,GAAItB,GAAoBf,EAAK,SAAU,CACrC,IAAMuC,EAAgB,MAAM5B,CAAE,GACxB6B,EAAYxC,EAAK,YAAc,OAAY,GAAGA,EAAK,SAAS,KAAO,GACzEjC,EAAM,KAAK,OAAOwE,CAAa,qBAAgBC,CAAS,KAAK,EAC7DzE,EAAM,KAAK,OAAO4C,CAAE,kBAAkB4B,CAAa,EAAE,EACrDxE,EAAM,KAAK,aAAawE,CAAa,8BAA8B,CACrE,CAEA,MAAO,CAAE,QAAS5B,EAAI,OAAQA,CAAG,CACnC,CAKA,SAASP,GACPJ,EACAhC,EACAD,EACAoB,EACArB,EACc,CACd,IAAM2E,EAAa9D,GAAe,UAAU,EACtC+D,EAAS,GAAGD,CAAU,QACtBE,EAAS,GAAGF,CAAU,QACtBG,EAAO5D,GAAmBgB,EAAK,MAAQ,UAAU,EACjD6C,EAAY7C,EAAK,OAAS,aAAe,gBAAkB,GAGjE,GAAIA,EAAK,SAAS,SAAW,EAAG,CAC9B,IAAMW,EAAK8B,EACLtB,EAAQrC,EAAkB,GAAG8D,CAAI,GAAGC,CAAS,EAAE,EAC/CC,EAAO,sCACPzE,EAASL,EAAQ,aAAegC,EAAK,aAAe,OACtD,IAAI1B,EAAe0B,EAAK,UAAU,CAAC,GACnC,GAGJ,OAAAjC,EAAM,KAAK,OAAO4C,CAAE,KAAKQ,CAAK,GAAG9C,CAAM,MAAMyE,CAAI,QAAsB9C,EAAK,KAAM,EAAE,EAC7E,CAAE,QAASW,EAAI,OAAQA,CAAG,CACnC,CAGA5C,EAAM,KAAK,gBAAgB0E,CAAU,KAAKG,CAAI,GAAGC,CAAS,IAAI,EAC9D9E,EAAM,KAAK,kBAAkB,EAG7BA,EAAM,KAAK,OAAO2E,CAAM,iBAAY,EAGpC,IAAMK,EAAyB,CAAC,EAChC,QAAWxD,KAASS,EAAK,SAAU,CACjC,IAAMpC,EAAS4B,GAAWD,EAAOvB,EAASD,EAAOoB,EAAUrB,CAAK,EAChEC,EAAM,KAAK,OAAO2E,CAAM,QAAQ9E,EAAO,OAAO,EAAE,EAChDmF,EAAa,KAAKnF,EAAO,MAAM,CACjC,CAGAG,EAAM,KAAK,OAAO4E,CAAM,iBAAY,EACpC,QAAWK,KAAUD,EACnBhF,EAAM,KAAK,OAAOiF,CAAM,QAAQL,CAAM,EAAE,EAG1C5E,EAAM,KAAK,SAAS,EAGpB,IAAMkF,EAA2BjD,EAAK,MACtC,OAAAjC,EAAM,KAAK,aAAa0E,CAAU,IAAIQ,CAAU,EAAE,EAE3C,CAAE,QAASP,EAAQ,OAAQC,CAAO,CAC3C,CAKA,SAASrC,GACPN,EACAhC,EACAD,EACAoB,EACArB,EACc,CACd,IAAM2E,EAAa9D,GAAe,MAAM,EAClCU,EAAU,GAAGoD,CAAU,SACvBhD,EAAQ,GAAGgD,CAAU,OACrBG,EAAO5D,GAAmBgB,EAAK,MAAQ,MAAM,EAGnD,GAAIA,EAAK,SAAS,SAAW,EAAG,CAC9B,IAAMW,EAAK8B,EACLtB,EAAQrC,EAAkB8D,CAAI,EAC9BE,EAAO,sCACPzE,EAASL,EAAQ,aAAegC,EAAK,aAAe,OACtD,IAAI1B,EAAe0B,EAAK,UAAU,CAAC,GACnC,GAEJ,OAAAjC,EAAM,KAAK,OAAO4C,CAAE,YAAOQ,CAAK,GAAG9C,CAAM,MAAMyE,CAAI,QAAsB9C,EAAK,KAAM,EAAE,EAC/E,CAAE,QAASW,EAAI,OAAQA,CAAG,CACnC,CAGA5C,EAAM,KAAK,gBAAgB0E,CAAU,YAAOG,CAAI,IAAI,EACpD7E,EAAM,KAAK,kBAAkB,EAG7BA,EAAM,KAAK,OAAOsB,CAAO,uBAAgB,EAGzC,IAAM0D,EAA6D,CAAC,EAChEG,EAEJ,QAAW3D,KAASS,EAAK,SAAU,CACjC,IAAMpC,EAAS4B,GAAWD,EAAOvB,EAASD,EAAOoB,EAAUrB,CAAK,EAC1DqF,EAAWnD,EAAK,WAAaT,EAAM,GACzCxB,EAAM,KAAK,OAAOsB,CAAO,QAAQzB,EAAO,OAAO,EAAE,EAE7CuF,IACFD,EAAetF,EAAO,QAExBmF,EAAa,KAAK,CAAE,OAAQnF,EAAO,OAAQ,SAAAuF,CAAS,CAAC,CACvD,CAGApF,EAAM,KAAK,OAAO0B,CAAK,oBAAe,EAGtC,OAAW,CAAE,OAAAuD,EAAQ,SAAAG,CAAS,IAAKJ,EAC7BI,GAAYD,EACdnF,EAAM,KAAK,OAAOiF,CAAM,0BAAmBvD,CAAK,EAAE,EACzCO,EAAK,SAEdjC,EAAM,KAAK,OAAOiF,CAAM,qBAAqBvD,CAAK,EAAE,EAGpD1B,EAAM,KAAK,OAAOiF,CAAM,QAAQvD,CAAK,EAAE,EAI3C1B,EAAM,KAAK,SAAS,EAEpB,IAAMkF,EAA2BjD,EAAK,MACtC,OAAAjC,EAAM,KAAK,aAAa0E,CAAU,IAAIQ,CAAU,EAAE,EAE3C,CAAE,QAAS5D,EAAS,OAAQI,CAAM,CAC3C,CAKA,SAASe,GACPR,EACAhC,EACAD,EACAoB,EACArB,EACc,CAEd,IAAIsF,EAAapD,EAAK,IAClB,YAAYA,EAAK,IAAI,QAAQ,gBAAiB,GAAG,CAAC,GAClDrB,GAAe,UAAU,EAG7B,GAAIF,GAAgB,IAAI2E,CAAU,EAAG,CACnC,IAAIpC,EAAS,EACb,KAAOvC,GAAgB,IAAI,GAAG2E,CAAU,IAAIpC,CAAM,EAAE,GAClDA,IAEFoC,EAAa,GAAGA,CAAU,IAAIpC,CAAM,EACtC,CACAvC,GAAgB,IAAI2E,CAAU,EAG9B,IAAMC,EAAYvE,EAAkBkB,EAAK,WAAa,WAAW,EAC3DsD,EAAgBtD,EAAK,gBAAkB,OACzC,MAAMlB,EAAkB,OAAOkB,EAAK,aAAa,CAAC,EAAE,MAAM,EAAG,EAAE,CAAC,GAChE,GAGEuD,EAAgB,GAAGF,CAAS,GAAGC,CAAa,GAAG,KAAK,EAC1DvF,EAAM,KAAK,OAAOqF,CAAU,KAAKG,CAAa,IAAI,EAGlD,IAAMC,EAA0B,CAAC,EAC7BC,EACEC,EAAgB,IAAI,IAE1B,QAAWC,KAAU3D,EAAK,SAAU,CAElC,IAAI4D,EAAW,GAAGR,CAAU,IAAIO,EAAO,MAAM,QAAQ,gBAAiB,GAAG,CAAC,GAE1E,GAAID,EAAc,IAAIE,CAAQ,EAAG,CAC/B,IAAI5C,EAAS,EACb,KAAO0C,EAAc,IAAI,GAAGE,CAAQ,IAAI5C,CAAM,EAAE,GAC9CA,IAEF4C,EAAW,GAAGA,CAAQ,IAAI5C,CAAM,EAClC,CACA0C,EAAc,IAAIE,CAAQ,EAE1B,IAAMC,EAAkB/E,EAAkB6E,EAAO,KAAK,EAChDG,EAAcH,EAAO,MACvB,GAAGE,CAAe,UAClB,GAAGA,CAAe,WAChBE,EAAcJ,EAAO,MAAQ,aAAe,aAGlD5F,EAAM,KAAK,OAAO6F,CAAQ,KAAKE,CAAW,KAAKC,CAAW,EAAE,EAK5D,IAAMC,EAAYL,EAAO,UACrB,IAAI7E,EAAkB6E,EAAO,SAAS,EAAE,QAAQ,MAAO,EAAE,CAAC,IAC1D,GAIJ,GAHA5F,EAAM,KAAK,OAAOqF,CAAU,OAAOY,CAAS,IAAIJ,CAAQ,EAAE,EAGtDD,EAAO,SAAS,OAAS,EAAG,CAC9B,IAAIM,EAASL,EACb,QAAWrE,KAASoE,EAAO,SAAU,CACnC,IAAM/F,EAAS4B,GAAWD,EAAOvB,EAASD,EAAOoB,EAAUrB,CAAK,EAChEC,EAAM,KAAK,OAAOkG,CAAM,QAAQrG,EAAO,OAAO,EAAE,EAChDqG,EAASrG,EAAO,MAClB,CACA4F,EAAc,KAAKS,CAAM,EACrBN,EAAO,QACTF,EAAoBQ,EAExB,MACET,EAAc,KAAKI,CAAQ,EACvBD,EAAO,QACTF,EAAoBG,EAG1B,CAGA,OAAIH,EACK,CAAE,QAASL,EAAY,OAAQK,CAAkB,EAInD,CAAE,QAASL,EAAY,OAAQA,CAAW,CACnD,CAMA,SAAS1C,GACPV,EACAhC,EACAD,EACc,CACd,IAAM4C,EAAK,UAAUX,EAAK,UAAU,QAAQ,gBAAiB,GAAG,CAAC,IAAIrB,GAAe,EAAE,CAAC,GAGjFuF,EAAS,KAAKlE,EAAK,UAAU,MAAMA,EAAK,SAAS,GAGnDoB,EAAY,GAChB,OAAQpB,EAAK,YAAa,CACxB,IAAK,SACHoB,EAAY,UACZ,MACF,IAAK,SACHA,EAAY,UACZ,MACF,IAAK,QACHA,EAAY,UACZ,KACJ,CAGA,IAAM/C,EACJL,EAAQ,aAAegC,EAAK,aAAe,OACvC,IAAI1B,EAAe0B,EAAK,UAAU,CAAC,GACnC,GAGAmE,EAAenE,EAAK,qBAAuB,kBAAoB,GAG/DmB,EAAQ,GAAGC,CAAS,UAAUtC,EAAkBkB,EAAK,SAAS,CAAC,MAAMkE,CAAM,GAAGC,CAAY,GAAG9F,CAAM,GAGrGyD,EACJ,OAAI9B,EAAK,cAAgB,QACvB8B,EAAY,cACH9B,EAAK,cAAgB,SAC9B8B,EAAY,eAEZA,EAAY,SAId/D,EAAM,KAAK,OAAO4C,CAAE,MAAMQ,CAAK,SAASW,CAAS,EAAE,EAE5C,CAAE,QAASnB,EAAI,OAAQA,CAAG,CACnC,CEpwBA,IAAMyD,EAAQ,CACZ,QAAS,SACT,SAAU,SACV,WAAY,SACZ,YAAa,SACb,WAAY,SACZ,SAAU,SACV,QAAS,SACT,MAAO,SACP,SAAU,SACV,QAAS,SACT,MAAO,SACP,UAAW,SACX,QAAS,QACX,EAMMC,GAAyC,CAC7C,KAAM,WACN,KAAM,WACN,QAAS,GACT,KAAM,WACN,IAAK,WACL,SAAU,UACZ,EAEMC,GAAQ,UAad,SAASC,GAAaC,EAAeC,EAAwB,CAC3D,IAAMC,EAAoB,CAAC,EACrBC,EAAmC,CAAC,EAC1C,QAASC,EAAI,EAAGA,EAAIH,EAAQG,IAC1BF,EAAM,KAAK,MAAMF,CAAK,EAAE,KAAK,GAAG,CAAC,EACjCG,EAAO,KAAK,MAAMH,CAAK,EAAE,KAAK,MAAS,CAAC,EAE1C,MAAO,CAAE,MAAAE,EAAO,OAAAC,EAAQ,MAAAH,EAAO,OAAAC,CAAO,CACxC,CAEA,SAASI,EAAQC,EAAgBC,EAAWH,EAAWI,EAAcC,EAAsB,CACrFF,GAAK,GAAKA,EAAID,EAAO,OAASF,GAAK,GAAKA,EAAIE,EAAO,SACrDA,EAAO,MAAMF,CAAC,EAAEG,CAAC,EAAIC,EACjBC,IAAOH,EAAO,OAAOF,CAAC,EAAEG,CAAC,EAAIE,GAErC,CAEA,SAASC,GAAQJ,EAAgBC,EAAWH,EAAmB,CAC7D,OAAIG,GAAK,GAAKA,EAAID,EAAO,OAASF,GAAK,GAAKA,EAAIE,EAAO,OAC9CA,EAAO,MAAMF,CAAC,EAAEG,CAAC,EAEnB,GACT,CAEA,SAASI,GAAQL,EAAgBC,EAAWH,EAAWJ,EAAeC,EAAsB,CAC1FI,EAAQC,EAAQC,EAAGH,EAAGR,EAAM,OAAO,EACnC,QAASgB,EAAI,EAAGA,EAAIZ,EAAQ,EAAGY,IAAKP,EAAQC,EAAQC,EAAIK,EAAGR,EAAGR,EAAM,UAAU,EAC9ES,EAAQC,EAAQC,EAAIP,EAAQ,EAAGI,EAAGR,EAAM,QAAQ,EAEhD,QAASiB,EAAI,EAAGA,EAAIZ,EAAS,EAAGY,IAC9BR,EAAQC,EAAQC,EAAGH,EAAIS,EAAGjB,EAAM,QAAQ,EACxCS,EAAQC,EAAQC,EAAIP,EAAQ,EAAGI,EAAIS,EAAGjB,EAAM,QAAQ,EAGtDS,EAAQC,EAAQC,EAAGH,EAAIH,EAAS,EAAGL,EAAM,UAAU,EACnD,QAASgB,EAAI,EAAGA,EAAIZ,EAAQ,EAAGY,IAAKP,EAAQC,EAAQC,EAAIK,EAAGR,EAAIH,EAAS,EAAGL,EAAM,UAAU,EAC3FS,EAAQC,EAAQC,EAAIP,EAAQ,EAAGI,EAAIH,EAAS,EAAGL,EAAM,WAAW,CAClE,CAEA,SAASkB,GAASR,EAAgBC,EAAWH,EAAWW,EAAcN,EAAsB,CAC1F,IAAMO,EAAQC,EAAUF,CAAI,EAAE,MAAM,EAAE,EACtC,QAAS,EAAI,EAAG,EAAIC,EAAM,OAAQ,IAChCX,EAAQC,EAAQC,EAAI,EAAGH,EAAGY,EAAM,CAAC,EAAGP,CAAK,CAE7C,CAEA,SAASS,EAAiBZ,EAAgBC,EAAWY,EAAgBC,EAAoB,CACvF,IAAMC,EAAO,KAAK,IAAIF,EAAQC,CAAI,EAC5BE,EAAO,KAAK,IAAIH,EAAQC,CAAI,EAClC,QAAShB,EAAIiB,EAAMjB,GAAKkB,EAAMlB,IAAK,CACjC,IAAMmB,EAAWb,GAAQJ,EAAQC,EAAGH,CAAC,EACjCmB,IAAa3B,EAAM,WACrBS,EAAQC,EAAQC,EAAGH,EAAGR,EAAM,KAAK,GACxB2B,IAAa,KAAOA,IAAa3B,EAAM,WAChDS,EAAQC,EAAQC,EAAGH,EAAGR,EAAM,QAAQ,CAExC,CACF,CAEA,SAAS4B,GAAmBlB,EAAgBF,EAAWqB,EAAgBC,EAAoB,CACzF,IAAMC,EAAO,KAAK,IAAIF,EAAQC,CAAI,EAC5BE,EAAO,KAAK,IAAIH,EAAQC,CAAI,EAClC,QAASnB,EAAIoB,EAAMpB,GAAKqB,EAAMrB,IAAK,CACjC,IAAMgB,EAAWb,GAAQJ,EAAQC,EAAGH,CAAC,EACjCmB,IAAa3B,EAAM,SACrBS,EAAQC,EAAQC,EAAGH,EAAGR,EAAM,KAAK,GACxB2B,IAAa,KAAOA,IAAa3B,EAAM,aAChDS,EAAQC,EAAQC,EAAGH,EAAGR,EAAM,UAAU,CAE1C,CACF,CAEA,SAASiC,GAAUvB,EAAgBC,EAAWH,EAAiB,CAC7DC,EAAQC,EAAQC,EAAGH,EAAGR,EAAM,SAAS,CACvC,CAEA,SAASkC,GAAexB,EAAwB,CAC9C,IAAMyB,EAAkB,CAAC,EACzB,QAAS3B,EAAI,EAAGA,EAAIE,EAAO,OAAQF,IAAK,CACtC,IAAI4B,EAAO,GACX,QAASzB,EAAI,EAAGA,EAAID,EAAO,MAAOC,IAAK,CACrC,IAAME,EAAQH,EAAO,OAAOF,CAAC,EAAEG,CAAC,EAC1BC,EAAOF,EAAO,MAAMF,CAAC,EAAEG,CAAC,EAC1BE,EACFuB,GAAQvB,EAAQD,EAAOV,GAEvBkC,GAAQxB,CAEZ,CACAuB,EAAM,KAAKC,EAAK,QAAQ,CAAC,CAC3B,CACA,KAAOD,EAAM,OAAS,GAAKA,EAAMA,EAAM,OAAS,CAAC,IAAM,IAAIA,EAAM,IAAI,EACrE,OAAOA,EAAM,KAAK;AAAA,CAAI,CACxB,CA+BA,IAAME,EAAgB,GAChBC,EAAc,EACdC,GAAe,EACfC,GAAiB,EAEvB,SAASC,GAAStB,EAAcuB,EAA4B,CAC1D,GAAIvB,EAAK,QAAUuB,EAAU,MAAO,CAACvB,CAAI,EACzC,IAAMwB,EAAQxB,EAAK,MAAM,GAAG,EACtBgB,EAAkB,CAAC,EACrBS,EAAU,GACd,QAAWC,KAAQF,EACZC,EAEMA,EAAQ,OAAS,EAAIC,EAAK,QAAUH,EAC7CE,GAAW,IAAMC,GAEjBV,EAAM,KAAKS,CAAO,EAClBA,EAAUC,GALVD,EAAUC,EASd,GADID,GAAST,EAAM,KAAKS,CAAO,EAC3BT,EAAM,SAAW,EACnB,QAASnB,EAAI,EAAGA,EAAIG,EAAK,OAAQH,GAAK0B,EACpCP,EAAM,KAAKhB,EAAK,MAAMH,EAAGA,EAAI0B,CAAQ,CAAC,EAG1C,OAAOP,CACT,CAEA,SAASW,GAAUC,EAAuB,CACxC,OAAQA,EAAO,CACb,IAAK,UAAW,MAAO,SACvB,IAAK,QAAS,MAAO,SACrB,IAAK,UAAW,MAAO,SACvB,IAAK,UAAW,MAAO,SACvB,IAAK,UACL,IAAK,UAAW,MAAO,SACvB,IAAK,SAAU,MAAO,SACtB,QAAS,MAAO,QAClB,CACF,CAMA,SAASC,GACPC,EACAC,EACAC,EAC8C,CAC9C,IAAMC,EAAgBF,EAAmC,cAAgB,GACnEG,EAAWH,EACXI,EAAU,KAAK,MAAMH,EAAc,CAAC,EACpCI,EAAsB,CAAC,EACzBC,EAAW,EAGf,GAAIJ,EAAc,CAChB,IAAMK,EAAYC,GAAiB,QAAS,QAAS,CAAC,cAAS,EAAG,UAAWJ,EAASE,CAAQ,EAC9FD,EAAM,KAAKE,CAAS,EACpBD,EAAWC,EAAU,QAAUlB,EACjC,CAGA,QAAWoB,KAASV,EAAG,KAAK,SAAU,CACpC,IAAMW,EAAeC,GAAeF,EAAOL,EAASE,EAAUL,EAAc,EAAGD,EAASG,CAAQ,EAChGE,EAAM,KAAKK,EAAa,IAAI,EAC5BJ,EAAWI,EAAa,QAAUrB,EACpC,CAIA,GAAIa,GADmB,CAAC,UAAW,QAAS,SAAS,EAClB,SAASH,EAAG,KAAK,KAAwC,EAAG,CAC7F,IAAMa,EACJb,EAAG,KAAK,QAAU,UAAY,cAC1BA,EAAG,KAAK,QAAU,QAAU,gBAC1B,mBACFc,EAAUL,GAAiB,MAAO,MAAO,CAACI,CAAQ,EAAGb,EAAG,KAAK,MAAOK,EAASE,CAAQ,EAC3FD,EAAM,KAAKQ,CAAO,EAClBP,EAAWO,EAAQ,OACrB,CAEA,MAAO,CAAE,MAAAR,EAAO,YAAaC,EAAW,CAAE,CAC5C,CAEA,SAASE,GACPM,EACAC,EACAC,EACAnB,EACAO,EACA9C,EACY,CACZ,IAAM2D,EAAc,KAAK,IAAI,GAAGD,EAAM,IAAIE,GAAK/C,EAAU+C,CAAC,EAAE,MAAM,CAAC,EAC7DhE,EAAQ,KAAK,IAAIiC,EAAe8B,EAAc7B,EAAc,CAAC,EAC7DjC,EAAS6D,EAAM,OAAS,EACxBvD,EAAI2C,EAAU,KAAK,MAAMlD,EAAQ,CAAC,EACxC,MAAO,CACL,GAAA4D,EACA,KAAAC,EACA,MAAAC,EACA,MAAAnB,EACA,EAAApC,EACA,EAAAH,EACA,MAAAJ,EACA,OAAAC,EACA,QAAAiD,EACA,QAAS9C,EAAIH,EAAS,CACxB,CACF,CAOA,SAASwD,GACPQ,EACAf,EACA/B,EACAmB,EACAQ,EACAG,EACkB,CAClB,GAAIiB,EAAWD,CAAI,EACjB,OAAOE,GAAeF,EAAMf,EAAS/B,EAAQmB,EAAUQ,EAASG,CAAQ,EAE1E,GAAImB,EAAeH,CAAI,GAAKI,EAAWJ,CAAI,EACzC,OAAOK,GAAoBL,EAAMf,EAAS/B,EAAQmB,EAAUQ,EAASG,CAAQ,EAE/E,GAAIsB,EAAeN,CAAI,EACrB,OAAOO,GAAmBP,EAAMf,EAAS/B,EAAQmB,EAAUQ,EAASG,CAAQ,EAE9E,GAAIwB,GAAaR,CAAI,EACnB,OAAOS,GAAiBT,EAAMf,EAAS/B,EAAQmB,EAAUQ,EAASG,CAAQ,EAG5E,IAAM0B,EAAWrB,GAAiBW,EAAK,GAAI,OAAQ,CAAC,GAAG,EAAGA,EAAK,MAAOf,EAAS/B,CAAM,EACrF,MAAO,CAAE,KAAMwD,EAAU,QAASA,EAAS,OAAQ,CACrD,CAEA,SAASR,GACPF,EACAf,EACA/B,EACAmB,EACAQ,EACAG,EACkB,CAClB,IAAM2B,EAAOX,EAAK,MAAQA,EAAK,KAAO,OAChCY,EAASnC,GAAUuB,EAAK,KAAK,EAC7BlC,EAAkB,CAAC,EAGrB+C,EAAY,GAAGD,CAAM,IAAID,CAAI,GAC7B9B,EAAQ,UAAYmB,EAAK,KAAOA,EAAK,OACvCa,GAAa,KAAKb,EAAK,GAAG,KAE5B,IAAMc,EAAa,KAAK,IAAIzC,EAAWJ,EAAc,EAAG,EAAE,EAiB1D,GAhBAH,EAAM,KAAK,GAAGM,GAASyC,EAAWC,CAAU,CAAC,EAGzCjC,EAAQ,aAAemB,EAAK,aAAe,QAC7ClC,EAAM,KAAK,IAAIiD,EAAef,EAAK,UAAU,CAAC,GAAG,EAI/CA,EAAK,YAAcA,EAAK,WAAa,GACvClC,EAAM,KAAK,GAAGkC,EAAK,UAAU,SAAS,EAEpCA,EAAK,UACPlC,EAAM,KAAK,SAAS,EAIlBkB,GAAU,gBAAkBA,EAAS,cAAe,CACtD,IAAMgC,EACJhC,EAAS,cAAc,IAAIgB,EAAK,KAAO,EAAE,GACzChB,EAAS,cAAc,IAAIgB,EAAK,MAAQ,EAAE,GAC1ChB,EAAS,cAAc,IAAIgB,EAAK,EAAE,EAChCgB,GAAWA,EAAQ,OAAS,GAC9BlD,EAAM,KAAKmD,GAAgBD,EAAS,CAAC,CAAC,CAE1C,CAEA,IAAME,EAAa,KAAK,IAAI,GAAGpD,EAAM,IAAIiC,GAAK/C,EAAU+C,CAAC,EAAE,MAAM,CAAC,EAC5DhE,EAAQ,KAAK,IAAIiC,EAAekD,EAAajD,EAAc,CAAC,EAC5DjC,EAAS8B,EAAM,OAAS,EACxBxB,EAAI2C,EAAU,KAAK,MAAMlD,EAAQ,CAAC,EAGpCoF,EACJ,GAAInC,GAAU,aAAeA,EAAS,YAAa,CACjD,IAAMoC,EAAYpB,EAAK,KAAOA,EAAK,MAAQA,EAAK,GAChDmB,EAAOnC,EAAS,YAAY,KAAK,IAAIgB,EAAK,EAAE,GAAKhB,EAAS,YAAY,KAAK,IAAIoC,CAAS,CAC1F,CAEA,IAAMC,EAAyB,CAC7B,GAAIrB,EAAK,GACT,KAAM,OACN,MAAOlC,EACP,MAAOkC,EAAK,MACZ,EAAA1D,EACA,EAAGY,EACH,MAAAnB,EACA,OAAAC,EACA,QAAAiD,EACA,QAAS/B,EAASlB,EAAS,EAC3B,SAAUmF,IAAS,OAAY,CAAE,KAAAA,CAAK,EAAI,MAC5C,EAEA,MAAO,CAAE,KAAME,EAAY,QAASA,EAAW,OAAQ,CACzD,CAEA,SAASZ,GACPT,EACAf,EACA/B,EACAmB,EACAQ,EACAyC,EACkB,CAClB,IAAMX,EAAO,UAAUX,EAAK,SAAS,GAC/BY,EAASZ,EAAK,cAAgB,SAAW,SAC3CA,EAAK,cAAgB,SAAW,SAC9B,SACAlC,EAAkB,CAAC,EAGnB+C,EAAY,GAAGD,CAAM,IAAID,CAAI,GAC7BG,EAAa,KAAK,IAAIzC,EAAWJ,EAAc,EAAG,EAAE,EAC1DH,EAAM,KAAK,GAAGM,GAASyC,EAAWC,CAAU,CAAC,EAG7ChD,EAAM,KAAK,KAAKkC,EAAK,UAAU,MAAMA,EAAK,SAAS,EAAE,EAGjDnB,EAAQ,aAAemB,EAAK,aAAe,QAC7ClC,EAAM,KAAK,IAAIiD,EAAef,EAAK,UAAU,CAAC,GAAG,EAI/CA,EAAK,sBACPlC,EAAM,KAAK,cAAc,EAG3B,IAAMoD,EAAa,KAAK,IAAI,GAAGpD,EAAM,IAAIiC,GAAK/C,EAAU+C,CAAC,EAAE,MAAM,CAAC,EAC5DhE,EAAQ,KAAK,IAAIiC,EAAekD,EAAajD,EAAc,CAAC,EAC5DjC,EAAS8B,EAAM,OAAS,EACxBxB,EAAI2C,EAAU,KAAK,MAAMlD,EAAQ,CAAC,EAElCsF,EAAyB,CAC7B,GAAIrB,EAAK,GACT,KAAM,SACN,MAAOlC,EACP,MAAOkC,EAAK,MACZ,EAAA1D,EACA,EAAGY,EACH,MAAAnB,EACA,OAAAC,EACA,QAAAiD,EACA,QAAS/B,EAASlB,EAAS,EAC3B,SAAU,CACR,YAAagE,EAAK,YAClB,qBAAsBA,EAAK,oBAC7B,CACF,EAEA,MAAO,CAAE,KAAMqB,EAAY,QAASA,EAAW,OAAQ,CACzD,CAEA,SAAShB,GACPL,EACAf,EACA/B,EACAmB,EACAQ,EACAG,EACkB,CAClB,IAAMuC,EAASnB,EAAWJ,CAAI,EACxBW,EAAOX,EAAK,OAASuB,EAAS,OAAS,YAEvCC,EAAc,GADLD,EAAS,SAAM,QACD,IAAIZ,CAAI,GAGrC,GAAIX,EAAK,SAAS,SAAW,EAAG,CAC9B,IAAMlC,EAAQ,CAAC0D,EAAa,eAAe,EACrCC,EAASpC,GAAiBW,EAAK,GAAIuB,EAAS,OAAS,WAAYzD,EAAOkC,EAAK,MAAOf,EAAS/B,CAAM,EACzG,MAAO,CAAE,KAAMuE,EAAQ,QAASA,EAAO,OAAQ,CACjD,CAGA,IAAMC,EAAgB,KAAK,OAAOrD,EAAWF,IAAkB6B,EAAK,SAAS,OAAS,IAAMA,EAAK,SAAS,MAAM,EAC1G2B,EAAwB,CAAC,EAE/B,QAAWrC,KAASU,EAAK,SAAU,CACjC,IAAM4B,EAAWC,GAAgBvC,EAAO,KAAK,IAAIoC,EAAe1D,CAAa,EAAGa,EAASG,CAAQ,EACjG2C,EAAY,KAAKC,EAAS,KAAK,CACjC,CAEA,IAAME,EAAkBH,EAAY,OAAO,CAACI,EAAGC,IAAMD,EAAIC,EAAG,CAAC,EAAI7D,IAAkB6B,EAAK,SAAS,OAAS,GAGpGiC,EAAoBH,EAAkBzD,GAAY2B,EAAK,SAAS,OAAS,EAGzEkC,EAAc,KAAK,IAAIlE,EAAewD,EAAY,OAASvD,EAAc,CAAC,EAC1EkE,EAAe,EACfC,EAAUnD,EAAU,KAAK,MAAMiD,EAAc,CAAC,EAEhD/C,EAAWjC,EAGfiC,GAAYgD,EACZhD,GAAY,EACZA,GAAY,EAGZ,IAAMkD,EAAyB,CAAC,EAEhC,GAAIJ,EAEF,QAAStF,EAAI,EAAGA,EAAIqD,EAAK,SAAS,OAAQrD,IAAK,CAC7C,IAAM2C,EAAQU,EAAK,SAASrD,CAAC,EACvB8E,EAASjC,GAAeF,EAAOL,EAASE,EAAUd,EAAUQ,EAASG,CAAQ,EAG/EuC,GAAUnB,EAAWJ,CAAI,GAAKA,EAAK,WAAaV,EAAM,KACxDmC,EAAO,KAAK,SAAW,CAAE,GAAGA,EAAO,KAAK,SAAU,SAAU,EAAK,GAGnEY,EAAS,KAAKZ,EAAO,IAAI,EACzBtC,EAAWsC,EAAO,QAAUvD,EAC9B,KACK,CAEL,IAAIoE,EAASrD,EAAU,KAAK,MAAM6C,EAAkB,CAAC,EAErD,QAASnF,EAAI,EAAGA,EAAIqD,EAAK,SAAS,OAAQrD,IAAK,CAC7C,IAAM2C,EAAQU,EAAK,SAASrD,CAAC,EACvB4F,EAAeD,EAAS,KAAK,MAAMX,EAAYhF,CAAC,EAAI,CAAC,EACrD8E,EAASjC,GAAeF,EAAOiD,EAAcpD,EAAUwC,EAAYhF,CAAC,EAAGkC,EAASG,CAAQ,EAG1FuC,GAAUnB,EAAWJ,CAAI,GAAKA,EAAK,WAAaV,EAAM,KACxDmC,EAAO,KAAK,SAAW,CAAE,GAAGA,EAAO,KAAK,SAAU,SAAU,EAAK,GAGnEY,EAAS,KAAKZ,EAAO,IAAI,EACzBa,GAAUX,EAAYhF,CAAC,EAAIwB,EAC7B,CACF,CAGA,IAAMqE,EAAkB,KAAK,IAAI,GAAGH,EAAS,IAAII,GAAKA,EAAE,OAAO,CAAC,EAK1DC,EADgB,CAACT,GAAqBI,EAAS,OAAS,EAE1DG,EAAkB,EAClBA,EAkBJ,MAAO,CAAE,KAfsB,CAC7B,GAAIxC,EAAK,GACT,KAAMuB,EAAS,OAAS,WACxB,MAAO,CAACC,CAAW,EACnB,MAAOxB,EAAK,MACZ,EAAGoC,EACH,EAAGlF,EACH,MAAOgF,EACP,OAAQC,EACR,QAAAlD,EACA,QAASyD,EACT,SAAAL,EACA,SAAU,CAAE,eAAgBJ,CAAkB,CAChD,EAE2B,QAASS,CAAa,CACnD,CAEA,SAASnC,GACPP,EACAf,EACA/B,EACAmB,EACAQ,EACAG,EACkB,CAClB,IAAM2B,EAAOX,EAAK,MAAQ,WACpB2C,EAAY3C,EAAK,UAAY,KAAKA,EAAK,UAAU,MAAM,EAAG,EAAE,CAAC,IAAM,GACnEwB,EAAc,UAAKb,CAAI,GAAGgC,CAAS,GAGnC7B,EAAa,KAAK,IAAIzC,EAAWJ,EAAc,EAAG,EAAE,EACpD2E,EAAaxE,GAASoD,EAAaV,CAAU,EAC7CI,EAAa,KAAK,IAAI,GAAG0B,EAAW,IAAI7C,GAAK/C,EAAU+C,CAAC,EAAE,MAAM,CAAC,EACjEmC,EAAc,KAAK,IAAIlE,EAAekD,EAAajD,EAAc,CAAC,EAClEkE,EAAeS,EAAW,OAAS,EACnCR,EAAUnD,EAAU,KAAK,MAAMiD,EAAc,CAAC,EAG9CW,EAAc7C,EAAK,SAAS,KAAKgC,GAAKA,EAAE,KAAK,EAC7Cc,EACHD,GAAeA,EAAY,SAAS,OAAS,EAC1CA,EACA7C,EAAK,SAAS,KAAKgC,GAAKA,EAAE,SAAS,OAAS,CAAC,EAEnD,GAAI,CAACc,GAAkBA,EAAe,SAAS,SAAW,EAAG,CAE3D,IAAMrB,EAAqB,CACzB,GAAIzB,EAAK,GACT,KAAM,WACN,MAAO4C,EACP,MAAO5C,EAAK,MACZ,EAAGoC,EACH,EAAGlF,EACH,MAAOgF,EACP,OAAQC,EACR,QAAAlD,EACA,QAAS/B,EAASiF,EAAe,CACnC,EACA,MAAO,CAAE,KAAMV,EAAQ,QAASA,EAAO,OAAQ,CACjD,CAGA,IAAItC,EAAWjC,EAASiF,EAAejE,GACjCmE,EAAyB,CAAC,EAEhC,QAAW/C,KAASwD,EAAe,SAAU,CAC3C,IAAMrB,EAASjC,GAAeF,EAAOL,EAASE,EAAUd,EAAUQ,EAASG,CAAQ,EACnFqD,EAAS,KAAKZ,EAAO,IAAI,EACzBtC,EAAWsC,EAAO,QAAUvD,EAC9B,CAEA,IAAM6E,EAAUV,EAAS,OAAS,EAAIA,EAASA,EAAS,OAAS,CAAC,EAAE,QAAUnF,EAASiF,EAAe,EAgBtG,MAAO,CAAE,KAdsB,CAC7B,GAAInC,EAAK,GACT,KAAM,WACN,MAAO4C,EACP,MAAO5C,EAAK,MACZ,EAAGoC,EACH,EAAGlF,EACH,MAAOgF,EACP,OAAQC,EACR,QAAAlD,EACA,QAAA8D,EACA,SAAAV,CACF,EAE2B,QAAAU,CAAQ,CACrC,CAEA,SAASlB,GACP7B,EACA3B,EACAQ,EACAG,EACmC,CACnC,GAAIiB,EAAWD,CAAI,EAAG,CACpB,IAAMW,EAAOX,EAAK,MAAQA,EAAK,KAAO,OAChCY,EAASnC,GAAUuB,EAAK,KAAK,EAC/BgD,EAAY,EACZnE,EAAQ,aAAemB,EAAK,aAAe,QAAWgD,IACtDhD,EAAK,YAAcA,EAAK,WAAa,GAAGgD,IACxChD,EAAK,UAAUgD,IACfhE,GAAU,iBAAmBA,EAAS,eAAe,IAAIgB,EAAK,KAAO,EAAE,GAAKhB,EAAS,eAAe,IAAIgB,EAAK,MAAQ,EAAE,GAAKhB,EAAS,eAAe,IAAIgB,EAAK,EAAE,IAAIgD,IAEvK,IAAInC,EAAY,GAAGD,CAAM,IAAID,CAAI,GAC7B9B,EAAQ,UAAYmB,EAAK,KAAOA,EAAK,OACvCa,GAAa,KAAKb,EAAK,GAAG,KAE5B,IAAMjE,EAAQ,KAAK,IAAIsC,EAAU,KAAK,IAAIL,EAAe6C,EAAU,OAAS5C,EAAc,CAAC,CAAC,EACtFjC,EAASgH,EAAY,EAC3B,MAAO,CAAE,MAAAjH,EAAO,OAAAC,CAAO,CACzB,CAEA,GAAImE,EAAeH,CAAI,GAAKI,EAAWJ,CAAI,EAAG,CAC5C,GAAIA,EAAK,SAAS,SAAW,EAC3B,MAAO,CAAE,MAAOhC,EAAgB,EAAG,OAAQ,CAAE,EAE/C,IAAM0D,EAAgB,KAAK,MAAMrD,EAAW2B,EAAK,SAAS,MAAM,EAC5DiD,EAAa,EACbC,EAAY,EAChB,QAAW5D,KAASU,EAAK,SAAU,CACjC,IAAMmD,EAAItB,GAAgBvC,EAAOoC,EAAe7C,EAASG,CAAQ,EACjEiE,GAAcE,EAAE,MAChBD,EAAY,KAAK,IAAIA,EAAWC,EAAE,MAAM,CAC1C,CACA,OAAAF,GAAc9E,IAAkB6B,EAAK,SAAS,OAAS,GAChD,CAAE,MAAO,KAAK,IAAIiD,EAAYjF,CAAa,EAAG,OAAQ,EAAQkF,EAAY,CAAE,CACrF,CAEA,GAAI5C,EAAeN,CAAI,EAAG,CACxB,IAAM6C,EAAc7C,EAAK,SAAS,KAAKgC,GAAKA,EAAE,KAAK,EAC/CoB,EAAc,EAClB,GAAIP,EACF,QAAWvD,KAASuD,EAAY,SAAU,CACxC,IAAMM,EAAItB,GAAgBvC,EAAOjB,EAAUQ,EAASG,CAAQ,EAC5DoE,GAAeD,EAAE,OAASjF,EAC5B,CAEF,MAAO,CAAE,MAAO,KAAK,IAAIG,EAAU,EAAE,EAAG,OAAQ,EAAI+E,CAAY,CAClE,CAEA,GAAI5C,GAAaR,CAAI,EAAG,CACtB,IAAMW,EAAO,UAAUX,EAAK,SAAS,GACjCgD,EAAY,EACZnE,EAAQ,aAAemB,EAAK,aAAe,QAAWgD,IACtDhD,EAAK,sBAAsBgD,IAC/B,IAAMjH,EAAQ,KAAK,IAAIsC,EAAU,KAAK,IAAIL,EAAe2C,EAAK,OAAS1C,EAAc,EAAI,CAAC,CAAC,EACrFjC,EAASgH,EAAY,EAC3B,MAAO,CAAE,MAAAjH,EAAO,OAAAC,CAAO,CACzB,CAEA,MAAO,CAAE,MAAOgC,EAAe,OAAQ,CAAE,CAC3C,CAMA,SAASqF,GACPhH,EACA6C,EACAL,EACM,CACN,IAAM3C,EAAS,CAAE,GAAGoH,GAAoB,GAAGzE,EAAQ,MAAO,EAE1D,QAASlC,EAAI,EAAGA,EAAIuC,EAAM,OAAQvC,IAAK,CACrC,IAAMqD,EAAOd,EAAMvC,CAAC,EACd4G,EAAS5G,IAAMuC,EAAM,OAAS,EAMpC,GAHAsE,GAAWnH,EAAQ2D,EAAM9D,CAAM,EAG3B,CAACqH,EAAQ,CACX,IAAME,EAAWvE,EAAMvC,EAAI,CAAC,EACtB+G,EAAQ1D,EAAK,QACb2D,EAAQ3D,EAAK,QAAU,EACvB4D,EAAMH,EAAS,QACfI,EAAMJ,EAAS,EAAI,EAGzBxG,EAAiBZ,EAAQqH,EAAOC,EAAOE,EAAM,CAAC,EAE9CjG,GAAUvB,EAAQuH,EAAKC,CAAG,CAC5B,CACF,CACF,CAEA,SAASL,GACPnH,EACA2D,EACA9D,EACM,CAEN,IAAM4H,EAAkB9D,EAAK,OAAS,QAChC+D,EAAqB/D,EAAK,OAAS,QAAU,CAACA,EAAK,UAAYA,EAAK,SAAS,SAAW,GAE9FtD,GAAQL,EAAQ2D,EAAK,EAAGA,EAAK,EAAGA,EAAK,MAAOA,EAAK,MAAM,EAGnD8D,GACF1H,EAAQC,EAAQ2D,EAAK,QAASA,EAAK,EAAGrE,EAAM,KAAK,GAE/CoI,GAAuB/D,EAAK,UAAYA,EAAK,SAAS,OAAS,IACjE5D,EAAQC,EAAQ2D,EAAK,QAASA,EAAK,EAAIA,EAAK,OAAS,EAAGrE,EAAM,OAAO,EAIvE,IAAMmF,EAAad,EAAK,MAAQ/B,EAAc,EACxCzB,EAAQwH,GAAahE,EAAM9D,CAAM,EAEvC,QAASU,EAAI,EAAGA,EAAIoD,EAAK,MAAM,OAAQpD,IAAK,CAC1C,IAAMmB,EAAOiC,EAAK,MAAMpD,CAAC,EACnBqH,EAAQjE,EAAK,EAAI,EAAI,KAAK,OAAOc,EAAa9D,EAAUe,CAAI,EAAE,QAAU,CAAC,EACzEmG,EAAQlE,EAAK,EAAI,EAAIpD,EAC3BC,GAASR,EAAQ4H,EAAOC,EAAOnG,EAAMvB,CAAK,CAC5C,CAGIwD,EAAK,UAAU,UACjBnD,GAASR,EAAQ2D,EAAK,EAAIA,EAAK,MAAQ,EAAGA,EAAK,EAAG,WAAI,EAIpDA,EAAK,UAAYA,EAAK,SAAS,OAAS,IACtCA,EAAK,OAAS,YAAcA,EAAK,OAAS,OAC5CmE,GAAwB9H,EAAQ2D,EAAM9D,CAAM,EAG5CkI,GAAyB/H,EAAQ2D,EAAM9D,CAAM,EAGnD,CAEA,SAASiI,GACP9H,EACAgI,EACAnI,EACM,CACN,IAAMmG,EAAWgC,EAAO,SACxB,GAAIhC,EAAS,SAAW,EAAG,OAE3B,IAAMiC,EAAQD,EAAO,EAAIA,EAAO,OAC1BE,EAAQF,EAAO,QAKrB,GAF0BA,EAAO,UAAU,iBAAmB,GAEvC,CAGrB,IAAMG,EAAanC,EAAS,CAAC,EAC7BpF,EAAiBZ,EAAQkI,EAAOD,EAAOE,EAAW,EAAI,CAAC,EACvD5G,GAAUvB,EAAQmI,EAAW,QAASA,EAAW,EAAI,CAAC,EAGtD,QAAS7H,EAAI,EAAGA,EAAI0F,EAAS,OAAQ1F,IAAK,CACxC,IAAM2C,EAAQ+C,EAAS1F,CAAC,EAGxB,GAFA6G,GAAWnH,EAAQiD,EAAOpD,CAAM,EAE5BS,EAAI0F,EAAS,OAAS,EAAG,CAC3B,IAAMoC,EAAYpC,EAAS1F,EAAI,CAAC,EAChCM,EAAiBZ,EAAQiD,EAAM,QAASA,EAAM,QAAU,EAAGmF,EAAU,EAAI,CAAC,EAC1E7G,GAAUvB,EAAQoI,EAAU,QAASA,EAAU,EAAI,CAAC,CACtD,CACF,CACA,MACF,CAGA,GAAIpC,EAAS,SAAW,EAEtBpF,EAAiBZ,EAAQkI,EAAOD,EAAOjC,EAAS,CAAC,EAAE,EAAI,CAAC,EACxDzE,GAAUvB,EAAQgG,EAAS,CAAC,EAAE,QAASA,EAAS,CAAC,EAAE,EAAI,CAAC,MACnD,CAEL,IAAMqC,EAAerC,EAAS,IAAII,GAAKA,EAAE,OAAO,EAC1C/E,EAAO,KAAK,IAAI,GAAGgH,CAAY,EAC/B/G,EAAO,KAAK,IAAI,GAAG+G,CAAY,EAGrCzH,EAAiBZ,EAAQkI,EAAOD,EAAOA,EAAQ,CAAC,EAGhD/G,GAAmBlB,EAAQiI,EAAQ,EAAG5G,EAAMC,CAAI,EAGhDvB,EAAQC,EAAQkI,EAAOD,EAAQ,EAAG3I,EAAM,KAAK,EAG7C,QAAW2D,KAAS+C,EAAU,CAC5B,IAAMsC,EAAKrF,EAAM,QACbqF,IAAOjH,EACTtB,EAAQC,EAAQsI,EAAIL,EAAQ,EAAG3I,EAAM,OAAO,EACnCgJ,IAAOhH,EAChBvB,EAAQC,EAAQsI,EAAIL,EAAQ,EAAG3I,EAAM,QAAQ,EACpCgJ,IAAOJ,GAChBnI,EAAQC,EAAQsI,EAAIL,EAAQ,EAAG3I,EAAM,OAAO,EAE9CsB,EAAiBZ,EAAQsI,EAAIL,EAAQ,EAAGhF,EAAM,EAAI,CAAC,EACnD1B,GAAUvB,EAAQsI,EAAIrF,EAAM,EAAI,CAAC,CACnC,CACF,CAGA,QAAWA,KAAS+C,EAClBmB,GAAWnH,EAAQiD,EAAOpD,CAAM,EAIlC,GAAImG,EAAS,OAAS,EAAG,CACvB,IAAMuC,EAAevC,EAAS,IAAII,GAAKA,EAAE,OAAO,EAC1CoC,EAAiB,KAAK,IAAI,GAAGD,CAAY,EACzCE,EAAQD,EAAiB,EAEzBH,EAAerC,EAAS,IAAII,GAAKA,EAAE,OAAO,EAC1C/E,EAAO,KAAK,IAAI,GAAGgH,CAAY,EAC/B/G,EAAO,KAAK,IAAI,GAAG+G,CAAY,EAGrC,QAAWpF,KAAS+C,EACd/C,EAAM,QAAUuF,GAClB5H,EAAiBZ,EAAQiD,EAAM,QAASA,EAAM,QAAU,EAAGwF,EAAQ,CAAC,EAKxEvH,GAAmBlB,EAAQyI,EAAOpH,EAAMC,CAAI,EAG5C,QAAW2B,KAAS+C,EAAU,CAC5B,IAAMsC,EAAKrF,EAAM,QACbqF,IAAOjH,EACTtB,EAAQC,EAAQsI,EAAIG,EAAOnJ,EAAM,UAAU,EAClCgJ,IAAOhH,EAChBvB,EAAQC,EAAQsI,EAAIG,EAAOnJ,EAAM,WAAW,EAE5CS,EAAQC,EAAQsI,EAAIG,EAAOnJ,EAAM,KAAK,CAE1C,CAGAS,EAAQC,EAAQgI,EAAO,QAASS,EAAOnJ,EAAM,OAAO,EAEpDS,EAAQC,EAAQgI,EAAO,QAASS,EAAQ,EAAGnJ,EAAM,QAAQ,CAC3D,CACF,CAEA,SAASyI,GACP/H,EACAgI,EACAnI,EACM,CACN,IAAMmG,EAAWgC,EAAO,SACxB,GAAIhC,EAAS,SAAW,EAAG,OAG3B,IAAMsB,EAAQU,EAAO,EAAIA,EAAO,OAC1BG,EAAanC,EAAS,CAAC,EAC7BpF,EAAiBZ,EAAQgI,EAAO,QAASV,EAAOa,EAAW,EAAI,CAAC,EAChE5G,GAAUvB,EAAQmI,EAAW,QAASA,EAAW,EAAI,CAAC,EAGtD,QAAS,EAAI,EAAG,EAAInC,EAAS,OAAQ,IAAK,CACxC,IAAM/C,EAAQ+C,EAAS,CAAC,EAGxB,GAFAmB,GAAWnH,EAAQiD,EAAOpD,CAAM,EAE5B,EAAImG,EAAS,OAAS,EAAG,CAC3B,IAAMoC,EAAYpC,EAAS,EAAI,CAAC,EAChCpF,EAAiBZ,EAAQiD,EAAM,QAASA,EAAM,QAAU,EAAGmF,EAAU,EAAI,CAAC,EAC1E7G,GAAUvB,EAAQoI,EAAU,QAASA,EAAU,EAAI,CAAC,CACtD,CACF,CACF,CAGA,IAAMM,GAAgB,CACpB,OAAQ,WACR,OAAQ,WACR,MAAO,UACT,EAEA,SAASf,GAAahE,EAAkB9D,EAAoD,CAC1F,GAAI8D,EAAK,UAAU,OAAS,OAAW,CACrC,IAAMgF,EAAQC,GAAajF,EAAK,SAAS,IAAI,EAC7C,OAAOpE,GAAYoJ,CAAK,GAAK,MAC/B,CAEA,OAAIhF,EAAK,OAAS,UAAYA,EAAK,UAAU,YACpC+E,GAAc/E,EAAK,SAAS,WAAW,GAAK,OAE9C9D,EAAO8D,EAAK,KAAK,GAAK,MAC/B,CAMO,SAASkF,IAA8B,CAC5C,MAAO,CACL,KAAM,YACN,aAAc,GAEd,OAAOtG,EAAgBC,EAAgC,CACrD,IAAM9C,EAAQ8C,EAAQ,eAAiB,GACjC,CAAE,MAAAK,EAAO,YAAAiG,CAAY,EAAIxG,GAAeC,EAAIC,EAAS9C,CAAK,EAC1DM,EAASP,GAAaC,EAAOoJ,CAAW,EAC9C,OAAA9B,GAAYhH,EAAQ6C,EAAOL,CAAO,EAC3BhB,GAAexB,CAAM,CAC9B,CACF,CACF,CCj1BA,SAAS+I,GAAUC,EAAqB,CAEtC,OAAOA,EAAI,QAAQ,kBAAmB,EAAE,CAC1C,CAKA,SAASC,GAAaC,EAA+B,CACnD,IAAMC,EAAoB,CAAC,EAE3B,SAASC,EAAKC,EAA4B,CACxC,QAAWC,KAAQD,EACjB,GAAIE,EAAWD,CAAI,EACjBH,EAAM,KAAKG,CAAI,UACNE,GAAeF,CAAI,EAC5BF,EAAKE,EAAK,QAAQ,UACTG,EAAeH,CAAI,GAAKI,EAAWJ,CAAI,EAChDF,EAAKE,EAAK,QAAQ,UACTK,EAAeL,CAAI,EAC5B,QAAWM,KAAUN,EAAK,SACpBM,EAAO,OACTR,EAAKQ,EAAO,QAAQ,CAK9B,CAEA,OAAAR,EAAKF,CAAK,EACHC,CACT,CAKA,SAASU,GAAUC,EAAyB,CAC1C,IAAMC,EAAe,CACnB,GAAID,EAAK,GACT,KAAMA,EAAK,MAAQA,EAAK,KAAOA,EAAK,GACpC,MAAOA,EAAK,KACd,EAEA,OAAIA,EAAK,MAAKC,EAAI,IAAMD,EAAK,KACzBA,EAAK,aAAe,SAAWC,EAAI,WAAaD,EAAK,YACrDA,EAAK,UAAY,SAAWC,EAAI,QAAUD,EAAK,SAC/CA,EAAK,QAAU,SAAWC,EAAI,MAAQD,EAAK,OAC3CA,EAAK,aAAe,QAAaA,EAAK,WAAa,IAAGC,EAAI,WAAaD,EAAK,YAC5EA,EAAK,WACPC,EAAI,SAAW,GACXD,EAAK,YAAc,SAAWC,EAAI,UAAYD,EAAK,YAErDA,EAAK,QAAU,SACjBC,EAAI,MAAQ,OAAOD,EAAK,OAAU,SAAWA,EAAK,MAAQ,OAAOA,EAAK,KAAK,GAIzEA,EAAK,WACHA,EAAK,SAAS,SAAQC,EAAI,OAASD,EAAK,SAAS,QACjDA,EAAK,SAAS,QAAOC,EAAI,MAAQD,EAAK,SAAS,OAC/CA,EAAK,SAAS,SAAQC,EAAI,OAASD,EAAK,SAAS,QACjDA,EAAK,SAAS,OAAO,SAAQC,EAAI,MAAQD,EAAK,SAAS,QAIzDA,EAAK,mBACPC,EAAI,iBAAmB,CACrB,IAAKD,EAAK,iBAAiB,IAC3B,OAAQA,EAAK,iBAAiB,MAChC,EACIA,EAAK,iBAAiB,gBAAgB,WACxCC,EAAI,iBAAiB,SAAWD,EAAK,iBAAiB,eAAe,UAEnEA,EAAK,iBAAiB,gBAAgB,YAAc,SACtDC,EAAI,iBAAiB,UAAYD,EAAK,iBAAiB,eAAe,YAInEC,CACT,CAKA,SAASC,GAAiBb,EAAoC,CAC5D,IAAIc,EAAe,EACfC,EAAa,EACbC,EAAY,EACZC,EAAe,EACfC,EAAe,EACfC,EAEJ,QAAWR,KAAQX,EACbW,EAAK,QAAU,WAAWG,IAC1BH,EAAK,QAAU,SAASI,IACxBJ,EAAK,QAAU,UAAUK,IACzBL,EAAK,QAAU,WAAWM,IAC1BN,EAAK,aAAe,SAAWO,GAAgBP,EAAK,YAEpDA,EAAK,aAAe,SAClB,CAACQ,GAAeR,EAAK,WAAaQ,EAAY,cAChDA,EAAc,CACZ,KAAMR,EAAK,MAAQA,EAAK,KAAOA,EAAK,GACpC,WAAYA,EAAK,UACnB,GAMN,IAAMS,EAAY,IAAI,IACtB,QAAWT,KAAQX,EAAO,CACxB,IAAMqB,EAASV,EAAK,UAAU,OAC9B,GAAIU,IAAWV,EAAK,QAAU,WAAaA,EAAK,QAAU,SAAU,CAClE,IAAMW,EAAQF,EAAU,IAAIC,CAAM,GAAK,CAAE,MAAO,EAAG,OAAQ,EAAG,cAAe,CAAE,EAC/EC,EAAM,QACFX,EAAK,QAAU,SAASW,EAAM,SAC9BX,EAAK,aAAe,SAAWW,EAAM,eAAiBX,EAAK,YAC/DS,EAAU,IAAIC,EAAQC,CAAK,CAC7B,CACF,CAEA,IAAIC,EACJ,GAAIH,EAAU,KAAO,EAAG,CACtBG,EAAW,CAAC,EACZ,OAAW,CAACF,EAAQC,CAAK,IAAKF,EAC5BG,EAASF,CAAM,EAAI,CACjB,MAAOC,EAAM,MACb,OAAQA,EAAM,OACd,cAAeA,EAAM,MAAQ,EAAI,KAAK,MAAMA,EAAM,cAAgBA,EAAM,KAAK,EAAI,CACnF,CAEJ,CAEA,MAAO,CACL,WAAYtB,EAAM,OAClB,aAAAc,EACA,WAAAC,EACA,UAAAC,EACA,aAAAC,EACA,aAAAC,EACA,YAAAC,EACA,GAAII,GAAY,CAAE,SAAAA,CAAS,CAC7B,CACF,CAKA,SAASC,GAAWC,EAA+B,CACjD,IAAMb,EAAe,CAAC,EAqBtB,GAnBIa,EAAM,YACRb,EAAI,UAAY,CACd,OAAQa,EAAM,UAAU,SAAS,OACjC,WAAYA,EAAM,UAAU,UAC9B,EACIA,EAAM,UAAU,QAAU,QAAaA,EAAM,UAAU,QAAU,OACnEb,EAAI,UAAU,MAAQ,OAAOa,EAAM,UAAU,KAAK,IAIlDA,EAAM,gBACRb,EAAI,cAAgB,CAClB,WAAYa,EAAM,cAAc,UAClC,EACIA,EAAM,cAAc,QAAU,QAAaA,EAAM,cAAc,QAAU,OAC3Eb,EAAI,cAAc,MAAQ,OAAOa,EAAM,cAAc,KAAK,IAI1DA,EAAM,YAAY,KAAO,EAAG,CAC9Bb,EAAI,YAAc,CAAC,EACnB,OAAW,CAACc,EAASC,CAAI,IAAKF,EAAM,YAAa,CAC/C,IAAMH,EAAkE,CAAE,QAAAI,CAAQ,EAC9EC,EAAK,aAAe,SAAWL,EAAM,WAAaK,EAAK,YACvDA,EAAK,QAAU,QAAaA,EAAK,QAAU,OAAML,EAAM,MAAQ,OAAOK,EAAK,KAAK,GACpFf,EAAI,YAAY,KAAKU,CAAK,CAC5B,CACF,CAEA,OAAOV,CACT,CAKA,SAASgB,GAAkBC,EAAgBC,EAA4C,CACrF,IAAMC,EAAOF,EAAG,KACV7B,EAAQF,GAAaiC,EAAK,QAAQ,EAClCC,EAAiBF,EAAQ,gBAAkB,GAC3CG,EAAcH,EAAQ,iBAAmB,GAEzCI,EAAuB,CAC3B,SAAU,CACR,GAAIH,EAAK,WACT,KAAMA,EAAK,KACX,MAAOA,EAAK,MACZ,WAAYA,EAAK,WACjB,UAAWA,EAAK,QAChB,YAAaA,EAAK,KACpB,EACA,MAAO/B,EAAM,IAAIU,EAAS,EAC1B,QAASG,GAAiBb,CAAK,CACjC,EAGA,GAAI6B,EAAG,MAAO,CACZ,IAAMM,EAAUX,GAAWK,EAAG,KAAK,EAC/B,OAAO,KAAKM,CAAO,EAAE,OAAS,IAChCD,EAAO,MAAQC,EAEnB,CAGA,GAAIH,EAAgB,CAKlB,IAAII,IAJkBN,EAAQ,eAAiB,WACZ,YAC/BO,GAAkB,EAClBC,GAAc,GACK,OAAOT,EAAIC,CAAO,EACrCG,IACFG,EAAUxC,GAAUwC,CAAO,GAE7BF,EAAO,QAAUE,CACnB,CAEA,OAAOF,CACT,CAkBO,SAASK,IAA2B,CACzC,MAAO,CACL,KAAM,SACN,aAAc,GACd,OAAOV,EAAgBC,EAAgC,CAErD,IAAMI,EAASN,GAAkBC,EADXC,CAC4B,EAClD,OAAO,KAAK,UAAUI,CAAM,CAC9B,CACF,CACF,CC1XA,IAAAM,EAAqC,mBCArC,IAAAC,GAAiB,sBAGXC,GAAY,OAAO,WAAe,KAAe,WAAY,YAAc,OAAQ,WAAoC,QAAW,WAOxI,SAASC,GAAgBC,EAA2B,CAClD,IAAIC,EACJ,GAAIH,GAEFG,EADW,WAAyG,OACzG,KAAKD,CAAK,EAAE,SAAS,QAAQ,MACnC,CACL,IAAIE,EAAS,GACb,QAASC,EAAI,EAAGA,EAAIH,EAAM,OAAQG,IAChCD,GAAU,OAAO,aAAaF,EAAMG,CAAC,CAAC,EAExCF,EAAS,KAAKC,CAAM,CACtB,CACA,OAAOD,EACJ,QAAQ,MAAO,GAAG,EAClB,QAAQ,MAAO,GAAG,EAClB,QAAQ,MAAO,EAAE,CACtB,CAgCO,SAASG,GAAeC,EAAsB,CAGnD,IAAMC,EADc,IAAI,YAAY,EACN,OAAOD,CAAI,EAGnCE,EAAa,GAAAC,QAAK,QAAQF,CAAS,EAGzC,OAAOG,GAAgBF,CAAU,CACnC,CChDA,IAAMG,GAAoB,mBAuBnB,SAASC,GACdC,EACAC,EACAC,EACAC,EAAoD,CAAC,EAC7C,CACR,IAAMC,EAAUD,EAAQ,SAAWL,GAC7BO,EAAUC,GAAeJ,CAAI,EACnC,MAAO,GAAGE,CAAO,IAAIJ,CAAW,IAAIC,CAAM,IAAII,CAAO,EACvD,CC6BA,IAAME,GAA0B,sBASzB,SAASC,GAAoBC,EAAsB,CAExD,MAAO,QADSC,GAAeD,CAAI,CACb,EACxB,CAMA,SAASE,GACPC,EACmB,CAEnB,GAAI,aAAcA,EAAS,CACzB,IAAMC,EAAaD,EACnB,MAAO,CACL,MAAOC,EAAW,aAClB,QAASA,EAAW,WACpB,MAAOA,EAAW,MAClB,IAAKA,EAAW,IAChB,MAAOA,EAAW,MAClB,OAAQA,EAAW,OACnB,MAAOA,EAAW,MAElB,UAAW,KACb,CACF,CAEA,OAAOD,CACT,CAKA,SAASE,GACPC,EACAH,EACQ,CACR,IAAMI,EAAmB,CAAC,EAG1B,OAAIJ,EAAQ,SACVI,EAAO,KAAK,WAAW,mBAAmBJ,EAAQ,OAAO,CAAC,EAAE,EAE1DA,EAAQ,OACVI,EAAO,KAAK,SAASJ,EAAQ,KAAK,EAAE,EAElCA,EAAQ,QAAU,QACpBI,EAAO,KAAK,SAASJ,EAAQ,KAAK,EAAE,EAElCA,EAAQ,SAAW,QACrBI,EAAO,KAAK,UAAUJ,EAAQ,MAAM,EAAE,EAEpCA,EAAQ,QAAU,SAAcA,EAAQ,QAAU,QAAaA,EAAQ,SAAW,SACpFI,EAAO,KAAK,SAASJ,EAAQ,KAAK,EAAE,EAIlCG,IAAW,OAASH,EAAQ,WAAaA,EAAQ,YAAc,QACjEI,EAAO,KAAK,QAAQJ,EAAQ,SAAS,EAAE,EAIrCG,IAAW,QACTH,EAAQ,KACVI,EAAO,KAAK,KAAK,EAEfJ,EAAQ,OAAS,CAACA,EAAQ,KAC5BI,EAAO,KAAK,SAASJ,EAAQ,KAAK,EAAE,EAElCA,EAAQ,WAAa,CAACA,EAAQ,KAChCI,EAAO,KAAK,WAAW,GAIpBA,EAAO,OAAS,EAAI,IAAIA,EAAO,KAAK,GAAG,CAAC,GAAK,EACtD,CA6BO,SAASC,GACdF,EACAN,EACAG,EAAuD,CAAC,EAChD,CACR,IAAMM,EAAaP,GAAiBC,CAAO,EACrCO,EAAUD,EAAW,SAAWX,GAChCa,EAAUZ,GAAoBC,CAAI,EAClCY,EAAcP,GAAiBC,EAAQG,CAAU,EACvD,MAAO,GAAGC,CAAO,IAAIJ,CAAM,IAAIK,CAAO,GAAGC,CAAW,EACtD,CH9KA,SAASC,GACPC,EACAC,EACAC,EAC8B,CAE9B,OAAIF,IAAa,eAAiBC,IAAgB,aAAkB,MAAG,MAAS,EAG5ED,IAAa,SAAWC,IAAgB,UACtCC,IAAW,SACN,OAAI,oBAAoB,KAE1B,MAAG,MAAS,KAId,OAAI,oBAAoB,CACjC,CAMA,SAASC,GACPC,EACqC,CACrC,OAAQA,EAAM,CACZ,IAAK,UACH,MAAO,UACT,IAAK,WACH,MAAO,WACT,IAAK,WACH,MAAO,UACX,CACF,CAMA,SAASC,GAAmBH,EAA6C,CACvE,OAAQA,EAAQ,CACd,IAAK,MACH,MAAO,MACT,IAAK,MACH,MAAO,MACT,IAAK,MACH,MAAO,KACX,CACF,CAwBO,SAASI,GACdC,EACAL,EACAM,EACAC,EAAqB,CAAC,EACU,CAEhC,OAAQF,EAAQ,KAAM,CACpB,IAAK,UACH,MACF,IAAK,WACL,IAAK,WACH,SAAO,OAAI,0BAA0B,EACvC,QAAS,CACP,IAAMG,EAAqBH,EAC3B,SAAO,OAAI,0BAA0B,CACvC,CACF,CAGA,IAAMI,EAAeZ,GAAwBS,EAAQ,SAAUD,EAAQ,KAAML,CAAM,EACnF,GAAI,CAACS,EAAa,GAChB,OAAOA,EAIT,OAAQH,EAAQ,SAAU,CACxB,IAAK,QACH,SAAO,MAAGI,GACRT,GAAmBI,EAAQ,IAAI,EAC/BL,EACAK,EAAQ,OACRC,CACF,CAAC,EACH,IAAK,cACH,SAAO,MAAGK,GACRR,GAAmBH,CAAM,EACzBK,EAAQ,OACRC,CACF,CAAC,EACH,QAAS,CACP,IAAME,EAAqBF,EAC3B,SAAO,OAAI,kBAAkB,CAC/B,CACF,CACF,CI+DO,SAASM,GACdC,EAA6B,CAAC,EACV,CACpB,GAAM,CACJ,aAAAC,EACA,eAAAC,EAAiB,GACjB,YAAAC,EAAc,GACd,SAAAC,EAAW,GACX,OAAQC,EACR,OAAQC,CACV,EAAIN,EAEEO,EAAUC,GAAgB,CAAE,eAAAN,CAAe,CAAC,EAC5CO,EAAiD,IAAI,IACvDC,EAGEC,EAAQC,GAAc,EACtBC,EAAUC,GAAgB,EAC1BC,EAASC,GAAe,EACxBC,EAAYC,GAAkB,EAG9BC,EAA+B,CACnC,YAAAhB,EACA,SAAAC,EACA,cAAe,QAAQ,QAAQ,SAAW,GAC1C,OAAQ,CAAE,GAAGgB,GAAoB,GAAGf,CAAa,CACnD,EAEA,SAASgB,GAAqB,CAC5B,GAAIZ,EAAgB,KAAO,EAAG,CAC5B,IAAMa,EAAKC,EAAM,EACjB,QAAWC,KAAYf,EACrBe,EAASF,CAAE,CAEf,CACF,CAEA,SAASG,EAAYC,EAAqC,CAExD,GAAIA,EAAM,OAAS,eAAiBA,EAAM,OAAS,YAAa,CAC9DC,EAAiBD,CAAwC,EACzD,MACF,CAEAnB,EAAQ,YAAYmB,CAAK,EAErB,iBAAkBA,GAAS,OAAQA,EAAoC,cAAiB,WAC1FhB,EAAiBgB,EAAmC,cAGtDL,EAAa,CACf,CAEA,SAASM,EAAiBD,EAA8C,CACtEnB,EAAQ,iBAAiBmB,CAAK,EAC9BL,EAAa,CACf,CAEA,SAASO,EACPF,EACM,CACNnB,EAAQ,oBAAoBmB,CAAK,EACjCL,EAAa,CACf,CAEA,SAASE,GAAoB,CAC3B,IAAMD,EAAKf,EAAQ,MAAM,EACnBsB,EAAO5B,GAAgBS,GAAiBY,EAAG,KAAK,KACtD,OAAIO,IACFP,EAAG,KAAK,KAAOO,GAEVP,CACT,CAEA,SAASQ,GAAiB,CACxB,IAAMR,EAAKC,EAAM,EACjB,OAAOZ,EAAM,OAAOW,EAAIH,CAAa,CACvC,CAEA,SAASY,EAASC,EAA8B,CAC9C,IAAMV,EAAKC,EAAM,EAEjB,OAAQS,EAAQ,CACd,IAAK,QACH,OAAOrB,EAAM,OAAOW,EAAIH,CAAa,EAEvC,IAAK,UACH,OAAON,EAAQ,OAAOS,EAAIH,CAAa,EAEzC,IAAK,OAAQ,CAEX,IAAMc,GAAcX,EAAG,MACnB,CACE,GAAGA,EACH,MAAO,CACL,GAAGA,EAAG,MACN,YACEA,EAAG,MAAM,uBAAuB,IAC5B,OAAO,YAAYA,EAAG,MAAM,WAAW,EACvCA,EAAG,MAAM,aAAe,CAAC,CACjC,CACF,EACAA,EAIJ,OAAO,KAAK,UAAUW,GAFL,CAACC,GAAcC,KAC9B,OAAOA,IAAU,SAAWA,GAAM,SAAS,EAAIA,GACJ,CAAC,CAChD,CAEA,IAAK,SACH,OAAOpB,EAAO,OAAOO,EAAIH,CAAa,EAExC,IAAK,YACH,OAAOF,EAAU,OAAOK,EAAIH,CAAa,EAE3C,QACE,MAAM,IAAI,MAAM,mBAAmBa,CAAM,EAAE,CAC/C,CACF,CAEA,SAASI,GAAc,CACrB7B,EAAQ,MAAM,EACdc,EAAa,CACf,CAEA,SAASgB,EAASb,EAAgD,CAChE,OAAAf,EAAgB,IAAIe,CAAQ,EACrB,IAAMf,EAAgB,OAAOe,CAAQ,CAC9C,CAMA,SAASc,EACPC,EACAC,EACe,CACf,GAAID,EAAM,OAAOA,EACjB,GAAIjC,GAAc,QAAS,OAAOA,EAAa,QAC/C,MAAM,IAAI,MACR,GAAGkC,CAAU,4IAGf,CACF,CAEA,SAASC,GAAkC,CACzC,IAAMnB,EAAKC,EAAM,EAEjB,MAAO,CAAE,KAAM,UAAW,OADXV,EAAQ,OAAOS,EAAIH,CAAa,CACd,CACnC,CAEA,SAASuB,EAASH,EAA8B,CAC9C,IAAMI,EAASC,GACbH,EAAiB,EACjB,MACAH,EAAqBC,EAAM,UAAU,EACrC,CAAE,OAAQ,UAAW,CACvB,EACA,GAAI,CAACI,EAAO,GACV,MAAM,IAAI,MAAM,6BAA6BA,EAAO,KAAK,EAAE,EAE7D,OAAOA,EAAO,KAChB,CAEA,SAASE,EAASN,EAA8B,CAC9C,IAAMI,EAASC,GACbH,EAAiB,EACjB,MACAH,EAAqBC,EAAM,UAAU,EACrC,CAAE,OAAQ,UAAW,CACvB,EACA,GAAI,CAACI,EAAO,GACV,MAAM,IAAI,MAAM,6BAA6BA,EAAO,KAAK,EAAE,EAE7D,OAAOA,EAAO,KAChB,CAEA,SAASG,EAASP,EAA8B,CAC9C,IAAMI,EAASC,GACbH,EAAiB,EACjB,MACAH,EAAqBC,EAAM,UAAU,EACrC,CAAE,OAAQ,UAAW,CACvB,EACA,GAAI,CAACI,EAAO,GACV,MAAM,IAAI,MAAM,6BAA6BA,EAAO,KAAK,EAAE,EAE7D,OAAOA,EAAO,KAChB,CAEA,SAASI,EAAMf,EAAsBO,EAA8B,CACjE,OAAQP,EAAQ,CACd,IAAK,MACH,OAAOU,EAASH,CAAI,EACtB,IAAK,MACH,OAAOM,EAASN,CAAI,EACtB,IAAK,MACH,OAAOO,EAASP,CAAI,EACtB,QAEE,OAD2BP,CAG/B,CACF,CAEA,MAAO,CACL,YAAAP,EACA,iBAAAE,EACA,oBAAAC,EACA,MAAAL,EACA,OAAAO,EACA,SAAAC,EACA,MAAAK,EACA,SAAAC,EACA,MAAAU,EACA,SAAAL,EACA,SAAAG,EACA,SAAAC,CACF,CACF,CCpPO,SAASE,GAAeC,EAA2B,CAAC,EAAa,CACtE,GAAM,CAAE,UAAAC,EAAY,GAAO,WAAAC,EAAa,GAAI,OAAAC,EAAS,QAAQ,GAAI,EAAIH,EAE/DI,EAAaC,GAAiBL,CAAO,EACrCM,EAAyB,CAAC,EAC5BC,EACAC,EAAoB,EAExB,SAASC,EAAYC,EAA0B,CAE7C,GAAIH,EAGF,IAFAD,EAAQ,KAAKC,CAAU,EAEhBD,EAAQ,OAASJ,GACtBI,EAAQ,MAAM,EAIlBE,EAAoB,KAAK,IAAI,EAC7BD,EAAa,CACX,GAAIG,EACJ,KAAMV,EAAQ,aACd,UAAWQ,EACX,OAAQ,CAAC,CACX,EAEAJ,EAAW,MAAM,CACnB,CAEA,SAASO,EAAcC,EAAkBC,EAAuB,CAC1DN,IACFA,EAAW,QAAU,KAAK,IAAI,EAC9BA,EAAW,WAAaA,EAAW,QAAUA,EAAW,UACxDA,EAAW,QAAUK,EACrBL,EAAW,MAAQM,EAEvB,CAEA,SAASC,EAAYC,EAAqC,CACpDd,GACFE,EAAO,cAAcY,EAAM,IAAI,KAAK,KAAK,UAAUA,CAAK,CAAC,EAAE,EAIzDA,EAAM,OAAS,kBACjBN,EAAYM,EAAM,UAAU,EAI1BR,GACFA,EAAW,OAAO,KAAKQ,CAAK,EAI9BX,EAAW,YAAYW,CAAK,EAGxBA,EAAM,OAAS,mBACjBJ,EAAc,EAAI,EACTI,EAAM,OAAS,kBACxBJ,EAAc,GAAOI,EAAM,KAAK,CAEpC,CAEA,SAASC,EACPD,EACM,CACFd,GACFE,EAAO,cAAcY,EAAM,IAAI,KAAK,KAAK,UAAUA,CAAK,CAAC,EAAE,EAGzDR,GACFA,EAAW,OAAO,KAAKQ,CAAK,EAG9BX,EAAW,oBAAoBW,CAAK,CACtC,CAEA,SAASE,GAAyC,CAChD,OAAOV,CACT,CAEA,SAASW,GAA4B,CACnC,MAAO,CAAC,GAAGZ,CAAO,CACpB,CAEA,SAASa,EAAOC,EAAqC,CACnD,OAAIb,GAAY,KAAOa,EAAWb,EAC3BD,EAAQ,KAAMe,GAAQA,EAAI,KAAOD,CAAE,CAC5C,CAEA,SAASE,EAAKC,EAAgBC,EAAqC,CACjE,IAAMC,EAAON,EAAOI,CAAM,EACpBG,EAAOP,EAAOK,CAAM,EAE1B,GAAI,GAACC,GAAQ,CAACC,GAEd,OAAOC,GAASF,EAAMC,CAAI,CAC5B,CAEA,SAASE,GAAwC,CAC/C,GAAI,CAACrB,GAAcD,EAAQ,SAAW,EAAG,OACzC,IAAMuB,EAAcvB,EAAQA,EAAQ,OAAS,CAAC,EAC9C,OAAOqB,GAASE,EAAatB,CAAU,CACzC,CAEA,SAASuB,GAAiB,CACxB,OAAO1B,EAAW,OAAO,CAC3B,CAEA,SAAS2B,EAASC,EAA8B,CAC9C,OAAO5B,EAAW,SAAS4B,CAAM,CACnC,CAEA,SAASC,GAAwB,CAC/B,OAAO7B,EAAW,SAAS,SAAS,CACtC,CAEA,SAAS8B,GAAyB,CAChC,IAAMC,EAAWC,EAAY,EAC7B,OAAOC,GAAeF,CAAQ,CAChC,CAEA,SAASC,GAA+B,CACtC,OAAK7B,EACE+B,GAAc/B,EAAW,OAAQC,CAAiB,EADjC,CAAC,CAE3B,CAEA,SAAS+B,GAAqB,CAC5BjC,EAAQ,OAAS,CACnB,CAEA,SAASkC,GAAc,CACrBjC,EAAa,OACbH,EAAW,MAAM,CACnB,CAEA,SAASqC,EAAUC,EAAwB,CACzC,IAAMrB,EAAMqB,EAAQvB,EAAOuB,CAAK,EAAInC,EACpC,OAAKc,EACE,KAAK,UAAUA,EAAK,KAAM,CAAC,EADjB,IAEnB,CAEA,SAASsB,EAAUC,EAA2B,CAC5C,IAAMvB,EAAM,KAAK,MAAMuB,CAAI,EAC3B,OAAAtC,EAAQ,KAAKe,CAAG,EACTA,CACT,CAEA,MAAO,CACL,YAAAP,EACA,oBAAAE,EACA,cAAAC,EACA,WAAAC,EACA,OAAAC,EACA,KAAAG,EACA,iBAAAM,EACA,OAAAE,EACA,SAAAC,EACA,cAAAE,EACA,eAAAC,EACA,YAAAE,EACA,aAAAG,EACA,MAAAC,EACA,UAAAC,EACA,UAAAE,CACF,CACF,CAMA,SAAShB,GAASF,EAAmBC,EAA4B,CAC/D,IAAMmB,EAASC,GAAarB,EAAK,MAAM,EACjCsB,EAASD,GAAapB,EAAK,MAAM,EAEjCsB,EAAoB,CAAC,EACrBC,EAAsB,CAAC,EACvBC,EAAsB,CAAC,EACvBC,EAAsB,CAAC,EAG7B,OAAW,CAACC,EAAMC,CAAK,IAAKN,EAAQ,CAClC,IAAMO,EAAQT,EAAO,IAAIO,CAAI,EAExBE,EAEMA,EAAM,SAAWD,EAAM,OAChCH,EAAQ,KAAK,CACX,KAAME,EACN,KAAM,SACN,KAAME,EAAM,OACZ,GAAID,EAAM,MACZ,CAAC,EACQC,EAAM,aAAeD,EAAM,WACpCH,EAAQ,KAAK,CACX,KAAME,EACN,KAAM,WACN,KAAME,EAAM,WACZ,GAAID,EAAM,UACZ,CAAC,EAEDF,EAAU,KAAKC,CAAI,EAhBnBJ,EAAM,KAAK,CAAE,KAAMI,EAAM,KAAM,QAAS,GAAIC,EAAM,MAAO,CAAC,CAkB9D,CAGA,OAAW,CAACD,CAAI,IAAKP,EACdE,EAAO,IAAIK,CAAI,GAClBH,EAAQ,KAAK,CAAE,KAAMG,EAAM,KAAM,UAAW,KAAMP,EAAO,IAAIO,CAAI,GAAG,MAAO,CAAC,EAKhF,IAAIG,EACEC,EAAU/B,EAAK,UAAY,OAAY,UAAYA,EAAK,QAAU,UAAY,QAC9EgC,EAAU/B,EAAK,UAAY,OAAY,UAAYA,EAAK,QAAU,UAAY,QAEhF8B,IAAYC,IACdF,EAAe,CAAE,KAAMC,EAAS,GAAIC,CAAQ,GAI9C,IAAIC,EACJ,OAAIjC,EAAK,aAAe,QAAaC,EAAK,aAAe,SACvDgC,EAAiBhC,EAAK,WAAaD,EAAK,YAGnC,CACL,MAAAuB,EACA,QAAAC,EACA,QAAAC,EACA,UAAAC,EACA,aAAAI,EACA,eAAAG,CACF,CACF,CAUA,SAASZ,GAAaa,EAAmD,CACvE,IAAMC,EAAQ,IAAI,IAElB,QAAW7C,KAAS4C,EAClB,GAAI5C,EAAM,OAAS,aAAc,CAC/B,IAAM8C,EAAI9C,EACJqC,EAAOS,EAAE,MAAQA,EAAE,SAAWA,EAAE,OACtCD,EAAM,IAAIR,EAAM,CACd,KAAAA,EACA,IAAKS,EAAE,QACP,OAAQ,SACV,CAAC,CACH,SAAW9C,EAAM,OAAS,eAAgB,CACxC,IAAM8C,EAAI9C,EACJqC,EAAOS,EAAE,MAAQA,EAAE,SAAWA,EAAE,OAChCC,EAAWF,EAAM,IAAIR,CAAI,EAC3BU,IACFA,EAAS,OAAS,UAClBA,EAAS,WAAaD,EAAE,WAE5B,SAAW9C,EAAM,OAAS,aAAc,CACtC,IAAM8C,EAAI9C,EACJqC,EAAOS,EAAE,MAAQA,EAAE,SAAWA,EAAE,OAChCC,EAAWF,EAAM,IAAIR,CAAI,EAC3BU,IACFA,EAAS,OAAS,QAClBA,EAAS,WAAaD,EAAE,WACxBC,EAAS,MAAQD,EAAE,MAEvB,SAAW9C,EAAM,OAAS,iBAAkB,CAC1C,IAAM8C,EAAI9C,EACJqC,EAAOS,EAAE,MAAQA,EAAE,QACzBD,EAAM,IAAIR,EAAM,CACd,KAAAA,EACA,IAAKS,EAAE,QACP,OAAQ,QACV,CAAC,CACH,SAAW9C,EAAM,OAAS,eAAgB,CACxC,IAAM8C,EAAI9C,EACJqC,EAAOS,EAAE,MAAQA,EAAE,SAAW,UACpCD,EAAM,IAAIR,EAAM,CACd,KAAAA,EACA,IAAKS,EAAE,QACP,OAAQ,SACV,CAAC,CACH,CAGF,OAAOD,CACT,CAMA,SAAStB,GAAcqB,EAA4BI,EAAoC,CACrF,IAAM5B,EAA4B,CAAC,EAC7B6B,EAAa,IAAI,IAEvB,QAAWjD,KAAS4C,EAClB,GAAI5C,EAAM,OAAS,aAAc,CAC/B,IAAM8C,EAAI9C,EACJqC,EAAOS,EAAE,MAAQA,EAAE,SAAWA,EAAE,OACtCG,EAAW,IAAIZ,EAAMS,EAAE,EAAE,EACzB1B,EAAS,KAAK,CACZ,KAAAiB,EACA,IAAKS,EAAE,QACP,QAASA,EAAE,GAAKE,EAChB,OAAQ,SACV,CAAC,CACH,SAAWhD,EAAM,OAAS,eAAgB,CACxC,IAAM8C,EAAI9C,EACJqC,EAAOS,EAAE,MAAQA,EAAE,SAAWA,EAAE,OAChCI,EAAQ9B,EAAS,KAAM+B,GAAMA,EAAE,OAASd,GAAQc,EAAE,SAAW,SAAS,EACxED,IACFA,EAAM,MAAQJ,EAAE,GAAKE,EACrBE,EAAM,WAAaJ,EAAE,WACrBI,EAAM,OAAS,UAEnB,SAAWlD,EAAM,OAAS,aAAc,CACtC,IAAM8C,EAAI9C,EACJqC,EAAOS,EAAE,MAAQA,EAAE,SAAWA,EAAE,OAChCI,EAAQ9B,EAAS,KAAM+B,GAAMA,EAAE,OAASd,GAAQc,EAAE,SAAW,SAAS,EACxED,IACFA,EAAM,MAAQJ,EAAE,GAAKE,EACrBE,EAAM,WAAaJ,EAAE,WACrBI,EAAM,OAAS,QACfA,EAAM,MAAQJ,EAAE,MAEpB,SAAW9C,EAAM,OAAS,iBAAkB,CAC1C,IAAM8C,EAAI9C,EACJqC,EAAOS,EAAE,MAAQA,EAAE,QACzB1B,EAAS,KAAK,CACZ,KAAAiB,EACA,IAAKS,EAAE,QACP,QAASA,EAAE,GAAKE,EAChB,MAAOF,EAAE,GAAKE,EACd,WAAY,EACZ,OAAQ,QACV,CAAC,CACH,SAAWhD,EAAM,OAAS,eAAgB,CACxC,IAAM8C,EAAI9C,EACJqC,EAAOS,EAAE,MAAQA,EAAE,SAAW,UACpC1B,EAAS,KAAK,CACZ,KAAAiB,EACA,IAAKS,EAAE,QACP,QAASA,EAAE,GAAKE,EAChB,MAAOF,EAAE,GAAKE,EACd,WAAY,EACZ,OAAQ,SACV,CAAC,CACH,CAGF,OAAO5B,CACT,CAEA,SAASE,GAAeF,EAAmC,CACzD,GAAIA,EAAS,SAAW,EAAG,MAAO,mBAElC,IAAMgC,EAAkB,CAAC,EACzBA,EAAM,KAAK,WAAW,EACtBA,EAAM,KAAK,SAAI,OAAO,EAAE,CAAC,EAGzB,IAAMC,EAAS,KAAK,IAAI,GAAGjC,EAAS,IAAK+B,GAAMA,EAAE,OAASA,EAAE,QAAU,GAAG,CAAC,EACpEG,EAAW,GAEjB,QAAWJ,KAAS9B,EAAU,CAC5B,IAAMmC,EAAW,KAAK,MAAOL,EAAM,QAAUG,EAAUC,CAAQ,EACzDE,EAAS,KAAK,OAAQN,EAAM,OAASA,EAAM,QAAU,IAAMG,EAAUC,CAAQ,EAC7EG,EAAQ,KAAK,IAAI,EAAGD,EAASD,CAAQ,EAErCG,EAAaC,GAAcT,EAAM,MAAM,EACvCU,EAAM,IAAI,OAAOL,CAAQ,EAAIG,EAAW,OAAOD,CAAK,EAEpDI,EAAWX,EAAM,aAAe,OAAY,GAAGA,EAAM,UAAU,KAAO,IAC5EE,EAAM,KAAK,GAAGF,EAAM,KAAK,OAAO,EAAE,CAAC,KAAKU,EAAI,OAAON,CAAQ,CAAC,KAAKO,CAAQ,EAAE,CAC7E,CAEA,OAAAT,EAAM,KAAK,SAAI,OAAO,EAAE,CAAC,EAClBA,EAAM,KAAK;AAAA,CAAI,CACxB,CAEA,SAASO,GAAcG,EAAyC,CAC9D,OAAQA,EAAQ,CACd,IAAK,UACH,MAAO,SACT,IAAK,QACH,MAAO,SACT,IAAK,UACH,MAAO,SACT,IAAK,SACH,MAAO,SACT,IAAK,UACH,MAAO,OACT,QACE,MAAO,GACX,CACF,CASO,SAASC,GAAWxD,EAAuB,CAChD,IAAM6C,EAAkB,CAAC,EAMzB,GAJI7C,EAAK,cACP6C,EAAM,KAAK,WAAW7C,EAAK,aAAa,IAAI,WAAMA,EAAK,aAAa,EAAE,EAAE,EAGtEA,EAAK,iBAAmB,OAAW,CACrC,IAAMyD,EAAOzD,EAAK,gBAAkB,EAAI,IAAM,GAC9C6C,EAAM,KAAK,aAAaY,CAAI,GAAGzD,EAAK,cAAc,IAAI,CACxD,CAEA,GAAIA,EAAK,MAAM,OAAS,EAAG,CACzB6C,EAAM,KAAK;AAAA,aAAgB,EAC3B,QAAWa,KAAQ1D,EAAK,MACtB6C,EAAM,KAAK,OAAOa,EAAK,IAAI,EAAE,CAEjC,CAEA,GAAI1D,EAAK,QAAQ,OAAS,EAAG,CAC3B6C,EAAM,KAAK;AAAA,eAAkB,EAC7B,QAAWa,KAAQ1D,EAAK,QACtB6C,EAAM,KAAK,OAAOa,EAAK,IAAI,EAAE,CAEjC,CAEA,GAAI1D,EAAK,QAAQ,OAAS,EAAG,CAC3B6C,EAAM,KAAK;AAAA,eAAkB,EAC7B,QAAWa,KAAQ1D,EAAK,QACtB6C,EAAM,KAAK,OAAOa,EAAK,IAAI,KAAKA,EAAK,IAAI,WAAMA,EAAK,EAAE,EAAE,CAE5D,CAEA,OAAI1D,EAAK,UAAU,OAAS,GAC1B6C,EAAM,KAAK;AAAA,aAAgB7C,EAAK,UAAU,MAAM,QAAQ,EAGnD6C,EAAM,KAAK;AAAA,CAAI,CACxB,CASO,SAASc,GACdC,EACAlF,EAA2B,CAAC,EACX,CACjB,IAAMmF,EAAWpF,GAAeC,CAAO,EAEvC,OAAOkF,EAAWC,EAAS,WAAW,EAAE,KAAK,IAAMA,EAAS,OAAO,CAAC,CACtE,CAKO,SAASC,GAAoBpF,EAAiD,CAAC,EAE5E,CACR,GAAM,CAAE,OAAAqF,EAAS,aAAc,OAAAC,EAAS,EAAK,EAAItF,EAE3CuF,EAAWD,EACb,CACE,MAAO,UACP,IAAK,UACL,MAAO,WACP,IAAK,WACL,OAAQ,WACR,KAAM,WACN,KAAM,UACR,EACA,CAAE,MAAO,GAAI,IAAK,GAAI,MAAO,GAAI,IAAK,GAAI,OAAQ,GAAI,KAAM,GAAI,KAAM,EAAG,EAE7E,OAAQvE,GAAkC,CACxC,IAAMyE,EAAY,IAAI,KAAK,EAAE,YAAY,EAAE,MAAM,GAAI,EAAE,EACnDC,EAEJ,OAAQ1E,EAAM,KAAM,CAClB,IAAK,iBACH0E,EAAU,GAAGF,EAAS,IAAI,0BAAqBA,EAAS,KAAK,GAC7D,MACF,IAAK,mBACHE,EAAU,GAAGF,EAAS,KAAK,4BAAuBA,EAAS,KAAK,IAAIA,EAAS,GAAG,IAAIxE,EAAM,UAAU,MAAMwE,EAAS,KAAK,GACxH,MACF,IAAK,iBACHE,EAAU,GAAGF,EAAS,GAAG,yBAAoBA,EAAS,KAAK,GAC3D,MACF,IAAK,aACHE,EAAU,GAAGF,EAAS,IAAI,UAAKxE,EAAM,MAAQA,EAAM,SAAWA,EAAM,MAAM,GAAGwE,EAAS,KAAK,GAC3F,MACF,IAAK,eACHE,EAAU,GAAGF,EAAS,KAAK,UAAKxE,EAAM,MAAQA,EAAM,SAAWA,EAAM,MAAM,GAAGwE,EAAS,KAAK,IAAIA,EAAS,GAAG,IAAIxE,EAAM,UAAU,MAAMwE,EAAS,KAAK,GACpJ,MACF,IAAK,aACHE,EAAU,GAAGF,EAAS,GAAG,UAAKxE,EAAM,MAAQA,EAAM,SAAWA,EAAM,MAAM,GAAGwE,EAAS,KAAK,GAC1F,MACF,IAAK,iBACHE,EAAU,GAAGF,EAAS,MAAM,UAAKxE,EAAM,MAAQA,EAAM,OAAO,YAAYwE,EAAS,KAAK,GACtF,MACF,IAAK,aACHE,EAAU,GAAGF,EAAS,MAAM,UAAKxE,EAAM,MAAQA,EAAM,SAAWA,EAAM,MAAM,UAAUA,EAAM,OAAO,IAAIA,EAAM,WAAW,GAAGwE,EAAS,KAAK,GACzI,MACF,QACEE,EAAU,GAAGF,EAAS,GAAG,GAAGxE,EAAM,IAAI,GAAGwE,EAAS,KAAK,EAC3D,CAEA,QAAQ,IAAI,GAAGA,EAAS,GAAG,GAAGC,CAAS,GAAGD,EAAS,KAAK,IAAIF,CAAM,IAAII,CAAO,EAAE,CACjF,CACF","names":["devtools_entry_exports","__export","createConsoleLogger","createDevtools","quickVisualize","renderDiff","__toCommonJS","formatDuration","ms","minutes","seconds","generateId","hasRealScopeNodes","nodes","node","detectParallelGroups","options","minOverlapMs","maxGapMs","stepsWithTiming","nonStepNodes","i","b","groups","currentGroup","step","groupStart","s","groupEnd","startedTogether","hasTrueOverlap","overlapDuration","groupedNodes","group","position","children","startTs","endTs","parallelNode","deriveGroupState","originalIndex","g","c","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","e","handleDecisionEvent","hookExec","streamKey","stream","handleScopeEvent","scopeIndex","s","nestedScope","nestedNode","deriveState","scope","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","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","getHeatLevel","heat","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","l","node","isStepNode","layoutStepNode","isParallelNode","isRaceNode","layoutBranchingNode","isDecisionNode","layoutDecisionNode","isStreamNode","layoutStreamNode","fallback","name","symbol","mainLabel","innerWidth","formatDuration","history","renderSparkline","labelWidth","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","domainMap","domain","entry","byDomain","hooksToLog","hooks","stepKey","hook","buildLoggerOutput","ir","options","root","includeDiagram","stripColors","output","hookLog","diagram","flowchartRenderer","asciiRenderer","loggerRenderer","import_awaitly","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","DEFAULT_MERMAID_INK_URL","encodeForMermaidInk","text","encodeForKroki","normalizeOptions","options","exportOpts","buildQueryString","format","params","buildMermaidInkUrl","normalized","baseUrl","encoded","queryString","validateFormatSupported","provider","diagramKind","format","toKrokiDiagramType","kind","toMermaidInkFormat","toExportUrl","diagram","options","ctx","_exhaustive","formatResult","buildKrokiUrl","buildMermaidInkUrl","createVisualizer","options","workflowName","detectParallel","showTimings","showKeys","customColors","exportConfig","builder","createIRBuilder","updateCallbacks","nameFromEvent","ascii","asciiRenderer","mermaid","mermaidRenderer","logger","loggerRenderer","flowchart","flowchartRenderer","renderOptions","defaultColorScheme","notifyUpdate","ir","getIR","callback","handleEvent","event","handleScopeEvent","handleDecisionEvent","name","render","renderAs","format","toSerialize","_key","value","reset","onUpdate","resolveExportOptions","opts","methodName","getDiagramSource","toSvgUrl","result","toExportUrl","toPngUrl","toPdfUrl","toUrl","createDevtools","options","logEvents","maxHistory","logger","visualizer","createVisualizer","history","currentRun","workflowStartTime","startNewRun","workflowId","endCurrentRun","success","error","handleEvent","event","handleDecisionEvent","getCurrentRun","getHistory","getRun","id","run","diff","runId1","runId2","run1","run2","diffRuns","diffWithPrevious","previousRun","render","renderAs","format","renderMermaid","renderTimeline","timeline","getTimeline","formatTimeline","buildTimeline","clearHistory","reset","exportRun","runId","importRun","json","steps1","extractSteps","steps2","added","removed","changed","unchanged","name","step2","step1","statusChange","status1","status2","durationChange","events","steps","e","existing","startTime","stepStarts","entry","t","lines","maxEnd","barWidth","startPos","endPos","width","statusChar","getStatusChar","bar","duration","status","renderDiff","sign","step","quickVisualize","workflowFn","devtools","createConsoleLogger","prefix","colors","colorize","timestamp","message"]}