@jagreehal/workflow 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core.ts","../src/workflow.ts","../src/conditional.ts","../src/circuit-breaker.ts","../src/saga.ts","../src/rate-limiter.ts","../src/versioning.ts","../src/autotel.ts","../src/webhook.ts","../src/policies.ts","../src/persistence.ts","../src/visualize/utils/timing.ts","../src/visualize/parallel-detector.ts","../src/visualize/ir-builder.ts","../src/visualize/types.ts","../src/visualize/renderers/colors.ts","../src/visualize/renderers/ascii.ts","../src/visualize/renderers/mermaid.ts","../src/visualize/index.ts","../src/devtools.ts","../src/hitl.ts","../src/testing.ts"],"sourcesContent":["/**\n * @jagreehal/workflow/core\n *\n * Core Result primitives and run() function.\n * Use this module for minimal bundle size when you don't need the full workflow capabilities\n * (like retries, timeout, or state persistence) provided by `createWorkflow`.\n *\n * This module provides:\n * 1. `Result` types for error handling without try/catch\n * 2. `run()` function for executing steps with standardized error management\n * 3. Utilities for transforming and combining Results\n */\n\n// =============================================================================\n// Core Result Types\n// =============================================================================\n\n/**\n * Represents a successful computation or a failed one.\n * Use this type to represent the outcome of an operation that might fail,\n * instead of throwing exceptions.\n *\n * @template T - The type of the success value\n * @template E - The type of the error value (defaults to unknown)\n * @template C - The type of the cause (defaults to unknown)\n */\nexport type Result<T, E = unknown, C = unknown> =\n | { ok: true; value: T }\n | { ok: false; error: E; cause?: C };\n\n/**\n * A Promise that resolves to a Result.\n * Use this for asynchronous operations that might fail.\n */\nexport type AsyncResult<T, E = unknown, C = unknown> = Promise<Result<T, E, C>>;\n\nexport type UnexpectedStepFailureCause =\n | {\n type: \"STEP_FAILURE\";\n origin: \"result\";\n error: unknown;\n cause?: unknown;\n }\n | {\n type: \"STEP_FAILURE\";\n origin: \"throw\";\n error: unknown;\n thrown: unknown;\n };\n\nexport type UnexpectedCause =\n | { type: \"UNCAUGHT_EXCEPTION\"; thrown: unknown }\n | UnexpectedStepFailureCause;\n\nexport type UnexpectedError = {\n type: \"UNEXPECTED_ERROR\";\n cause: UnexpectedCause;\n};\nexport type PromiseRejectedError = { type: \"PROMISE_REJECTED\"; cause: unknown };\n/** Cause type for promise rejections in async batch helpers */\nexport type PromiseRejectionCause = { type: \"PROMISE_REJECTION\"; reason: unknown };\nexport type EmptyInputError = { type: \"EMPTY_INPUT\"; message: string };\nexport type MaybeAsyncResult<T, E, C = unknown> = Result<T, E, C> | Promise<Result<T, E, C>>;\n\n// =============================================================================\n// Result Constructors\n// =============================================================================\n\n/**\n * Creates a successful Result.\n * Use this when an operation completes successfully.\n *\n * @param value - The success value to wrap\n * @returns A Result object with `{ ok: true, value }`\n *\n * @example\n * ```typescript\n * function divide(a: number, b: number): Result<number, string> {\n * if (b === 0) return err(\"Division by zero\");\n * return ok(a / b);\n * }\n * ```\n */\nexport const ok = <T>(value: T): Result<T, never, never> => ({ ok: true, value });\n\n/**\n * Creates a failed Result.\n * Use this when an operation fails.\n *\n * @param error - The error value describing what went wrong (e.g., error code, object)\n * @param options - Optional context about the failure\n * @param options.cause - The underlying cause of the error (e.g., a caught exception)\n * @returns A Result object with `{ ok: false, error }` (and optional cause)\n *\n * @example\n * ```typescript\n * // Simple error\n * const r1 = err(\"NOT_FOUND\");\n *\n * // Error with cause (useful for wrapping exceptions)\n * try {\n * // ... unsafe code\n * } catch (e) {\n * return err(\"PROCESSING_FAILED\", { cause: e });\n * }\n * ```\n */\nexport const err = <E, C = unknown>(\n error: E,\n options?: { cause?: C }\n): Result<never, E, C> => ({\n ok: false,\n error,\n ...(options?.cause !== undefined ? { cause: options.cause } : {}),\n});\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Checks if a Result is successful.\n * Use this to narrow the type of a Result to the success case.\n *\n * @param r - The Result to check\n * @returns `true` if successful, allowing access to `r.value`\n *\n * @example\n * ```typescript\n * const r = someOperation();\n * if (isOk(r)) {\n * // Use r.value (Type is T)\n * processValue(r.value);\n * } else {\n * // Handle r.error (Type is E)\n * handleError(r.error);\n * }\n * ```\n */\nexport const isOk = <T, E, C>(r: Result<T, E, C>): r is { ok: true; value: T } =>\n r.ok;\n\n/**\n * Checks if a Result is a failure.\n * Use this to narrow the type of a Result to the error case.\n *\n * @param r - The Result to check\n * @returns `true` if failed, allowing access to `r.error` and `r.cause`\n *\n * @example\n * ```typescript\n * if (isErr(r)) {\n * // Handle error case early\n * return;\n * }\n * // Proceed with success case\n * ```\n */\nexport const isErr = <T, E, C>(\n r: Result<T, E, C>\n): r is { ok: false; error: E; cause?: C } => !r.ok;\n\n/**\n * Checks if an error is an UnexpectedError.\n * Used internally by the framework but exported for advanced custom handling.\n * Indicates an error that wasn't typed/expected in the `run` signature.\n */\nexport const isUnexpectedError = (e: unknown): e is UnexpectedError =>\n typeof e === \"object\" &&\n e !== null &&\n (e as UnexpectedError).type === \"UNEXPECTED_ERROR\";\n\n// =============================================================================\n// Type Utilities\n// =============================================================================\n\ntype AnyFunction = (...args: never[]) => unknown;\n\n/**\n * Helper to extract the error type from Result or AsyncResult return values.\n * Works even when a function is declared to return a union of both forms.\n */\ntype ErrorOfReturn<R> = Extract<Awaited<R>, { ok: false }> extends { error: infer E }\n ? E\n : never;\n\n/**\n * Extract error type from a single function's return type\n */\nexport type ErrorOf<T extends AnyFunction> = ErrorOfReturn<ReturnType<T>>;\n\n/**\n * Extract union of error types from multiple functions\n */\nexport type Errors<T extends AnyFunction[]> = {\n [K in keyof T]: ErrorOf<T[K]>;\n}[number];\n\n/**\n * Extract value type from Result\n */\nexport type ExtractValue<T> = T extends { ok: true; value: infer U }\n ? U\n : never;\n\n/**\n * Extract error type from Result\n */\nexport type ExtractError<T> = T extends { ok: false; error: infer E }\n ? E\n : never;\n\n/**\n * Extract cause type from Result\n */\nexport type ExtractCause<T> = T extends { ok: false; cause?: infer C }\n ? C\n : never;\n\n/**\n * Helper to extract the cause type from Result or AsyncResult return values.\n * Works even when a function is declared to return a union of both forms.\n */\ntype CauseOfReturn<R> = Extract<Awaited<R>, { ok: false }> extends { cause?: infer C }\n ? C\n : never;\n\n/**\n * Extract cause type from a function's return type\n */\nexport type CauseOf<T extends AnyFunction> = CauseOfReturn<ReturnType<T>>;\n\n// =============================================================================\n// Step Options\n// =============================================================================\n\n/**\n * Options for configuring a step within a workflow.\n * Use these to enable tracing, caching, and state persistence.\n */\nexport type StepOptions = {\n /**\n * Human-readable label for the step.\n * Used in logs, traces, and error messages.\n * Highly recommended for debugging complex workflows.\n */\n name?: string;\n\n /**\n * Stable identity key for the step.\n * REQUIRED for:\n * 1. Caching: Used as the cache key.\n * 2. Resuming: Used to identify which steps have already completed.\n *\n * Must be unique within the workflow.\n */\n key?: string;\n\n /**\n * Retry configuration for transient failures.\n * When specified, the step will retry on errors according to this config.\n */\n retry?: RetryOptions;\n\n /**\n * Timeout configuration for the operation.\n * When specified, each attempt will be aborted after the timeout duration.\n */\n timeout?: TimeoutOptions;\n};\n\n// =============================================================================\n// Retry and Timeout Types\n// =============================================================================\n\n/**\n * Backoff strategy for retry operations.\n */\nexport type BackoffStrategy = \"fixed\" | \"linear\" | \"exponential\";\n\n/**\n * Configuration for step retry behavior.\n */\nexport type RetryOptions = {\n /**\n * Total number of attempts (1 = no retry, 3 = initial + 2 retries).\n * Must be >= 1.\n */\n attempts: number;\n\n /**\n * Backoff strategy between retries.\n * - 'fixed': Same delay each time (initialDelay)\n * - 'linear': Delay increases linearly (initialDelay * attempt)\n * - 'exponential': Delay doubles each time (initialDelay * 2^(attempt-1))\n * @default 'exponential'\n */\n backoff?: BackoffStrategy;\n\n /**\n * Initial delay in milliseconds before first retry.\n * @default 100\n */\n initialDelay?: number;\n\n /**\n * Maximum delay cap in milliseconds.\n * Prevents exponential backoff from growing too large.\n * @default 30000 (30 seconds)\n */\n maxDelay?: number;\n\n /**\n * Whether to add random jitter (0-25% of delay).\n * Helps prevent thundering herd when multiple workflows retry simultaneously.\n * @default true\n */\n jitter?: boolean;\n\n /**\n * Predicate to determine if a retry should occur.\n * Receives the error and current attempt number (1-indexed).\n * Return true to retry, false to fail immediately.\n * @default Always retry on any error\n */\n retryOn?: (error: unknown, attempt: number) => boolean;\n\n /**\n * Callback invoked before each retry attempt.\n * Useful for logging, metrics, or side effects.\n */\n onRetry?: (error: unknown, attempt: number, delayMs: number) => void;\n};\n\n/**\n * Configuration for step timeout behavior.\n */\nexport type TimeoutOptions = {\n /**\n * Timeout duration in milliseconds per attempt.\n * When combined with retry, each attempt gets its own timeout.\n */\n ms: number;\n\n /**\n * Custom error to use when timeout occurs.\n * @default StepTimeoutError with step details\n */\n error?: unknown;\n\n /**\n * Whether to pass an AbortSignal to the operation.\n * When true, the operation function receives (signal: AbortSignal) as argument.\n * Useful for fetch() and other APIs that support cancellation.\n * @default false\n */\n signal?: boolean;\n};\n\n/**\n * Standard timeout error type.\n */\nexport type StepTimeoutError = {\n type: \"STEP_TIMEOUT\";\n stepName?: string;\n stepKey?: string;\n timeoutMs: number;\n attempt?: number;\n};\n\n/**\n * Symbol used to mark any error (including custom errors) as a timeout error.\n * This allows detection of timeout errors even when users provide custom error payloads.\n */\nexport const STEP_TIMEOUT_MARKER: unique symbol = Symbol.for(\"step_timeout_marker\");\n\n/**\n * Metadata attached to timeout-marked errors.\n */\nexport type StepTimeoutMarkerMeta = {\n timeoutMs: number;\n stepName?: string;\n stepKey?: string;\n attempt?: number;\n};\n\n/**\n * Type guard to check if an error is a StepTimeoutError.\n * This checks both the standard type field AND the timeout marker symbol,\n * so custom errors provided via timeout.error are also detected.\n */\nexport function isStepTimeoutError(e: unknown): e is StepTimeoutError {\n if (typeof e !== \"object\" || e === null) {\n return false;\n }\n // Check for standard type field\n if ((e as StepTimeoutError).type === \"STEP_TIMEOUT\") {\n return true;\n }\n // Check for timeout marker (custom errors)\n return STEP_TIMEOUT_MARKER in e;\n}\n\n/**\n * Get timeout metadata from a timeout error (works with both standard and custom errors).\n * Returns undefined if the error is not a timeout error.\n */\nexport function getStepTimeoutMeta(e: unknown): StepTimeoutMarkerMeta | undefined {\n if (typeof e !== \"object\" || e === null) {\n return undefined;\n }\n // Check for standard type field first\n if ((e as StepTimeoutError).type === \"STEP_TIMEOUT\") {\n const err = e as StepTimeoutError;\n return {\n timeoutMs: err.timeoutMs,\n stepName: err.stepName,\n stepKey: err.stepKey,\n attempt: err.attempt,\n };\n }\n // Check for timeout marker (custom errors)\n if (STEP_TIMEOUT_MARKER in e) {\n return (e as Record<symbol, StepTimeoutMarkerMeta>)[STEP_TIMEOUT_MARKER];\n }\n return undefined;\n}\n\n// =============================================================================\n// RunStep Interface\n// =============================================================================\n\n/**\n * The `step` object passed to the function in `run(async (step) => { ... })`.\n * acts as the bridge between your business logic and the workflow engine.\n *\n * It provides methods to:\n * 1. Execute operations that return `Result` types.\n * 2. safely wrap operations that might throw exceptions (using `step.try`).\n * 3. Assign names and keys to operations for tracing and caching.\n *\n * @template E - The union of all known error types expected in this workflow.\n */\nexport interface RunStep<E = unknown> {\n /**\n * Execute a Result-returning operation (lazy function form).\n *\n * Use this form when the operation has side effects or is expensive,\n * so it's only executed if the step hasn't been cached/completed yet.\n *\n * @param operation - A function that returns a Result or AsyncResult\n * @param options - Step name or options object\n * @returns The success value (unwrapped)\n * @throws {EarlyExit} If the result is an error (stops execution safely)\n *\n * @example\n * ```typescript\n * const user = await step(() => fetchUser(id), \"fetch-user\");\n * ```\n */\n <T, StepE extends E, StepC = unknown>(\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n options?: StepOptions | string\n ): Promise<T>;\n\n /**\n * Execute a Result-returning operation (direct value form).\n *\n * Use this form for simple operations or when you already have a Result/Promise.\n * Note: The operation has already started/completed by the time `step` is called.\n *\n * @param result - A Result object or Promise resolving to a Result\n * @param options - Step name or options object\n * @returns The success value (unwrapped)\n * @throws {EarlyExit} If the result is an error (stops execution safely)\n *\n * @example\n * ```typescript\n * const user = await step(existingResult, \"check-result\");\n * ```\n */\n <T, StepE extends E, StepC = unknown>(\n result: Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n options?: StepOptions | string\n ): Promise<T>;\n\n /**\n * Execute a standard throwing operation safely.\n * Catches exceptions and maps them to a typed error, or wraps them if no mapper is provided.\n *\n * Use this when integrating with libraries that throw exceptions.\n *\n * @param operation - A function that returns a value or Promise (may throw)\n * @param options - Configuration including error mapping\n * @returns The success value\n * @throws {EarlyExit} If the operation throws (stops execution safely)\n *\n * @example\n * ```typescript\n * const data = await step.try(\n * () => db.query(),\n * {\n * name: \"db-query\",\n * onError: (e) => ({ type: \"DB_ERROR\", cause: e })\n * }\n * );\n * ```\n */\n try: <T, const Err extends E>(\n operation: () => T | Promise<T>,\n options:\n | { error: Err; name?: string; key?: string }\n | { onError: (cause: unknown) => Err; name?: string; key?: string }\n ) => Promise<T>;\n\n /**\n * Execute a Result-returning function and map its error to a typed error.\n *\n * Use this when calling functions that return Result<T, E> and you want to\n * map their typed errors to your workflow's error type. Unlike step.try(),\n * the error passed to onError is typed (not unknown).\n *\n * @param operation - A function that returns a Result or AsyncResult\n * @param options - Configuration including error mapping\n * @returns The success value (unwrapped)\n * @throws {EarlyExit} If the result is an error (stops execution safely)\n *\n * @example\n * ```typescript\n * const response = await step.fromResult(\n * () => callProvider(input),\n * {\n * name: \"call-provider\",\n * onError: (providerError) => ({\n * type: \"PROVIDER_FAILED\",\n * provider: providerError.provider,\n * cause: providerError\n * })\n * }\n * );\n * ```\n */\n fromResult: <T, ResultE, const Err extends E>(\n operation: () => Result<T, ResultE, unknown> | AsyncResult<T, ResultE, unknown>,\n options:\n | { error: Err; name?: string; key?: string }\n | { onError: (resultError: ResultE) => Err; name?: string; key?: string }\n ) => Promise<T>;\n\n /**\n * Execute a parallel operation (allAsync) with scope events for visualization.\n *\n * This wraps the operation with scope_start and scope_end events, enabling\n * visualization of parallel execution branches.\n *\n * @param name - Name for this parallel block (used in visualization)\n * @param operation - A function that returns a Result from allAsync or allSettledAsync\n * @returns The success value (unwrapped array)\n *\n * @example\n * ```typescript\n * const [user, posts] = await step.parallel('Fetch all data', () =>\n * allAsync([fetchUser(id), fetchPosts(id)])\n * );\n * ```\n */\n parallel: <T, StepE extends E, StepC = unknown>(\n name: string,\n operation: () => Result<T[], StepE, StepC> | AsyncResult<T[], StepE, StepC>\n ) => Promise<T[]>;\n\n /**\n * Execute a race operation (anyAsync) with scope events for visualization.\n *\n * This wraps the operation with scope_start and scope_end events, enabling\n * visualization of racing execution branches.\n *\n * @param name - Name for this race block (used in visualization)\n * @param operation - A function that returns a Result from anyAsync\n * @returns The success value (first to succeed)\n *\n * @example\n * ```typescript\n * const data = await step.race('Fastest API', () =>\n * anyAsync([fetchFromPrimary(id), fetchFromFallback(id)])\n * );\n * ```\n */\n race: <T, StepE extends E, StepC = unknown>(\n name: string,\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>\n ) => Promise<T>;\n\n /**\n * Execute an allSettled operation with scope events for visualization.\n *\n * This wraps the operation with scope_start and scope_end events, enabling\n * visualization of allSettled execution branches. Unlike step.parallel,\n * allSettled collects all results even if some fail.\n *\n * @param name - Name for this allSettled block (used in visualization)\n * @param operation - A function that returns a Result from allSettledAsync\n * @returns The success value (unwrapped array)\n *\n * @example\n * ```typescript\n * const [user, posts] = await step.allSettled('Fetch all data', () =>\n * allSettledAsync([fetchUser(id), fetchPosts(id)])\n * );\n * ```\n */\n allSettled: <T, StepE extends E, StepC = unknown>(\n name: string,\n operation: () => Result<T[], StepE, StepC> | AsyncResult<T[], StepE, StepC>\n ) => Promise<T[]>;\n\n /**\n * Execute an operation with retry and optional timeout.\n *\n * Use this for operations that may fail transiently (network issues, rate limits)\n * and benefit from automatic retry with backoff.\n *\n * @param operation - A function that returns a Result or AsyncResult\n * @param options - Retry configuration and optional timeout\n * @returns The success value (unwrapped)\n * @throws {EarlyExit} If all retries are exhausted (stops execution safely)\n *\n * @example\n * ```typescript\n * const data = await step.retry(\n * () => fetchFromExternalApi(id),\n * {\n * name: 'fetch-external',\n * attempts: 3,\n * backoff: 'exponential',\n * initialDelay: 200,\n * retryOn: (error) => error === 'RATE_LIMITED' || error === 'TRANSIENT',\n * onRetry: (error, attempt, delay) => {\n * console.log(`Retry ${attempt} after ${delay}ms`);\n * },\n * }\n * );\n * ```\n */\n retry: <T, StepE extends E, StepC = unknown>(\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n options: RetryOptions & { name?: string; key?: string; timeout?: TimeoutOptions }\n ) => Promise<T>;\n\n /**\n * Execute an operation with a timeout.\n *\n * Use this for operations that may hang indefinitely (external APIs, connections)\n * and need to be aborted after a certain duration.\n *\n * When `signal: true` is set, an AbortSignal is passed to your operation,\n * which you can use with APIs like fetch() for proper cancellation.\n *\n * @param operation - A function that returns a Result (may receive AbortSignal)\n * @param options - Timeout configuration\n * @returns The success value (unwrapped)\n * @throws {EarlyExit} If the operation times out (stops execution safely)\n *\n * @example\n * ```typescript\n * // Without AbortSignal\n * const data = await step.withTimeout(\n * () => fetchData(id),\n * { ms: 5000, name: 'fetch-data' }\n * );\n *\n * // With AbortSignal for fetch()\n * const data = await step.withTimeout(\n * (signal) => fetch(url, { signal }).then(r => ok(r.json())),\n * { ms: 5000, signal: true, name: 'fetch-url' }\n * );\n * ```\n */\n withTimeout: <T, StepE extends E, StepC = unknown>(\n operation:\n | (() => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>)\n | ((signal: AbortSignal) => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>),\n options: TimeoutOptions & { name?: string; key?: string }\n ) => Promise<T>;\n}\n\n// =============================================================================\n// Event Types (for run() optional event support)\n// =============================================================================\n\n/**\n * Unified event stream for workflow execution.\n *\n * Note: step_complete.result uses Result<unknown, unknown, unknown> because events\n * aggregate results from heterogeneous steps. At runtime, the actual Result object\n * preserves its original types, but the event type cannot statically represent them.\n * Use runtime checks or the meta field to interpret cause values.\n */\n/**\n * Scope types for parallel and race operations.\n */\nexport type ScopeType = \"parallel\" | \"race\" | \"allSettled\";\n\nexport type WorkflowEvent<E> =\n | { type: \"workflow_start\"; workflowId: string; ts: number }\n | { type: \"workflow_success\"; workflowId: string; ts: number; durationMs: number }\n | { type: \"workflow_error\"; workflowId: string; ts: number; durationMs: number; error: E }\n | { type: \"step_start\"; workflowId: string; stepId: string; stepKey?: string; name?: string; ts: number }\n | { type: \"step_success\"; workflowId: string; stepId: string; stepKey?: string; name?: string; ts: number; durationMs: number }\n | { type: \"step_error\"; workflowId: string; stepId: string; stepKey?: string; name?: string; ts: number; durationMs: number; error: E }\n | { type: \"step_aborted\"; workflowId: string; stepId: string; stepKey?: string; name?: string; ts: number; durationMs: number }\n | { type: \"step_complete\"; workflowId: string; stepKey: string; name?: string; ts: number; durationMs: number; result: Result<unknown, unknown, unknown>; meta?: StepFailureMeta }\n | { type: \"step_cache_hit\"; workflowId: string; stepKey: string; name?: string; ts: number }\n | { type: \"step_cache_miss\"; workflowId: string; stepKey: string; name?: string; ts: number }\n | { type: \"step_skipped\"; workflowId: string; stepKey?: string; name?: string; reason?: string; decisionId?: string; ts: number }\n | { type: \"scope_start\"; workflowId: string; scopeId: string; scopeType: ScopeType; name?: string; ts: number }\n | { type: \"scope_end\"; workflowId: string; scopeId: string; ts: number; durationMs: number; winnerId?: string }\n // Retry events\n | {\n type: \"step_retry\";\n workflowId: string;\n stepId: string;\n stepKey?: string;\n name?: string;\n ts: number;\n attempt: number;\n maxAttempts: number;\n delayMs: number;\n error: E;\n }\n | {\n type: \"step_retries_exhausted\";\n workflowId: string;\n stepId: string;\n stepKey?: string;\n name?: string;\n ts: number;\n durationMs: number;\n attempts: number;\n lastError: E;\n }\n // Timeout event\n | {\n type: \"step_timeout\";\n workflowId: string;\n stepId: string;\n stepKey?: string;\n name?: string;\n ts: number;\n timeoutMs: number;\n attempt?: number;\n };\n\n// =============================================================================\n// Run Options\n// =============================================================================\n\nexport type RunOptionsWithCatch<E, C = void> = {\n /**\n * Handler for expected errors.\n * Called when a step fails with a known error type.\n */\n onError?: (error: E, stepName?: string) => void;\n /**\n * Listener for workflow events (start, success, error, step events).\n * Use this for logging, telemetry, or debugging.\n */\n onEvent?: (event: WorkflowEvent<E | UnexpectedError>, ctx: C) => void;\n /**\n * Catch-all mapper for unexpected exceptions.\n * Required for \"Strict Mode\".\n * Converts unknown exceptions (like network crashes or bugs) into your typed error union E.\n */\n catchUnexpected: (cause: unknown) => E;\n /**\n * Unique ID for this workflow execution.\n * Defaults to a random UUID.\n * Useful for correlating logs across distributed systems.\n */\n workflowId?: string;\n /**\n * Arbitrary context object passed to onEvent.\n * Useful for passing request IDs, user IDs, or loggers.\n */\n context?: C;\n};\n\nexport type RunOptionsWithoutCatch<E, C = void> = {\n /**\n * Handler for expected errors AND unexpected errors.\n * Unexpected errors will be wrapped in `UnexpectedError`.\n */\n onError?: (error: E | UnexpectedError, stepName?: string) => void;\n onEvent?: (event: WorkflowEvent<E | UnexpectedError>, ctx: C) => void;\n catchUnexpected?: undefined;\n workflowId?: string;\n context?: C;\n};\n\nexport type RunOptions<E, C = void> = RunOptionsWithCatch<E, C> | RunOptionsWithoutCatch<E, C>;\n\n// =============================================================================\n// Early Exit Mechanism (exported for caching layer)\n// =============================================================================\n\n/**\n * Symbol used to identify early exit throws.\n * Exported for the caching layer in workflow.ts.\n * @internal\n */\nexport const EARLY_EXIT_SYMBOL: unique symbol = Symbol(\"early-exit\");\n\n/**\n * Metadata about how a step failed.\n * @internal\n */\nexport type StepFailureMeta =\n | { origin: \"result\"; resultCause?: unknown }\n | { origin: \"throw\"; thrown: unknown };\n\n/**\n * Early exit object thrown to short-circuit workflow execution.\n * @internal\n */\nexport type EarlyExit<E> = {\n [EARLY_EXIT_SYMBOL]: true;\n error: E;\n meta: StepFailureMeta;\n};\n\n/**\n * Create an early exit throw object.\n * Used by the caching layer to synthesize early exits for cached errors.\n * @internal\n */\nexport function createEarlyExit<E>(error: E, meta: StepFailureMeta): EarlyExit<E> {\n return {\n [EARLY_EXIT_SYMBOL]: true,\n error,\n meta,\n };\n}\n\n/**\n * Type guard for early exit objects.\n * @internal\n */\nexport function isEarlyExit<E>(e: unknown): e is EarlyExit<E> {\n return (\n typeof e === \"object\" &&\n e !== null &&\n (e as Record<PropertyKey, unknown>)[EARLY_EXIT_SYMBOL] === true\n );\n}\n\n/**\n * Symbol to mark exceptions thrown by catchUnexpected mappers.\n * These should propagate without being re-processed.\n * @internal\n */\nconst MAPPER_EXCEPTION_SYMBOL: unique symbol = Symbol(\"mapper-exception\");\n\ntype MapperException = {\n [MAPPER_EXCEPTION_SYMBOL]: true;\n thrown: unknown;\n};\n\nfunction createMapperException(thrown: unknown): MapperException {\n return { [MAPPER_EXCEPTION_SYMBOL]: true, thrown };\n}\n\nfunction isMapperException(e: unknown): e is MapperException {\n return (\n typeof e === \"object\" &&\n e !== null &&\n (e as Record<PropertyKey, unknown>)[MAPPER_EXCEPTION_SYMBOL] === true\n );\n}\n\n/** Helper to parse step options - accepts string or object form */\nfunction parseStepOptions(\n options?: StepOptions | string\n): StepOptions & { name?: string; key?: string } {\n if (typeof options === \"string\") {\n return { name: options };\n }\n return options ?? {};\n}\n\n// =============================================================================\n// Retry and Timeout Utilities\n// =============================================================================\n\n/**\n * Calculate the delay for a retry attempt based on the backoff strategy.\n * @internal\n */\nfunction calculateRetryDelay(\n attempt: number,\n options: {\n backoff: BackoffStrategy;\n initialDelay: number;\n maxDelay: number;\n jitter: boolean;\n }\n): number {\n const { backoff, initialDelay, maxDelay, jitter } = options;\n\n let delay: number;\n\n switch (backoff) {\n case \"fixed\":\n delay = initialDelay;\n break;\n case \"linear\":\n delay = initialDelay * attempt;\n break;\n case \"exponential\":\n delay = initialDelay * Math.pow(2, attempt - 1);\n break;\n }\n\n // Apply max cap\n delay = Math.min(delay, maxDelay);\n\n // Apply jitter (0-25% of delay)\n if (jitter) {\n const jitterAmount = delay * 0.25 * Math.random();\n delay = delay + jitterAmount;\n }\n\n return Math.floor(delay);\n}\n\n/**\n * Sleep for a specified number of milliseconds.\n * @internal\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Symbol used internally to identify timeout rejection.\n */\nconst TIMEOUT_SYMBOL: unique symbol = Symbol(\"timeout\");\n\n/**\n * Execute an operation with a timeout using Promise.race.\n * @internal\n */\nasync function executeWithTimeout<T>(\n operation: (() => Promise<T>) | ((signal: AbortSignal) => Promise<T>),\n options: TimeoutOptions,\n stepInfo: { name?: string; key?: string; attempt?: number }\n): Promise<T> {\n const controller = new AbortController();\n\n // Create the timeout error once\n const timeoutError: StepTimeoutError =\n (options.error as StepTimeoutError) ?? {\n type: \"STEP_TIMEOUT\",\n stepName: stepInfo.name,\n stepKey: stepInfo.key,\n timeoutMs: options.ms,\n attempt: stepInfo.attempt,\n };\n\n // Track the timeout ID for cleanup\n let timeoutId: ReturnType<typeof setTimeout>;\n\n // Create a timeout promise that rejects after the specified duration\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n controller.abort(); // Signal abort for operations that support it\n reject({ [TIMEOUT_SYMBOL]: true, error: timeoutError });\n }, options.ms);\n });\n\n // Execute the operation\n let operationPromise: Promise<T>;\n if (options.signal) {\n // Operation expects an AbortSignal\n operationPromise = Promise.resolve(\n (operation as (signal: AbortSignal) => Promise<T>)(controller.signal)\n );\n } else {\n // Standard operation\n operationPromise = Promise.resolve((operation as () => Promise<T>)());\n }\n\n try {\n // Race between operation and timeout\n const result = await Promise.race([operationPromise, timeoutPromise]);\n return result;\n } catch (error) {\n // Check if this was our timeout\n if (\n typeof error === \"object\" &&\n error !== null &&\n (error as Record<symbol, unknown>)[TIMEOUT_SYMBOL] === true\n ) {\n const errorToThrow = (error as { error: unknown }).error;\n\n // Mark the error with STEP_TIMEOUT_MARKER if it's a custom error (not already a StepTimeoutError)\n // This allows isStepTimeoutError() and getStepTimeoutMeta() to work with custom errors\n // Note: Always update metadata to reflect the current attempt (same error may be reused across retries)\n if (\n typeof errorToThrow === \"object\" &&\n errorToThrow !== null &&\n (errorToThrow as StepTimeoutError).type !== \"STEP_TIMEOUT\"\n ) {\n const meta: StepTimeoutMarkerMeta = {\n timeoutMs: options.ms,\n stepName: stepInfo.name,\n stepKey: stepInfo.key,\n attempt: stepInfo.attempt,\n };\n\n if (STEP_TIMEOUT_MARKER in errorToThrow) {\n // Update existing marker with current attempt's metadata\n (errorToThrow as Record<symbol, StepTimeoutMarkerMeta>)[STEP_TIMEOUT_MARKER] = meta;\n } else {\n // Define new marker (writable so it can be updated on retry)\n Object.defineProperty(errorToThrow, STEP_TIMEOUT_MARKER, {\n value: meta,\n enumerable: false,\n writable: true,\n configurable: false,\n });\n }\n }\n\n throw errorToThrow;\n }\n // Re-throw other errors\n throw error;\n } finally {\n // Always clear the timeout to prevent leaks\n clearTimeout(timeoutId!);\n }\n}\n\n/**\n * Default retry configuration values.\n * @internal\n */\nconst DEFAULT_RETRY_CONFIG = {\n backoff: \"exponential\" as BackoffStrategy,\n initialDelay: 100,\n maxDelay: 30000,\n jitter: true,\n retryOn: () => true,\n onRetry: () => {},\n} as const;\n\n// =============================================================================\n// run() Function\n// =============================================================================\n\n/**\n * Execute a workflow with step-based error handling.\n *\n * ## When to Use run()\n *\n * Use `run()` when:\n * - Dependencies are dynamic (passed at runtime, not known at compile time)\n * - You don't need step caching or resume state\n * - Error types are known upfront and can be specified manually\n * - Building lightweight, one-off workflows\n *\n * For automatic error type inference from static dependencies, use `createWorkflow()`.\n *\n * ## Modes\n *\n * `run()` has three modes based on options:\n * - **Strict Mode** (`catchUnexpected`): Returns `Result<T, E>` (closed union)\n * - **Typed Mode** (`onError`): Returns `Result<T, E | UnexpectedError>`\n * - **Safe Default** (no options): Returns `Result<T, UnexpectedError>`\n *\n * @example\n * ```typescript\n * // Typed mode with explicit error union\n * const result = await run<Output, 'NOT_FOUND' | 'FETCH_ERROR'>(\n * async (step) => {\n * const user = await step(fetchUser(userId));\n * return user;\n * },\n * { onError: (e) => console.log('Failed:', e) }\n * );\n * ```\n *\n * @see createWorkflow - For static dependencies with auto error inference\n */\n\n/**\n * Execute a workflow with \"Strict Mode\" error handling.\n *\n * In this mode, you MUST provide `catchUnexpected` to map unknown exceptions\n * to your typed error union `E`. This guarantees that the returned Result\n * will only ever contain errors of type `E`.\n *\n * @param fn - The workflow function containing steps\n * @param options - Configuration options, including `catchUnexpected`\n * @returns A Promise resolving to `Result<T, E>`\n *\n * @example\n * ```typescript\n * const result = await run(async (step) => {\n * // ... steps ...\n * }, {\n * catchUnexpected: (e) => ({ type: 'UNKNOWN_ERROR', cause: e })\n * });\n * ```\n */\nexport function run<T, E, C = void>(\n fn: (step: RunStep<E>) => Promise<T> | T,\n options: RunOptionsWithCatch<E, C>\n): AsyncResult<T, E, unknown>;\n\n/**\n * Execute a workflow with \"Typed Mode\" error handling.\n *\n * In this mode, you provide an `onError` callback. The returned Result\n * may contain your typed errors `E` OR `UnexpectedError` if an uncaught\n * exception occurs.\n *\n * @param fn - The workflow function containing steps\n * @param options - Configuration options, including `onError`\n * @returns A Promise resolving to `Result<T, E | UnexpectedError>`\n */\nexport function run<T, E, C = void>(\n fn: (step: RunStep<E | UnexpectedError>) => Promise<T> | T,\n options: {\n onError: (error: E | UnexpectedError, stepName?: string) => void;\n onEvent?: (event: WorkflowEvent<E | UnexpectedError>, ctx: C) => void;\n workflowId?: string;\n context?: C;\n }\n): AsyncResult<T, E | UnexpectedError, unknown>;\n\n/**\n * Execute a workflow with \"Safe Default\" error handling.\n *\n * In this mode, you don't need to specify any error types.\n * Any error (Result error or thrown exception) will be returned as\n * an `UnexpectedError`.\n *\n * @param fn - The workflow function containing steps\n * @param options - Optional configuration\n * @returns A Promise resolving to `Result<T, UnexpectedError>`\n *\n * @example\n * ```typescript\n * const result = await run(async (step) => {\n * return await step(someOp());\n * });\n * ```\n */\nexport function run<T, C = void>(\n fn: (step: RunStep) => Promise<T> | T,\n options?: {\n onEvent?: (event: WorkflowEvent<UnexpectedError>, ctx: C) => void;\n workflowId?: string;\n context?: C;\n }\n): AsyncResult<T, UnexpectedError, unknown>;\n\n// Implementation\nexport async function run<T, E, C = void>(\n fn: (step: RunStep<E | UnexpectedError>) => Promise<T> | T,\n options?: RunOptions<E, C>\n): AsyncResult<T, E | UnexpectedError> {\n const {\n onError,\n onEvent,\n catchUnexpected,\n workflowId: providedWorkflowId,\n context,\n } = options && typeof options === \"object\"\n ? (options as RunOptions<E, C>)\n : ({} as RunOptions<E, C>);\n\n const workflowId = providedWorkflowId ?? crypto.randomUUID();\n const wrapMode = !onError && !catchUnexpected;\n\n // Track active scopes as a stack for proper nesting\n // When a step succeeds, only the innermost race scope gets the winner\n const activeScopeStack: Array<{ scopeId: string; type: \"race\" | \"parallel\" | \"allSettled\"; winnerId?: string }> = [];\n\n // Counter for generating unique step IDs\n let stepIdCounter = 0;\n\n // Generate a unique step ID\n // Uses stepKey when provided (for cache stability), otherwise generates a unique ID.\n // Note: name is NOT used for stepId because multiple concurrent steps may share a name,\n // which would cause them to collide in activeSteps tracking and race winner detection.\n const generateStepId = (stepKey?: string): string => {\n return stepKey ?? `step_${++stepIdCounter}`;\n };\n\n const emitEvent = (event: WorkflowEvent<E | UnexpectedError>) => {\n // Track first successful step in the innermost race scope for winnerId\n if (event.type === \"step_success\") {\n // Use the stepId from the event (already generated at step start)\n const stepId = event.stepId;\n\n // Find innermost race scope (search from end of stack)\n for (let i = activeScopeStack.length - 1; i >= 0; i--) {\n const scope = activeScopeStack[i];\n if (scope.type === \"race\" && !scope.winnerId) {\n scope.winnerId = stepId;\n break; // Only update innermost race scope\n }\n }\n }\n onEvent?.(event, context as C);\n };\n\n // Use the exported early exit function with proper type parameter\n const earlyExit = createEarlyExit<E>;\n\n // Local type guard that narrows to EarlyExit<E> specifically\n const isEarlyExitE = (e: unknown): e is EarlyExit<E> => isEarlyExit(e);\n\n const wrapForStep = (\n error: unknown,\n meta?: StepFailureMeta\n ): E | UnexpectedError => {\n if (!wrapMode) {\n return error as E;\n }\n\n if (meta?.origin === \"result\") {\n return {\n type: \"UNEXPECTED_ERROR\",\n cause: {\n type: \"STEP_FAILURE\",\n origin: \"result\",\n error,\n ...(meta.resultCause !== undefined\n ? { cause: meta.resultCause }\n : {}),\n },\n };\n }\n\n if (meta?.origin === \"throw\") {\n return {\n type: \"UNEXPECTED_ERROR\",\n cause: {\n type: \"STEP_FAILURE\",\n origin: \"throw\",\n error,\n thrown: meta.thrown,\n },\n };\n }\n\n return {\n type: \"UNEXPECTED_ERROR\",\n cause: {\n type: \"STEP_FAILURE\",\n origin: \"result\",\n error,\n },\n };\n };\n\n const causeFromMeta = (meta: StepFailureMeta): unknown => {\n if (meta.origin === \"result\") {\n return meta.resultCause;\n }\n return meta.thrown;\n };\n\n const unexpectedFromFailure = (failure: EarlyExit<E>): UnexpectedError => ({\n type: \"UNEXPECTED_ERROR\",\n cause:\n failure.meta.origin === \"result\"\n ? {\n type: \"STEP_FAILURE\" as const,\n origin: \"result\" as const,\n error: failure.error,\n ...(failure.meta.resultCause !== undefined\n ? { cause: failure.meta.resultCause }\n : {}),\n }\n : {\n type: \"STEP_FAILURE\" as const,\n origin: \"throw\" as const,\n error: failure.error,\n thrown: failure.meta.thrown,\n },\n });\n\n try {\n const stepFn = <T, StepE, StepC = unknown>(\n operationOrResult:\n | (() => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>)\n | Result<T, StepE, StepC>\n | AsyncResult<T, StepE, StepC>,\n stepOptions?: StepOptions | string\n ): Promise<T> => {\n return (async () => {\n const parsedOptions = parseStepOptions(stepOptions);\n const { name: stepName, key: stepKey, retry: retryConfig, timeout: timeoutConfig } = parsedOptions;\n const stepId = generateStepId(stepKey);\n const hasEventListeners = onEvent;\n const overallStartTime = hasEventListeners ? performance.now() : 0;\n\n // Validate that retry/timeout are only used with function operations\n // Direct Promise/Result values cannot be re-executed or wrapped with timeout\n const isFunction = typeof operationOrResult === \"function\";\n if (!isFunction) {\n if (retryConfig && retryConfig.attempts > 1) {\n throw new Error(\n `step: retry options require a function operation. ` +\n `Direct Promise/Result values cannot be re-executed on retry. ` +\n `Wrap your operation in a function: step(() => yourOperation, { retry: {...} })`\n );\n }\n if (timeoutConfig) {\n throw new Error(\n `step: timeout options require a function operation. ` +\n `Direct Promise/Result values cannot be wrapped with timeout after they've started. ` +\n `Wrap your operation in a function: step(() => yourOperation, { timeout: {...} })`\n );\n }\n }\n\n // Build effective retry config with defaults\n // Ensure at least 1 attempt (0 would skip the loop entirely and crash)\n const maxAttempts = Math.max(1, retryConfig?.attempts ?? 1);\n const effectiveRetry = {\n attempts: maxAttempts,\n backoff: retryConfig?.backoff ?? DEFAULT_RETRY_CONFIG.backoff,\n initialDelay: retryConfig?.initialDelay ?? DEFAULT_RETRY_CONFIG.initialDelay,\n maxDelay: retryConfig?.maxDelay ?? DEFAULT_RETRY_CONFIG.maxDelay,\n jitter: retryConfig?.jitter ?? DEFAULT_RETRY_CONFIG.jitter,\n retryOn: retryConfig?.retryOn ?? DEFAULT_RETRY_CONFIG.retryOn,\n onRetry: retryConfig?.onRetry ?? DEFAULT_RETRY_CONFIG.onRetry,\n };\n\n // Emit step_start only once (before first attempt)\n if (onEvent) {\n emitEvent({\n type: \"step_start\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n });\n }\n\n let lastResult: Result<T, StepE, StepC> | undefined;\n\n for (let attempt = 1; attempt <= effectiveRetry.attempts; attempt++) {\n const attemptStartTime = hasEventListeners ? performance.now() : 0;\n\n try {\n // Execute operation with optional timeout\n let result: Result<T, StepE, StepC>;\n\n if (typeof operationOrResult === \"function\") {\n if (timeoutConfig) {\n // Wrap with timeout\n result = await executeWithTimeout(\n operationOrResult as () => Promise<Result<T, StepE, StepC>>,\n timeoutConfig,\n { name: stepName, key: stepKey, attempt }\n );\n } else {\n result = await operationOrResult();\n }\n } else {\n // Direct value - timeout doesn't apply\n result = await operationOrResult;\n }\n\n // Success case\n if (result.ok) {\n const durationMs = performance.now() - overallStartTime;\n emitEvent({\n type: \"step_success\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n });\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n result,\n });\n }\n return result.value;\n }\n\n // Result error case - check if we should retry\n lastResult = result;\n\n if (attempt < effectiveRetry.attempts && effectiveRetry.retryOn(result.error, attempt)) {\n const delay = calculateRetryDelay(attempt, effectiveRetry);\n\n // Emit retry event\n emitEvent({\n type: \"step_retry\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n attempt: attempt + 1,\n maxAttempts: effectiveRetry.attempts,\n delayMs: delay,\n error: result.error as unknown as E,\n });\n\n effectiveRetry.onRetry(result.error, attempt, delay);\n await sleep(delay);\n continue;\n }\n\n // No more retries or retryOn returned false - emit exhausted event if we retried\n if (effectiveRetry.attempts > 1) {\n emitEvent({\n type: \"step_retries_exhausted\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: performance.now() - overallStartTime,\n attempts: attempt,\n lastError: result.error as unknown as E,\n });\n }\n\n // Fall through to final error handling below\n break;\n\n } catch (thrown) {\n const durationMs = performance.now() - attemptStartTime;\n\n // Handle early exit - propagate immediately\n if (isEarlyExitE(thrown)) {\n emitEvent({\n type: \"step_aborted\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n });\n throw thrown;\n }\n\n // Handle timeout error\n if (isStepTimeoutError(thrown)) {\n // Get timeout metadata from the error (works for both standard and custom errors)\n const timeoutMeta = getStepTimeoutMeta(thrown);\n const timeoutMs = timeoutConfig?.ms ?? timeoutMeta?.timeoutMs ?? 0;\n emitEvent({\n type: \"step_timeout\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n timeoutMs,\n attempt,\n });\n\n // Check if we should retry after timeout\n if (attempt < effectiveRetry.attempts && effectiveRetry.retryOn(thrown, attempt)) {\n const delay = calculateRetryDelay(attempt, effectiveRetry);\n\n emitEvent({\n type: \"step_retry\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n attempt: attempt + 1,\n maxAttempts: effectiveRetry.attempts,\n delayMs: delay,\n error: thrown as unknown as E,\n });\n\n effectiveRetry.onRetry(thrown, attempt, delay);\n await sleep(delay);\n continue;\n }\n\n // No more retries - emit exhausted if we retried\n if (effectiveRetry.attempts > 1) {\n emitEvent({\n type: \"step_retries_exhausted\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: performance.now() - overallStartTime,\n attempts: attempt,\n lastError: thrown as unknown as E,\n });\n }\n\n // Treat timeout as a thrown error for error handling\n }\n\n // Handle other thrown errors (continue to error handling below)\n\n // Check if we should retry thrown errors\n if (attempt < effectiveRetry.attempts && effectiveRetry.retryOn(thrown, attempt)) {\n const delay = calculateRetryDelay(attempt, effectiveRetry);\n\n emitEvent({\n type: \"step_retry\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n attempt: attempt + 1,\n maxAttempts: effectiveRetry.attempts,\n delayMs: delay,\n error: thrown as unknown as E,\n });\n\n effectiveRetry.onRetry(thrown, attempt, delay);\n await sleep(delay);\n continue;\n }\n\n // No more retries for thrown errors - emit exhausted if we retried\n if (effectiveRetry.attempts > 1 && !isStepTimeoutError(thrown)) {\n emitEvent({\n type: \"step_retries_exhausted\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: performance.now() - overallStartTime,\n attempts: attempt,\n lastError: thrown as unknown as E,\n });\n }\n\n // Handle the error based on mode\n const totalDurationMs = performance.now() - overallStartTime;\n\n if (catchUnexpected) {\n let mappedError: E;\n try {\n mappedError = catchUnexpected(thrown) as unknown as E;\n } catch (mapperError) {\n throw createMapperException(mapperError);\n }\n emitEvent({\n type: \"step_error\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: totalDurationMs,\n error: mappedError,\n });\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: totalDurationMs,\n result: err(mappedError, { cause: thrown }),\n meta: { origin: \"throw\", thrown },\n });\n }\n onError?.(mappedError as E, stepName);\n throw earlyExit(mappedError as E, { origin: \"throw\", thrown });\n } else {\n const unexpectedError: UnexpectedError = {\n type: \"UNEXPECTED_ERROR\",\n cause: { type: \"UNCAUGHT_EXCEPTION\", thrown },\n };\n emitEvent({\n type: \"step_error\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: totalDurationMs,\n error: unexpectedError,\n });\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: totalDurationMs,\n result: err(unexpectedError, { cause: thrown }),\n meta: { origin: \"throw\", thrown },\n });\n }\n throw thrown;\n }\n }\n }\n\n // All retries exhausted with Result error - handle final error\n // At this point lastResult must be an error result (we only reach here on error)\n const errorResult = lastResult as { ok: false; error: StepE; cause?: StepC };\n const totalDurationMs = performance.now() - overallStartTime;\n const wrappedError = wrapForStep(errorResult.error, {\n origin: \"result\",\n resultCause: errorResult.cause,\n });\n emitEvent({\n type: \"step_error\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: totalDurationMs,\n error: wrappedError,\n });\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: totalDurationMs,\n result: errorResult,\n meta: { origin: \"result\", resultCause: errorResult.cause },\n });\n }\n onError?.(errorResult.error as unknown as E, stepName);\n throw earlyExit(errorResult.error as unknown as E, {\n origin: \"result\",\n resultCause: errorResult.cause,\n });\n })();\n };\n\n stepFn.try = <T, Err>(\n operation: () => T | Promise<T>,\n opts:\n | { error: Err; name?: string; key?: string }\n | { onError: (cause: unknown) => Err; name?: string; key?: string }\n ): Promise<T> => {\n const stepName = opts.name;\n const stepKey = opts.key;\n const stepId = generateStepId(stepKey);\n const mapToError = \"error\" in opts ? () => opts.error : opts.onError;\n const hasEventListeners = onEvent;\n\n return (async () => {\n const startTime = hasEventListeners ? performance.now() : 0;\n\n if (onEvent) {\n emitEvent({\n type: \"step_start\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n });\n }\n\n try {\n const value = await operation();\n const durationMs = performance.now() - startTime;\n emitEvent({\n type: \"step_success\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n });\n // Emit step_complete for keyed steps (for state persistence)\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n result: ok(value),\n });\n }\n return value;\n } catch (error) {\n const mapped = mapToError(error);\n const durationMs = performance.now() - startTime;\n const wrappedError = wrapForStep(mapped, { origin: \"throw\", thrown: error });\n emitEvent({\n type: \"step_error\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n error: wrappedError,\n });\n // Emit step_complete for keyed steps (for state persistence)\n // Note: For step.try errors, we encode the mapped error, not the original thrown\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n result: err(mapped, { cause: error }),\n meta: { origin: \"throw\", thrown: error },\n });\n }\n onError?.(mapped as unknown as E, stepName);\n throw earlyExit(mapped as unknown as E, { origin: \"throw\", thrown: error });\n }\n })();\n };\n\n // step.fromResult: Execute a Result-returning function and map its typed error\n stepFn.fromResult = <T, ResultE, Err>(\n operation: () => Result<T, ResultE, unknown> | AsyncResult<T, ResultE, unknown>,\n opts:\n | { error: Err; name?: string; key?: string }\n | { onError: (resultError: ResultE) => Err; name?: string; key?: string }\n ): Promise<T> => {\n const stepName = opts.name;\n const stepKey = opts.key;\n const stepId = generateStepId(stepKey);\n const mapToError = \"error\" in opts ? () => opts.error : opts.onError;\n const hasEventListeners = onEvent;\n\n return (async () => {\n const startTime = hasEventListeners ? performance.now() : 0;\n\n if (onEvent) {\n emitEvent({\n type: \"step_start\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n });\n }\n\n const result = await operation();\n\n if (result.ok) {\n const durationMs = performance.now() - startTime;\n emitEvent({\n type: \"step_success\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n });\n // Emit step_complete for keyed steps (for state persistence)\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n result: ok(result.value),\n });\n }\n return result.value;\n } else {\n const mapped = mapToError(result.error);\n const durationMs = performance.now() - startTime;\n // For fromResult, the cause is the original result.error (what got mapped)\n // This is analogous to step.try using thrown exception as cause\n const wrappedError = wrapForStep(mapped, {\n origin: \"result\",\n resultCause: result.error,\n });\n emitEvent({\n type: \"step_error\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n error: wrappedError,\n });\n // Emit step_complete for keyed steps (for state persistence)\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n result: err(mapped, { cause: result.error }),\n meta: { origin: \"result\", resultCause: result.error },\n });\n }\n onError?.(mapped as unknown as E, stepName);\n throw earlyExit(mapped as unknown as E, {\n origin: \"result\",\n resultCause: result.error,\n });\n }\n })();\n };\n\n // step.retry: Execute an operation with retry and optional timeout\n stepFn.retry = <T, StepE, StepC = unknown>(\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n options: RetryOptions & { name?: string; key?: string; timeout?: TimeoutOptions }\n ): Promise<T> => {\n // Delegate to stepFn with retry options merged into StepOptions\n return stepFn(operation, {\n name: options.name,\n key: options.key,\n retry: {\n attempts: options.attempts,\n backoff: options.backoff,\n initialDelay: options.initialDelay,\n maxDelay: options.maxDelay,\n jitter: options.jitter,\n retryOn: options.retryOn,\n onRetry: options.onRetry,\n },\n timeout: options.timeout,\n });\n };\n\n // step.withTimeout: Execute an operation with a timeout\n stepFn.withTimeout = <T, StepE, StepC = unknown>(\n operation:\n | (() => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>)\n | ((signal: AbortSignal) => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>),\n options: TimeoutOptions & { name?: string; key?: string }\n ): Promise<T> => {\n // Delegate to stepFn with timeout options\n // The signal handling happens in executeWithTimeout when timeout.signal is true\n return stepFn(\n operation as () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n {\n name: options.name,\n key: options.key,\n timeout: options,\n }\n );\n };\n\n // step.parallel: Execute a parallel operation with scope events\n stepFn.parallel = <T, StepE, StepC>(\n name: string,\n operation: () => Result<T[], StepE, StepC> | AsyncResult<T[], StepE, StepC>\n ): Promise<T[]> => {\n const scopeId = `scope_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n\n return (async () => {\n const startTime = performance.now();\n let scopeEnded = false;\n\n // Push this scope onto the stack for proper nesting tracking\n activeScopeStack.push({ scopeId, type: \"parallel\" });\n\n // Helper to emit scope_end exactly once\n const emitScopeEnd = () => {\n if (scopeEnded) return;\n scopeEnded = true;\n // Pop this scope from the stack\n const idx = activeScopeStack.findIndex(s => s.scopeId === scopeId);\n if (idx !== -1) activeScopeStack.splice(idx, 1);\n emitEvent({\n type: \"scope_end\",\n workflowId,\n scopeId,\n ts: Date.now(),\n durationMs: performance.now() - startTime,\n });\n };\n\n // Emit scope_start event\n emitEvent({\n type: \"scope_start\",\n workflowId,\n scopeId,\n scopeType: \"parallel\",\n name,\n ts: Date.now(),\n });\n\n try {\n const result = await operation();\n\n // Emit scope_end before processing result\n emitScopeEnd();\n\n if (!result.ok) {\n onError?.(result.error as unknown as E, name);\n throw earlyExit(result.error as unknown as E, {\n origin: \"result\",\n resultCause: result.cause,\n });\n }\n\n return result.value;\n } catch (error) {\n // Always emit scope_end in finally-like fashion\n emitScopeEnd();\n throw error;\n }\n })();\n };\n\n // step.race: Execute a race operation with scope events\n stepFn.race = <T, StepE, StepC>(\n name: string,\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>\n ): Promise<T> => {\n const scopeId = `scope_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n\n return (async () => {\n const startTime = performance.now();\n let scopeEnded = false;\n\n // Push this race scope onto the stack to track the first successful step as winner\n const scopeEntry = { scopeId, type: \"race\" as const, winnerId: undefined as string | undefined };\n activeScopeStack.push(scopeEntry);\n\n // Helper to emit scope_end exactly once, including winnerId\n const emitScopeEnd = () => {\n if (scopeEnded) return;\n scopeEnded = true;\n // Pop this scope from the stack\n const idx = activeScopeStack.findIndex(s => s.scopeId === scopeId);\n if (idx !== -1) activeScopeStack.splice(idx, 1);\n emitEvent({\n type: \"scope_end\",\n workflowId,\n scopeId,\n ts: Date.now(),\n durationMs: performance.now() - startTime,\n winnerId: scopeEntry.winnerId,\n });\n };\n\n // Emit scope_start event\n emitEvent({\n type: \"scope_start\",\n workflowId,\n scopeId,\n scopeType: \"race\",\n name,\n ts: Date.now(),\n });\n\n try {\n const result = await operation();\n\n // Emit scope_end before processing result\n emitScopeEnd();\n\n if (!result.ok) {\n onError?.(result.error as unknown as E, name);\n throw earlyExit(result.error as unknown as E, {\n origin: \"result\",\n resultCause: result.cause,\n });\n }\n\n return result.value;\n } catch (error) {\n // Always emit scope_end in finally-like fashion\n emitScopeEnd();\n throw error;\n }\n })();\n };\n\n // step.allSettled: Execute an allSettled operation with scope events\n stepFn.allSettled = <T, StepE, StepC>(\n name: string,\n operation: () => Result<T[], StepE, StepC> | AsyncResult<T[], StepE, StepC>\n ): Promise<T[]> => {\n const scopeId = `scope_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n\n return (async () => {\n const startTime = performance.now();\n let scopeEnded = false;\n\n // Push this scope onto the stack for proper nesting tracking\n activeScopeStack.push({ scopeId, type: \"allSettled\" });\n\n // Helper to emit scope_end exactly once\n const emitScopeEnd = () => {\n if (scopeEnded) return;\n scopeEnded = true;\n // Pop this scope from the stack\n const idx = activeScopeStack.findIndex(s => s.scopeId === scopeId);\n if (idx !== -1) activeScopeStack.splice(idx, 1);\n emitEvent({\n type: \"scope_end\",\n workflowId,\n scopeId,\n ts: Date.now(),\n durationMs: performance.now() - startTime,\n });\n };\n\n // Emit scope_start event\n emitEvent({\n type: \"scope_start\",\n workflowId,\n scopeId,\n scopeType: \"allSettled\",\n name,\n ts: Date.now(),\n });\n\n try {\n const result = await operation();\n\n // Emit scope_end before processing result\n emitScopeEnd();\n\n if (!result.ok) {\n onError?.(result.error as unknown as E, name);\n throw earlyExit(result.error as unknown as E, {\n origin: \"result\",\n resultCause: result.cause,\n });\n }\n\n return result.value;\n } catch (error) {\n // Always emit scope_end in finally-like fashion\n emitScopeEnd();\n throw error;\n }\n })();\n };\n\n const step = stepFn as RunStep<E | UnexpectedError>;\n const value = await fn(step);\n return ok(value);\n } catch (error) {\n // If a catchUnexpected mapper threw, propagate without re-processing\n if (isMapperException(error)) {\n throw error.thrown;\n }\n\n if (isEarlyExitE(error)) {\n const failureCause = causeFromMeta(error.meta);\n if (catchUnexpected || onError) {\n return err(error.error, { cause: failureCause });\n }\n // If the error is already an UnexpectedError (e.g., from resumed state),\n // return it directly without wrapping in another STEP_FAILURE\n if (isUnexpectedError(error.error)) {\n return err(error.error, { cause: failureCause });\n }\n const unexpectedError = unexpectedFromFailure(error);\n return err(unexpectedError, { cause: failureCause });\n }\n\n if (catchUnexpected) {\n const mapped = catchUnexpected(error);\n onError?.(mapped, \"unexpected\");\n return err(mapped, { cause: error });\n }\n\n const unexpectedError: UnexpectedError = {\n type: \"UNEXPECTED_ERROR\",\n cause: { type: \"UNCAUGHT_EXCEPTION\", thrown: error },\n };\n onError?.(unexpectedError as unknown as E, \"unexpected\");\n return err(unexpectedError, { cause: error });\n }\n}\n\n/**\n * Executes a workflow in \"Strict Mode\" with a closed error union.\n *\n * ## When to Use\n *\n * Use `run.strict()` when:\n * - You want a closed error union (no `UnexpectedError`)\n * - You need exhaustive error handling in production\n * - You want to guarantee all errors are explicitly typed\n * - You're building APIs where error types must be known\n *\n * ## Why Use This\n *\n * - **Closed union**: Error type is exactly `E`, no `UnexpectedError`\n * - **Exhaustive**: Forces you to handle all possible errors\n * - **Type-safe**: TypeScript ensures all errors are typed\n * - **Production-ready**: Better for APIs and libraries\n *\n * ## Important\n *\n * You MUST provide `catchUnexpected` to map any uncaught exceptions to your error type `E`.\n * This ensures the error union is truly closed.\n *\n * @param fn - The workflow function containing steps\n * @param options - Configuration options, MUST include `catchUnexpected`\n * @returns A Promise resolving to `Result<T, E>` (no UnexpectedError)\n *\n * @example\n * ```typescript\n * type AppError = 'NOT_FOUND' | 'UNAUTHORIZED' | 'UNEXPECTED';\n *\n * const result = await run.strict<User, AppError>(\n * async (step) => {\n * return await step(fetchUser(id));\n * },\n * {\n * catchUnexpected: () => 'UNEXPECTED' as const\n * }\n * );\n * // result.error: 'NOT_FOUND' | 'UNAUTHORIZED' | 'UNEXPECTED' (exactly)\n * ```\n */\nrun.strict = <T, E, C = void>(\n fn: (step: RunStep<E>) => Promise<T> | T,\n options: {\n onError?: (error: E, stepName?: string) => void;\n onEvent?: (event: WorkflowEvent<E | UnexpectedError>, ctx: C) => void;\n catchUnexpected: (cause: unknown) => E;\n workflowId?: string;\n context?: C;\n }\n): AsyncResult<T, E, unknown> => {\n return run<T, E, C>(fn, options);\n};\n\n// =============================================================================\n// Unwrap Utilities\n// =============================================================================\n\n/**\n * Error thrown when `unwrap()` is called on an error Result.\n *\n * This error is thrown to prevent silent failures when using `unwrap()`.\n * Prefer using `unwrapOr`, `unwrapOrElse`, or pattern matching with `match` or `isOk`/`isErr`.\n */\nexport class UnwrapError<E = unknown, C = unknown> extends Error {\n constructor(\n public readonly error: E,\n public readonly cause?: C\n ) {\n super(`Unwrap called on an error result: ${String(error)}`);\n this.name = \"UnwrapError\";\n }\n}\n\n/**\n * Unwraps a Result, throwing an error if it's a failure.\n *\n * ## When to Use\n *\n * Use `unwrap()` when:\n * - You're certain the Result is successful (e.g., after checking with `isOk`)\n * - You're in a context where errors should crash (e.g., tests, initialization)\n * - You need the value immediately and can't handle errors gracefully\n *\n * ## Why Avoid This\n *\n * **Prefer alternatives** in production code:\n * - `unwrapOr(defaultValue)` - Provide a fallback value\n * - `unwrapOrElse(fn)` - Compute fallback from error\n * - `match()` - Handle both cases explicitly\n * - `isOk()` / `isErr()` - Type-safe pattern matching\n *\n * Throwing errors makes error handling harder and can crash your application.\n *\n * @param r - The Result to unwrap\n * @returns The success value if the Result is successful\n * @throws {UnwrapError} If the Result is an error (includes the error and cause)\n *\n * @example\n * ```typescript\n * // Safe usage after checking\n * const result = someOperation();\n * if (isOk(result)) {\n * const value = unwrap(result); // Safe - we know it's ok\n * }\n *\n * // Unsafe usage (not recommended)\n * const value = unwrap(someOperation()); // May throw!\n * ```\n */\nexport const unwrap = <T, E, C>(r: Result<T, E, C>): T => {\n if (r.ok) return r.value;\n throw new UnwrapError<E, C>(r.error, r.cause);\n};\n\n/**\n * Unwraps a Result, returning a default value if it's a failure.\n *\n * ## When to Use\n *\n * Use `unwrapOr()` when:\n * - You have a sensible default value for errors\n * - You want to continue execution even on failure\n * - The default value is cheap to compute (use `unwrapOrElse` if expensive)\n *\n * ## Why Use This\n *\n * - **Safe**: Never throws, always returns a value\n * - **Simple**: One-liner for common error handling\n * - **Type-safe**: TypeScript knows you'll always get a `T`\n *\n * @param r - The Result to unwrap\n * @param defaultValue - The value to return if the Result is an error\n * @returns The success value if successful, otherwise the default value\n *\n * @example\n * ```typescript\n * // Provide default for missing data\n * const user = unwrapOr(fetchUser(id), { id: 'anonymous', name: 'Guest' });\n *\n * // Provide default for numeric operations\n * const count = unwrapOr(parseCount(input), 0);\n *\n * // Provide default for optional features\n * const config = unwrapOr(loadConfig(), getDefaultConfig());\n * ```\n */\nexport const unwrapOr = <T, E, C>(r: Result<T, E, C>, defaultValue: T): T =>\n r.ok ? r.value : defaultValue;\n\n/**\n * Unwraps a Result, computing a default value from the error if it's a failure.\n *\n * ## When to Use\n *\n * Use `unwrapOrElse()` when:\n * - The default value is expensive to compute (lazy evaluation)\n * - You need to log or handle the error before providing a default\n * - The default depends on the error type or cause\n * - You want to transform the error into a success value\n *\n * ## Why Use This Instead of `unwrapOr`\n *\n * - **Lazy**: Default is only computed if needed (better performance)\n * - **Error-aware**: You can inspect the error before providing default\n * - **Flexible**: Default can depend on error type or cause\n *\n * @param r - The Result to unwrap\n * @param fn - Function that receives the error and optional cause, returns the default value\n * @returns The success value if successful, otherwise the result of calling `fn(error, cause)`\n *\n * @example\n * ```typescript\n * // Compute default based on error type\n * const port = unwrapOrElse(parsePort(env.PORT), (error) => {\n * if (error === 'INVALID_FORMAT') return 3000;\n * if (error === 'OUT_OF_RANGE') return 8080;\n * return 4000; // default\n * });\n *\n * // Log error before providing default\n * const data = unwrapOrElse(fetchData(), (error, cause) => {\n * console.error('Failed to fetch:', error, cause);\n * return getCachedData();\n * });\n *\n * // Transform error into success value\n * const result = unwrapOrElse(operation(), (error) => {\n * return { success: false, reason: String(error) };\n * });\n * ```\n */\nexport const unwrapOrElse = <T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => T\n): T => (r.ok ? r.value : fn(r.error, r.cause));\n\n// =============================================================================\n// Wrapping Functions\n// =============================================================================\n\n/**\n * Wraps a synchronous throwing function in a Result.\n *\n * ## When to Use\n *\n * Use `from()` when:\n * - You have a synchronous function that throws exceptions\n * - You want to convert exceptions to typed errors\n * - You're integrating with libraries that throw (e.g., JSON.parse, fs.readFileSync)\n * - You need to handle errors without try/catch blocks\n *\n * ## Why Use This\n *\n * - **Type-safe errors**: Convert thrown exceptions to typed Result errors\n * - **No try/catch**: Cleaner code without nested try/catch blocks\n * - **Composable**: Results can be chained with `andThen`, `map`, etc.\n * - **Explicit errors**: Forces you to handle errors explicitly\n *\n * @param fn - The synchronous function to execute (may throw)\n * @returns A Result with the function's return value or the thrown error\n *\n * @example\n * ```typescript\n * // Wrap JSON.parse\n * const parsed = from(() => JSON.parse('{\"key\": \"value\"}'));\n * // parsed: { ok: true, value: { key: \"value\" } }\n *\n * const error = from(() => JSON.parse('invalid'));\n * // error: { ok: false, error: SyntaxError }\n * ```\n */\nexport function from<T>(fn: () => T): Result<T, unknown>;\n/**\n * Wraps a synchronous throwing function in a Result with custom error mapping.\n *\n * Use this overload when you want to map thrown exceptions to your typed error union.\n *\n * @param fn - The synchronous function to execute (may throw)\n * @param onError - Function to map the thrown exception to a typed error\n * @returns A Result with the function's return value or the mapped error\n *\n * @example\n * ```typescript\n * // Map exceptions to typed errors\n * const parsed = from(\n * () => JSON.parse(input),\n * (cause) => ({ type: 'PARSE_ERROR' as const, cause })\n * );\n * // parsed.error: { type: 'PARSE_ERROR', cause: SyntaxError }\n *\n * // Map to simple error codes\n * const value = from(\n * () => riskyOperation(),\n * () => 'OPERATION_FAILED' as const\n * );\n * ```\n */\nexport function from<T, E>(fn: () => T, onError: (cause: unknown) => E): Result<T, E>;\nexport function from<T, E>(fn: () => T, onError?: (cause: unknown) => E) {\n try {\n return ok(fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Wraps a Promise in a Result, converting rejections to errors.\n *\n * ## When to Use\n *\n * Use `fromPromise()` when:\n * - You have an existing Promise that might reject\n * - You want to convert Promise rejections to typed errors\n * - You're working with libraries that return Promises (fetch, database clients)\n * - You need to handle rejections without .catch() chains\n *\n * ## Why Use This\n *\n * - **Type-safe errors**: Convert Promise rejections to typed Result errors\n * - **Composable**: Results can be chained with `andThen`, `map`, etc.\n * - **Explicit handling**: Forces you to handle errors explicitly\n * - **No .catch() chains**: Cleaner than Promise.catch() patterns\n *\n * @param promise - The Promise to await (may reject)\n * @returns A Promise resolving to a Result with the resolved value or rejection reason\n *\n * @example\n * ```typescript\n * // Wrap fetch\n * const result = await fromPromise(\n * fetch('/api').then(r => r.json())\n * );\n * // result.ok: true if fetch succeeded, false if rejected\n * ```\n */\nexport function fromPromise<T>(promise: Promise<T>): AsyncResult<T, unknown>;\n/**\n * Wraps a Promise in a Result with custom error mapping.\n *\n * Use this overload when you want to map Promise rejections to your typed error union.\n *\n * @param promise - The Promise to await (may reject)\n * @param onError - Function to map the rejection reason to a typed error\n * @returns A Promise resolving to a Result with the resolved value or mapped error\n *\n * @example\n * ```typescript\n * // Map fetch errors to typed errors\n * const result = await fromPromise(\n * fetch('/api').then(r => {\n * if (!r.ok) throw new Error(`HTTP ${r.status}`);\n * return r.json();\n * }),\n * () => 'FETCH_FAILED' as const\n * );\n * // result.error: 'FETCH_FAILED' if fetch failed\n *\n * // Map with error details\n * const data = await fromPromise(\n * db.query(sql),\n * (cause) => ({ type: 'DB_ERROR' as const, message: String(cause) })\n * );\n * ```\n */\nexport function fromPromise<T, E>(\n promise: Promise<T>,\n onError: (cause: unknown) => E\n): AsyncResult<T, E>;\nexport async function fromPromise<T, E>(\n promise: Promise<T>,\n onError?: (cause: unknown) => E\n): AsyncResult<T, E | unknown> {\n try {\n return ok(await promise);\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Wraps an async function in a Result, catching both thrown exceptions and Promise rejections.\n *\n * ## When to Use\n *\n * Use `tryAsync()` when:\n * - You have an async function that might throw or reject\n * - You want to convert both exceptions and rejections to typed errors\n * - You're creating new async functions (use `fromPromise` for existing Promises)\n * - You need to handle errors without try/catch or .catch()\n *\n * ## Why Use This Instead of `fromPromise`\n *\n * - **Function form**: Takes a function, not a Promise (lazy evaluation)\n * - **Catches both**: Handles both thrown exceptions and Promise rejections\n * - **Cleaner syntax**: No need to wrap in Promise manually\n *\n * @param fn - The async function to execute (may throw or reject)\n * @returns A Promise resolving to a Result with the function's return value or error\n *\n * @example\n * ```typescript\n * // Wrap async function\n * const result = await tryAsync(async () => {\n * const data = await fetchData();\n * return processData(data);\n * });\n * ```\n */\nexport function tryAsync<T>(fn: () => Promise<T>): AsyncResult<T, unknown>;\n/**\n * Wraps an async function in a Result with custom error mapping.\n *\n * Use this overload when you want to map errors to your typed error union.\n *\n * @param fn - The async function to execute (may throw or reject)\n * @param onError - Function to map the error (exception or rejection) to a typed error\n * @returns A Promise resolving to a Result with the function's return value or mapped error\n *\n * @example\n * ```typescript\n * // Map errors to typed errors\n * const result = await tryAsync(\n * async () => await fetchData(),\n * () => 'FETCH_ERROR' as const\n * );\n *\n * // Map with error details\n * const data = await tryAsync(\n * async () => await processFile(path),\n * (cause) => ({ type: 'PROCESSING_ERROR' as const, cause })\n * );\n * ```\n */\nexport function tryAsync<T, E>(\n fn: () => Promise<T>,\n onError: (cause: unknown) => E\n): AsyncResult<T, E>;\nexport async function tryAsync<T, E>(\n fn: () => Promise<T>,\n onError?: (cause: unknown) => E\n): AsyncResult<T, E | unknown> {\n try {\n return ok(await fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Converts a nullable value to a Result.\n *\n * ## When to Use\n *\n * Use `fromNullable()` when:\n * - You have a value that might be `null` or `undefined`\n * - You want to treat null/undefined as an error case\n * - You're working with APIs that return nullable values (DOM APIs, optional properties)\n * - You want to avoid null checks scattered throughout your code\n *\n * ## Why Use This\n *\n * - **Type-safe**: Converts nullable types to non-nullable Results\n * - **Explicit errors**: Forces you to handle null/undefined cases\n * - **Composable**: Results can be chained with `andThen`, `map`, etc.\n * - **No null checks**: Eliminates need for `if (value == null)` checks\n *\n * @param value - The value that may be null or undefined\n * @param onNull - Function that returns an error when value is null/undefined\n * @returns A Result with the value if not null/undefined, otherwise the error from `onNull`\n *\n * @example\n * ```typescript\n * // Convert DOM element lookup\n * const element = fromNullable(\n * document.getElementById('app'),\n * () => 'ELEMENT_NOT_FOUND' as const\n * );\n *\n * // Convert optional property\n * const userId = fromNullable(\n * user.id,\n * () => 'USER_ID_MISSING' as const\n * );\n *\n * // Convert database query result\n * const record = fromNullable(\n * await db.find(id),\n * () => ({ type: 'NOT_FOUND' as const, id })\n * );\n * ```\n */\nexport function fromNullable<T, E>(\n value: T | null | undefined,\n onNull: () => E\n): Result<T, E> {\n return value != null ? ok(value) : err(onNull());\n}\n\n// =============================================================================\n// Transformers\n// =============================================================================\n\n/**\n * Transforms the success value of a Result.\n *\n * ## When to Use\n *\n * Use `map()` when:\n * - You need to transform a success value to another type\n * - You want to apply a pure function to the value\n * - You're building a pipeline of transformations\n * - The transformation cannot fail (use `andThen` if it can fail)\n *\n * ## Why Use This\n *\n * - **Functional style**: Composable, chainable transformations\n * - **Error-preserving**: Errors pass through unchanged\n * - **Type-safe**: TypeScript tracks the transformation\n * - **No unwrapping**: Avoids manual `if (r.ok)` checks\n *\n * @param r - The Result to transform\n * @param fn - Pure function that transforms the success value (must not throw)\n * @returns A new Result with the transformed value, or the original error if `r` was an error\n *\n * @example\n * ```typescript\n * // Transform numeric value\n * const doubled = map(ok(21), n => n * 2);\n * // doubled: { ok: true, value: 42 }\n *\n * // Transform object property\n * const name = map(fetchUser(id), user => user.name);\n *\n * // Chain transformations\n * const formatted = map(\n * map(parseNumber(input), n => n * 2),\n * n => `Result: ${n}`\n * );\n * ```\n */\nexport function map<T, U, E, C>(\n r: Result<T, E, C>,\n fn: (value: T) => U\n): Result<U, E, C> {\n return r.ok ? ok(fn(r.value)) : r;\n}\n\n/**\n * Transforms the error value of a Result.\n *\n * ## When to Use\n *\n * Use `mapError()` when:\n * - You need to normalize or transform error types\n * - You want to convert errors to a different error type\n * - You're building error handling pipelines\n * - You need to format error messages or codes\n *\n * ## Why Use This\n *\n * - **Error normalization**: Convert errors to a common format\n * - **Type transformation**: Change error type while preserving value type\n * - **Composable**: Can be chained with other transformers\n * - **Success-preserving**: Success values pass through unchanged\n *\n * @param r - The Result to transform\n * @param fn - Function that transforms the error value (must not throw)\n * @returns A new Result with the original value, or the transformed error if `r` was an error\n *\n * @example\n * ```typescript\n * // Normalize error codes\n * const normalized = mapError(err('not_found'), e => e.toUpperCase());\n * // normalized: { ok: false, error: 'NOT_FOUND' }\n *\n * // Convert error types\n * const typed = mapError(\n * err('404'),\n * code => ({ type: 'HTTP_ERROR' as const, status: parseInt(code) })\n * );\n *\n * // Format error messages\n * const formatted = mapError(\n * err('PARSE_ERROR'),\n * code => `Failed to parse: ${code}`\n * );\n * ```\n */\nexport function mapError<T, E, F, C>(\n r: Result<T, E, C>,\n fn: (error: E) => F\n): Result<T, F, C> {\n return r.ok ? r : err(fn(r.error), { cause: r.cause });\n}\n\n/**\n * Pattern matches on a Result, calling the appropriate handler.\n *\n * ## When to Use\n *\n * Use `match()` when:\n * - You need to handle both success and error cases\n * - You want to transform a Result to a different type\n * - You need exhaustive handling (both cases must be handled)\n * - You're building user-facing messages or responses\n *\n * ## Why Use This\n *\n * - **Exhaustive**: Forces you to handle both success and error cases\n * - **Type-safe**: TypeScript ensures both handlers are provided\n * - **Functional**: Pattern matching style, similar to Rust's `match` or Haskell's `case`\n * - **Single expression**: Can be used in expressions, not just statements\n *\n * @param r - The Result to match\n * @param handlers - Object with `ok` and `err` handler functions\n * @param handlers.ok - Function called with the success value\n * @param handlers.err - Function called with the error and optional cause\n * @returns The return value of the appropriate handler (both must return the same type `R`)\n *\n * @example\n * ```typescript\n * // Build user-facing messages\n * const message = match(result, {\n * ok: (user) => `Hello ${user.name}`,\n * err: (error) => `Error: ${error}`,\n * });\n *\n * // Transform to API response\n * const response = match(operation(), {\n * ok: (data) => ({ status: 200, body: data }),\n * err: (error) => ({ status: 400, error: String(error) }),\n * });\n *\n * // Handle with cause\n * const response = match(result, {\n * ok: (value) => ({ status: 'success', data: value }),\n * err: (error, cause) => ({ status: 'error', error, cause }),\n * });\n * ```\n */\nexport function match<T, E, C, R>(\n r: Result<T, E, C>,\n handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }\n): R {\n return r.ok ? handlers.ok(r.value) : handlers.err(r.error, r.cause);\n}\n\n/**\n * Chains Results together (flatMap/monadic bind).\n *\n * ## When to Use\n *\n * Use `andThen()` when:\n * - You need to chain operations that can fail\n * - The next operation depends on the previous success value\n * - You're building a pipeline of dependent operations\n * - You want to avoid nested `if (r.ok)` checks\n *\n * ## Why Use This Instead of `map`\n *\n * - **Can fail**: The chained function returns a Result (can fail)\n * - **Short-circuits**: If first Result fails, second operation never runs\n * - **Error accumulation**: Errors from both operations are in the union\n * - **Composable**: Can chain multiple operations together\n *\n * ## Common Pattern\n *\n * This is the fundamental building block for Result pipelines:\n * ```typescript\n * andThen(operation1(), value1 =>\n * andThen(operation2(value1), value2 =>\n * ok({ value1, value2 })\n * )\n * )\n * ```\n *\n * @param r - The first Result\n * @param fn - Function that takes the success value and returns a new Result (may fail)\n * @returns The Result from `fn` if `r` was successful, otherwise the original error\n *\n * @example\n * ```typescript\n * // Chain dependent operations\n * const userPosts = andThen(\n * fetchUser('1'),\n * user => fetchPosts(user.id)\n * );\n *\n * // Build complex pipelines\n * const result = andThen(parseInput(input), parsed =>\n * andThen(validate(parsed), validated =>\n * process(validated)\n * )\n * );\n *\n * // Chain with different error types\n * const data = andThen(\n * fetchUser(id), // Returns Result<User, 'FETCH_ERROR'>\n * user => fetchPosts(user.id) // Returns Result<Post[], 'NOT_FOUND'>\n * );\n * // data.error: 'FETCH_ERROR' | 'NOT_FOUND'\n * ```\n */\nexport function andThen<T, U, E, F, C1, C2>(\n r: Result<T, E, C1>,\n fn: (value: T) => Result<U, F, C2>\n): Result<U, E | F, C1 | C2> {\n return r.ok ? fn(r.value) : r;\n}\n\n/**\n * Executes a side effect on a successful Result without changing it.\n *\n * ## When to Use\n *\n * Use `tap()` when:\n * - You need to log, debug, or observe success values\n * - You want to perform side effects in a pipeline\n * - You need to mutate external state based on success\n * - You're debugging and want to inspect values without breaking the chain\n *\n * ## Why Use This\n *\n * - **Non-breaking**: Doesn't change the Result, just performs side effect\n * - **Composable**: Can be inserted anywhere in a pipeline\n * - **Type-preserving**: Returns the same Result type\n * - **Lazy**: Side effect only runs if Result is successful\n *\n * @param r - The Result to tap\n * @param fn - Side effect function called with the success value (return value ignored)\n * @returns The original Result unchanged (for chaining)\n *\n * @example\n * ```typescript\n * // Log success values\n * const logged = tap(result, user => console.log('Got user:', user.name));\n * // logged === result, but console.log was called\n *\n * // Debug in pipeline\n * const debugged = pipe(\n * fetchUser(id),\n * r => tap(r, user => console.log('Fetched:', user)),\n * r => map(r, user => user.name)\n * );\n *\n * // Mutate external state\n * const tracked = tap(result, data => {\n * analytics.track('operation_success', data);\n * });\n * ```\n */\nexport function tap<T, E, C>(\n r: Result<T, E, C>,\n fn: (value: T) => void\n): Result<T, E, C> {\n if (r.ok) fn(r.value);\n return r;\n}\n\n/**\n * Executes a side effect on an error Result without changing it.\n *\n * ## When to Use\n *\n * Use `tapError()` when:\n * - You need to log, debug, or observe error values\n * - You want to perform side effects on errors in a pipeline\n * - You need to report errors to external systems (logging, monitoring)\n * - You're debugging and want to inspect errors without breaking the chain\n *\n * ## Why Use This\n *\n * - **Non-breaking**: Doesn't change the Result, just performs side effect\n * - **Composable**: Can be inserted anywhere in a pipeline\n * - **Type-preserving**: Returns the same Result type\n * - **Lazy**: Side effect only runs if Result is an error\n *\n * @param r - The Result to tap\n * @param fn - Side effect function called with the error and optional cause (return value ignored)\n * @returns The original Result unchanged (for chaining)\n *\n * @example\n * ```typescript\n * // Log errors\n * const logged = tapError(result, (error, cause) => {\n * console.error('Error:', error, cause);\n * });\n *\n * // Report to error tracking\n * const tracked = tapError(result, (error, cause) => {\n * errorTracker.report(error, cause);\n * });\n *\n * // Debug in pipeline\n * const debugged = pipe(\n * operation(),\n * r => tapError(r, (err, cause) => console.error('Failed:', err)),\n * r => mapError(r, err => 'FORMATTED_ERROR')\n * );\n * ```\n */\nexport function tapError<T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => void\n): Result<T, E, C> {\n if (!r.ok) fn(r.error, r.cause);\n return r;\n}\n\n/**\n * Transforms the success value of a Result, catching any errors thrown by the transform.\n *\n * ## When to Use\n *\n * Use `mapTry()` when:\n * - Your transform function might throw exceptions\n * - You want to convert transform errors to typed errors\n * - You're working with libraries that throw (e.g., JSON.parse, Date parsing)\n * - You need to handle both Result errors and transform exceptions\n *\n * ## Why Use This Instead of `map`\n *\n * - **Exception-safe**: Catches exceptions from the transform function\n * - **Error mapping**: Converts thrown exceptions to typed errors\n * - **Dual error handling**: Handles both Result errors and transform exceptions\n *\n * @param result - The Result to transform\n * @param transform - Function to transform the success value (may throw exceptions)\n * @param onError - Function to map thrown exceptions to a typed error\n * @returns A Result with:\n * - Transformed value if both Result and transform succeed\n * - Original error if Result was an error\n * - Transform error if transform threw an exception\n *\n * @example\n * ```typescript\n * // Safe JSON parsing\n * const parsed = mapTry(\n * ok('{\"key\": \"value\"}'),\n * JSON.parse,\n * () => 'PARSE_ERROR' as const\n * );\n *\n * // Safe date parsing\n * const date = mapTry(\n * ok('2024-01-01'),\n * str => new Date(str),\n * () => 'INVALID_DATE' as const\n * );\n *\n * // Transform with error details\n * const processed = mapTry(\n * result,\n * value => riskyTransform(value),\n * (cause) => ({ type: 'TRANSFORM_ERROR' as const, cause })\n * );\n * ```\n */\nexport function mapTry<T, U, E, F, C>(\n result: Result<T, E, C>,\n transform: (value: T) => U,\n onError: (cause: unknown) => F\n): Result<U, E | F, C | unknown> {\n if (!result.ok) return result;\n try {\n return ok(transform(result.value));\n } catch (error) {\n return err(onError(error), { cause: error });\n }\n}\n\n/**\n * Transforms the error value of a Result, catching any errors thrown by the transform.\n *\n * ## When to Use\n *\n * Use `mapErrorTry()` when:\n * - Your error transform function might throw exceptions\n * - You're doing complex error transformations (e.g., string formatting, object construction)\n * - You want to handle both Result errors and transform exceptions\n * - You need to safely normalize error types\n *\n * ## Why Use This Instead of `mapError`\n *\n * - **Exception-safe**: Catches exceptions from the error transform function\n * - **Error mapping**: Converts thrown exceptions to typed errors\n * - **Dual error handling**: Handles both Result errors and transform exceptions\n *\n * @param result - The Result to transform\n * @param transform - Function to transform the error value (may throw exceptions)\n * @param onError - Function to map thrown exceptions to a typed error\n * @returns A Result with:\n * - Original value if Result was successful\n * - Transformed error if both Result was error and transform succeeded\n * - Transform error if transform threw an exception\n *\n * @example\n * ```typescript\n * // Safe error formatting\n * const formatted = mapErrorTry(\n * err('not_found'),\n * e => e.toUpperCase(), // Might throw if e is not a string\n * () => 'FORMAT_ERROR' as const\n * );\n *\n * // Complex error transformation\n * const normalized = mapErrorTry(\n * result,\n * error => ({ type: 'NORMALIZED', message: String(error) }),\n * () => 'TRANSFORM_ERROR' as const\n * );\n * ```\n */\nexport function mapErrorTry<T, E, F, G, C>(\n result: Result<T, E, C>,\n transform: (error: E) => F,\n onError: (cause: unknown) => G\n): Result<T, F | G, C | unknown> {\n if (result.ok) return result;\n try {\n return err(transform(result.error), { cause: result.cause });\n } catch (error) {\n return err(onError(error), { cause: error });\n }\n}\n\n// =============================================================================\n// Batch Operations\n// =============================================================================\n\ntype AllValues<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> ? V : never;\n};\ntype AllErrors<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> ? E : never;\n}[number];\ntype AllCauses<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> ? C : never;\n}[number];\n\n/**\n * Combines multiple Results into one, requiring all to succeed.\n *\n * ## When to Use\n *\n * Use `all()` when:\n * - You have multiple independent operations that all must succeed\n * - You want to short-circuit on the first error (fail-fast)\n * - You need all values together (e.g., combining API responses)\n * - Performance matters (stops on first error, doesn't wait for all)\n *\n * ## Why Use This\n *\n * - **Fail-fast**: Stops immediately on first error (better performance)\n * - **Type-safe**: TypeScript infers the array type from input\n * - **Short-circuit**: Doesn't evaluate remaining Results after error\n * - **Composable**: Can be chained with other operations\n *\n * ## Important\n *\n * - **Short-circuits**: Returns first error immediately, doesn't wait for all Results\n * - **All must succeed**: If any Result fails, the entire operation fails\n * - **Use `allSettled`**: If you need to collect all errors (e.g., form validation)\n *\n * @param results - Array of Results to combine (all must succeed)\n * @returns A Result with an array of all success values, or the first error encountered\n *\n * @example\n * ```typescript\n * // Combine multiple successful Results\n * const combined = all([ok(1), ok(2), ok(3)]);\n * // combined: { ok: true, value: [1, 2, 3] }\n *\n * // Short-circuits on first error\n * const error = all([ok(1), err('ERROR'), ok(3)]);\n * // error: { ok: false, error: 'ERROR' }\n * // Note: ok(3) is never evaluated\n *\n * // Combine API responses\n * const data = all([\n * fetchUser(id),\n * fetchPosts(id),\n * fetchComments(id)\n * ]);\n * // data.value: [user, posts, comments] if all succeed\n * ```\n */\nexport function all<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): Result<AllValues<T>, AllErrors<T>, AllCauses<T>> {\n const values: unknown[] = [];\n for (const result of results) {\n if (!result.ok) {\n return result as unknown as Result<AllValues<T>, AllErrors<T>, AllCauses<T>>;\n }\n values.push(result.value);\n }\n return ok(values) as Result<AllValues<T>, AllErrors<T>, AllCauses<T>>;\n}\n\n/**\n * Combines multiple Results or Promises of Results into one (async version of `all`).\n *\n * ## When to Use\n *\n * Use `allAsync()` when:\n * - You have multiple async operations that all must succeed\n * - You want to run operations in parallel (better performance)\n * - You want to short-circuit on the first error (fail-fast)\n * - You need all values together from parallel operations\n *\n * ## Why Use This Instead of `all`\n *\n * - **Parallel execution**: All Promises start immediately (faster)\n * - **Async support**: Works with Promises and AsyncResults\n * - **Promise rejection handling**: Converts Promise rejections to `PromiseRejectedError`\n *\n * ## Important\n *\n * - **Short-circuits**: Returns first error immediately, cancels remaining operations\n * - **Parallel**: All operations start simultaneously (unlike sequential `andThen`)\n * - **Use `allSettledAsync`**: If you need to collect all errors\n *\n * @param results - Array of Results or Promises of Results to combine (all must succeed)\n * @returns A Promise resolving to a Result with an array of all success values, or the first error\n *\n * @example\n * ```typescript\n * // Parallel API calls\n * const combined = await allAsync([\n * fetchUser('1'),\n * fetchPosts('1'),\n * fetchComments('1')\n * ]);\n * // All three calls start simultaneously\n * // combined: { ok: true, value: [user, posts, comments] } if all succeed\n *\n * // Mix Results and Promises\n * const data = await allAsync([\n * ok(cachedUser), // Already resolved\n * fetchPosts(userId), // Promise\n * ]);\n * ```\n */\nexport async function allAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never },\n { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number] | PromiseRejectedError,\n { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number] | PromiseRejectionCause\n >\n> {\n type Values = { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never };\n type Errors = { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number] | PromiseRejectedError;\n type Causes = { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number] | PromiseRejectionCause;\n\n if (results.length === 0) {\n return ok([]) as Result<Values, Errors, Causes>;\n }\n\n return new Promise((resolve) => {\n let settled = false;\n let pendingCount = results.length;\n const values: unknown[] = new Array(results.length);\n\n for (let i = 0; i < results.length; i++) {\n const index = i;\n Promise.resolve(results[index])\n .catch((reason) => err(\n { type: \"PROMISE_REJECTED\" as const, cause: reason },\n { cause: { type: \"PROMISE_REJECTION\" as const, reason } as PromiseRejectionCause }\n ))\n .then((result) => {\n if (settled) return;\n\n if (!result.ok) {\n settled = true;\n resolve(result as Result<Values, Errors, Causes>);\n return;\n }\n\n values[index] = result.value;\n pendingCount--;\n\n if (pendingCount === 0) {\n resolve(ok(values) as Result<Values, Errors, Causes>);\n }\n });\n }\n });\n}\n\nexport type SettledError<E, C = unknown> = { error: E; cause?: C };\n\ntype AllSettledResult<T extends readonly Result<unknown, unknown, unknown>[]> = Result<\n AllValues<T>,\n SettledError<AllErrors<T>, AllCauses<T>>[]\n>;\n\n/**\n * Combines multiple Results, collecting all errors instead of short-circuiting.\n *\n * ## When to Use\n *\n * Use `allSettled()` when:\n * - You need to see ALL errors, not just the first one\n * - You're doing form validation (show all field errors)\n * - You want to collect partial results (some succeed, some fail)\n * - You need to process all Results regardless of failures\n *\n * ## Why Use This Instead of `all`\n *\n * - **Collects all errors**: Returns array of all errors, not just first\n * - **No short-circuit**: Evaluates all Results even if some fail\n * - **Partial success**: Can see which operations succeeded and which failed\n * - **Better UX**: Show users all validation errors at once\n *\n * ## Important\n *\n * - **No short-circuit**: All Results are evaluated (slower if many fail early)\n * - **Error array**: Returns array of `{ error, cause }` objects, not single error\n * - **Use `all`**: If you want fail-fast behavior (better performance)\n *\n * @param results - Array of Results to combine (all are evaluated)\n * @returns A Result with:\n * - Array of all success values if all succeed\n * - Array of `{ error, cause }` objects if any fail\n *\n * @example\n * ```typescript\n * // Form validation - show all errors\n * const validated = allSettled([\n * validateEmail(email),\n * validatePassword(password),\n * validateAge(age),\n * ]);\n * // If email and password fail:\n * // { ok: false, error: [\n * // { error: 'INVALID_EMAIL' },\n * // { error: 'WEAK_PASSWORD' }\n * // ]}\n *\n * // Collect partial results\n * const results = allSettled([\n * fetchUser('1'), // succeeds\n * fetchUser('2'), // fails\n * fetchUser('3'), // succeeds\n * ]);\n * // Can see which succeeded and which failed\n * ```\n */\nexport function allSettled<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): AllSettledResult<T> {\n const values: unknown[] = [];\n const errors: SettledError<unknown>[] = [];\n\n for (const result of results) {\n if (result.ok) {\n values.push(result.value);\n } else {\n errors.push({ error: result.error, cause: result.cause });\n }\n }\n\n if (errors.length > 0) {\n return err(errors) as unknown as AllSettledResult<T>;\n }\n\n return ok(values) as unknown as AllSettledResult<T>;\n}\n\n/**\n * Splits an array of Results into separate arrays of success values and errors.\n *\n * ## When to Use\n *\n * Use `partition()` when:\n * - You have an array of Results and need to separate successes from failures\n * - You want to process successes and errors separately\n * - You're collecting results from multiple operations (some may fail)\n * - You need to handle partial success scenarios\n *\n * ## Why Use This\n *\n * - **Simple separation**: One call splits successes and errors\n * - **Type-safe**: TypeScript knows `values` is `T[]` and `errors` is `E[]`\n * - **No unwrapping**: Doesn't require manual `if (r.ok)` checks\n * - **Preserves order**: Maintains original array order in both arrays\n *\n * ## Common Pattern\n *\n * Often used after `Promise.all()` with Results:\n * ```typescript\n * const results = await Promise.all(ids.map(id => fetchUser(id)));\n * const { values: users, errors } = partition(results);\n * // Process successful users, handle errors separately\n * ```\n *\n * @param results - Array of Results to partition\n * @returns An object with:\n * - `values`: Array of all success values (type `T[]`)\n * - `errors`: Array of all error values (type `E[]`)\n *\n * @example\n * ```typescript\n * // Split successes and errors\n * const results = [ok(1), err('ERROR_1'), ok(3), err('ERROR_2')];\n * const { values, errors } = partition(results);\n * // values: [1, 3]\n * // errors: ['ERROR_1', 'ERROR_2']\n *\n * // Process batch operations\n * const userResults = await Promise.all(userIds.map(id => fetchUser(id)));\n * const { values: users, errors: fetchErrors } = partition(userResults);\n *\n * // Process successful users\n * users.forEach(user => processUser(user));\n *\n * // Handle errors\n * fetchErrors.forEach(error => logError(error));\n * ```\n */\nexport function partition<T, E, C>(\n results: readonly Result<T, E, C>[]\n): { values: T[]; errors: E[] } {\n const values: T[] = [];\n const errors: E[] = [];\n\n for (const result of results) {\n if (result.ok) {\n values.push(result.value);\n } else {\n errors.push(result.error);\n }\n }\n\n return { values, errors };\n}\n\ntype AnyValue<T extends readonly Result<unknown, unknown, unknown>[]> =\n T[number] extends Result<infer U, unknown, unknown> ? U : never;\ntype AnyErrors<T extends readonly Result<unknown, unknown, unknown>[]> = {\n -readonly [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> ? E : never;\n}[number];\ntype AnyCauses<T extends readonly Result<unknown, unknown, unknown>[]> = {\n -readonly [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> ? C : never;\n}[number];\n\n/**\n * Returns the first successful Result from an array (succeeds fast).\n *\n * ## When to Use\n *\n * Use `any()` when:\n * - You have multiple fallback options and need the first that succeeds\n * - You're trying multiple strategies (e.g., cache → DB → API)\n * - You want fail-fast success (stops on first success)\n * - You have redundant data sources and any one will do\n *\n * ## Why Use This\n *\n * - **Succeeds fast**: Returns immediately on first success (better performance)\n * - **Fallback pattern**: Perfect for trying multiple options\n * - **Short-circuits**: Stops evaluating after first success\n * - **Type-safe**: TypeScript infers the success type\n *\n * ## Important\n *\n * - **First success wins**: Returns first successful Result, ignores rest\n * - **All errors**: If all fail, returns first error (not all errors)\n * - **Empty array**: Returns `EmptyInputError` if array is empty\n * - **Use `all`**: If you need ALL to succeed\n *\n * @param results - Array of Results to check (evaluated in order)\n * @returns The first successful Result, or first error if all fail, or `EmptyInputError` if empty\n *\n * @example\n * ```typescript\n * // Try multiple fallback strategies\n * const data = any([\n * fetchFromCache(id),\n * fetchFromDB(id),\n * fetchFromAPI(id)\n * ]);\n * // Returns first that succeeds\n *\n * // Try multiple formats\n * const parsed = any([\n * parseJSON(input),\n * parseXML(input),\n * parseYAML(input)\n * ]);\n *\n * // All errors case\n * const allErrors = any([err('A'), err('B'), err('C')]);\n * // allErrors: { ok: false, error: 'A' } (first error)\n * ```\n */\nexport function any<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): Result<AnyValue<T>, AnyErrors<T> | EmptyInputError, AnyCauses<T>> {\n type ReturnErr = Result<never, AnyErrors<T> | EmptyInputError, AnyCauses<T>>;\n type ReturnOk = Result<AnyValue<T>, never, AnyCauses<T>>;\n\n if (results.length === 0) {\n return err({\n type: \"EMPTY_INPUT\",\n message: \"any() requires at least one Result\",\n }) as ReturnErr;\n }\n let firstError: Result<never, unknown, unknown> | null = null;\n for (const result of results) {\n if (result.ok) return result as ReturnOk;\n if (!firstError) firstError = result;\n }\n return firstError as ReturnErr;\n}\n\ntype AnyAsyncValue<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> =\n Awaited<T[number]> extends Result<infer U, unknown, unknown> ? U : never;\ntype AnyAsyncErrors<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> = {\n -readonly [K in keyof T]: Awaited<T[K]> extends Result<unknown, infer E, unknown>\n ? E\n : never;\n}[number];\ntype AnyAsyncCauses<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> = {\n -readonly [K in keyof T]: Awaited<T[K]> extends Result<unknown, unknown, infer C>\n ? C\n : never;\n}[number];\n\n/**\n * Returns the first successful Result from an array of Results or Promises (async version of `any`).\n *\n * ## When to Use\n *\n * Use `anyAsync()` when:\n * - You have multiple async fallback options and need the first that succeeds\n * - You're trying multiple async strategies in parallel (cache → DB → API)\n * - You want fail-fast success from parallel operations\n * - You have redundant async data sources and any one will do\n *\n * ## Why Use This Instead of `any`\n *\n * - **Parallel execution**: All Promises start immediately (faster)\n * - **Async support**: Works with Promises and AsyncResults\n * - **Promise rejection handling**: Converts Promise rejections to `PromiseRejectedError`\n *\n * ## Important\n *\n * - **First success wins**: Returns first successful Result (from any Promise)\n * - **Parallel**: All operations run simultaneously\n * - **All errors**: If all fail, returns first error encountered\n *\n * @param results - Array of Results or Promises of Results to check (all start in parallel)\n * @returns A Promise resolving to the first successful Result, or first error if all fail\n *\n * @example\n * ```typescript\n * // Try multiple async fallbacks in parallel\n * const data = await anyAsync([\n * fetchFromCache(id), // Fastest wins\n * fetchFromDB(id),\n * fetchFromAPI(id)\n * ]);\n *\n * // Try multiple API endpoints\n * const response = await anyAsync([\n * fetch('/api/v1/data'),\n * fetch('/api/v2/data'),\n * fetch('/backup-api/data')\n * ]);\n * ```\n */\nexport async function anyAsync<\n const T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[],\n>(\n results: T\n): Promise<\n Result<AnyAsyncValue<T>, AnyAsyncErrors<T> | EmptyInputError | PromiseRejectedError, AnyAsyncCauses<T> | PromiseRejectionCause>\n> {\n type ReturnErr = Result<\n never,\n AnyAsyncErrors<T> | EmptyInputError | PromiseRejectedError,\n AnyAsyncCauses<T> | PromiseRejectionCause\n >;\n type ReturnOk = Result<AnyAsyncValue<T>, never, AnyAsyncCauses<T>>;\n\n if (results.length === 0) {\n return err({\n type: \"EMPTY_INPUT\",\n message: \"anyAsync() requires at least one Result\",\n }) as ReturnErr;\n }\n\n return new Promise((resolve) => {\n let settled = false;\n let pendingCount = results.length;\n let firstError: Result<never, unknown, unknown> | null = null;\n\n for (const item of results) {\n Promise.resolve(item)\n .catch((reason) =>\n err(\n { type: \"PROMISE_REJECTED\" as const, cause: reason },\n { cause: { type: \"PROMISE_REJECTION\" as const, reason } as PromiseRejectionCause }\n )\n )\n .then((result) => {\n if (settled) return;\n\n if (result.ok) {\n settled = true;\n resolve(result as ReturnOk);\n return;\n }\n\n if (!firstError) firstError = result;\n pendingCount--;\n\n if (pendingCount === 0) {\n resolve(firstError as ReturnErr);\n }\n });\n }\n });\n}\n\ntype AllAsyncValues<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> = {\n [K in keyof T]: Awaited<T[K]> extends Result<infer V, unknown, unknown> ? V : never;\n};\ntype AllAsyncErrors<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> = {\n [K in keyof T]: Awaited<T[K]> extends Result<unknown, infer E, unknown> ? E : never;\n}[number];\ntype AllAsyncCauses<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> = {\n [K in keyof T]: Awaited<T[K]> extends Result<unknown, unknown, infer C> ? C : never;\n}[number];\n\n/**\n * Combines multiple Results or Promises of Results, collecting all errors (async version of `allSettled`).\n *\n * ## When to Use\n *\n * Use `allSettledAsync()` when:\n * - You have multiple async operations and need ALL errors\n * - You're doing async form validation (show all field errors)\n * - You want to run operations in parallel and collect all results\n * - You need partial results from parallel operations\n *\n * ## Why Use This Instead of `allSettled`\n *\n * - **Parallel execution**: All Promises start immediately (faster)\n * - **Async support**: Works with Promises and AsyncResults\n * - **Promise rejection handling**: Converts Promise rejections to `PromiseRejectedError`\n *\n * ## Important\n *\n * - **No short-circuit**: All operations complete (even if some fail)\n * - **Parallel**: All operations run simultaneously\n * - **Error array**: Returns array of `{ error, cause }` objects\n *\n * @param results - Array of Results or Promises of Results to combine (all are evaluated)\n * @returns A Promise resolving to a Result with:\n * - Array of all success values if all succeed\n * - Array of `{ error, cause }` objects if any fail\n *\n * @example\n * ```typescript\n * // Async form validation\n * const validated = await allSettledAsync([\n * validateEmailAsync(email),\n * validatePasswordAsync(password),\n * checkUsernameAvailableAsync(username),\n * ]);\n *\n * // Parallel API calls with error collection\n * const results = await allSettledAsync([\n * fetchUser('1'),\n * fetchUser('2'),\n * fetchUser('3'),\n * ]);\n * // Can see which succeeded and which failed\n * ```\n */\nexport async function allSettledAsync<\n const T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[],\n>(\n results: T\n): Promise<Result<AllAsyncValues<T>, SettledError<AllAsyncErrors<T> | PromiseRejectedError, AllAsyncCauses<T> | PromiseRejectionCause>[]>> {\n const settled = await Promise.all(\n results.map((item) =>\n Promise.resolve(item)\n .then((result) => ({ status: \"result\" as const, result }))\n .catch((reason) => ({\n status: \"rejected\" as const,\n error: { type: \"PROMISE_REJECTED\" as const, cause: reason } as PromiseRejectedError,\n cause: { type: \"PROMISE_REJECTION\" as const, reason } as PromiseRejectionCause,\n }))\n )\n );\n\n const values: unknown[] = [];\n const errors: SettledError<unknown, unknown>[] = [];\n\n for (const item of settled) {\n if (item.status === \"rejected\") {\n errors.push({ error: item.error, cause: item.cause });\n } else if (item.result.ok) {\n values.push(item.result.value);\n } else {\n errors.push({ error: item.result.error, cause: item.result.cause });\n }\n }\n\n if (errors.length > 0) {\n return err(errors) as unknown as Result<AllAsyncValues<T>, SettledError<AllAsyncErrors<T> | PromiseRejectedError, AllAsyncCauses<T> | PromiseRejectionCause>[]>;\n }\n return ok(values) as unknown as Result<AllAsyncValues<T>, SettledError<AllAsyncErrors<T> | PromiseRejectedError, AllAsyncCauses<T> | PromiseRejectionCause>[]>;\n}\n","/**\n * @jagreehal/workflow/workflow\n *\n * Workflow orchestration with createWorkflow.\n * Use this for typed async workflows with automatic error inference.\n */\n\nimport {\n run,\n ok,\n err,\n createEarlyExit,\n isEarlyExit,\n type EarlyExit,\n type StepFailureMeta,\n type Result,\n type AsyncResult,\n type UnexpectedError,\n type RunStep,\n type WorkflowEvent,\n type StepOptions,\n type RetryOptions,\n type TimeoutOptions,\n type ErrorOf,\n type CauseOf,\n} from \"./core\";\n\n// Re-export types that workflow users commonly need\nexport type {\n Result,\n AsyncResult,\n UnexpectedError,\n RunStep,\n WorkflowEvent,\n StepOptions,\n} from \"./core\";\n\n// =============================================================================\n// Step Cache Types\n// =============================================================================\n\n/**\n * Interface for step result caching.\n * Implement this interface to provide custom caching strategies.\n * A simple Map<string, Result> works for in-memory caching.\n *\n * Note: Cache stores Result<unknown, unknown, unknown> because different steps\n * have different value/error/cause types. The actual runtime values are preserved;\n * only the static types are widened. For error results, the cause value is encoded\n * in CachedErrorCause to preserve metadata for proper replay.\n *\n * @example\n * // Simple in-memory cache\n * const cache = new Map<string, Result<unknown, unknown, unknown>>();\n *\n * // Or implement custom cache with TTL, LRU, etc.\n * const cache: StepCache = {\n * get: (key) => myCache.get(key),\n * set: (key, result) => myCache.set(key, result, { ttl: 60000 }),\n * has: (key) => myCache.has(key),\n * delete: (key) => myCache.delete(key),\n * clear: () => myCache.clear(),\n * };\n */\nexport interface StepCache {\n get(key: string): Result<unknown, unknown, unknown> | undefined;\n set(key: string, result: Result<unknown, unknown, unknown>): void;\n has(key: string): boolean;\n delete(key: string): boolean;\n clear(): void;\n}\n\n/**\n * Entry for a saved step result with optional metadata.\n * The meta field preserves origin information for proper replay.\n */\nexport interface ResumeStateEntry {\n result: Result<unknown, unknown, unknown>;\n /** Optional metadata for error origin (from step_complete event) */\n meta?: StepFailureMeta;\n}\n\n/**\n * Resume state for workflow replay.\n * Pre-populate step results to skip execution on resume.\n *\n * Note: When saving to persistent storage, you may need custom serialization\n * for complex cause types. JSON.stringify works for simple values, but Error\n * objects and other non-plain types require special handling.\n *\n * @example\n * // Collect from step_complete events using the helper\n * const collector = createStepCollector();\n * const workflow = createWorkflow({ fetchUser }, {\n * onEvent: collector.handleEvent,\n * });\n * // Later: collector.getState() returns ResumeState\n *\n * @example\n * // Resume with saved state\n * const workflow = createWorkflow({ fetchUser }, {\n * resumeState: { steps: savedSteps }\n * });\n */\nexport interface ResumeState {\n /** Map of step keys to their cached results with optional metadata */\n steps: Map<string, ResumeStateEntry>;\n}\n\n/**\n * Create a collector for step results to build resume state.\n *\n * ## When to Use\n *\n * Use `createStepCollector` when you need to:\n * - **Save workflow state** for later replay/resume\n * - **Persist step results** to a database or file system\n * - **Build resume state** from workflow execution\n * - **Enable workflow replay** after application restarts\n *\n * ## Why Use This Instead of Manual Collection\n *\n * - **Automatic filtering**: Only collects `step_complete` events (ignores other events)\n * - **Metadata preservation**: Captures both result and meta for proper error replay\n * - **Type-safe**: Returns properly typed `ResumeState`\n * - **Convenient API**: Simple `handleEvent` → `getState` pattern\n *\n * ## How It Works\n *\n * 1. Create collector and pass `handleEvent` to workflow's `onEvent` option\n * 2. Workflow emits `step_complete` events for keyed steps\n * 3. Collector automatically captures these events\n * 4. Call `getState()` to get the collected `ResumeState`\n * 5. Persist state (e.g., to database) for later resume\n *\n * ## Important Notes\n *\n * - Only steps with a `key` option are collected (unkeyed steps are not saved)\n * - The collector preserves error metadata for proper replay behavior\n * - State can be serialized to JSON (but complex cause types may need custom handling)\n *\n * @returns An object with:\n * - `handleEvent`: Function to pass to workflow's `onEvent` option\n * - `getState`: Get collected resume state (call after workflow execution)\n * - `clear`: Clear all collected state\n *\n * @example\n * ```typescript\n * // Collect state during workflow execution\n * const collector = createStepCollector();\n *\n * const workflow = createWorkflow({ fetchUser, fetchPosts }, {\n * onEvent: collector.handleEvent, // Pass collector's handler\n * });\n *\n * await workflow(async (step) => {\n * // Only keyed steps are collected\n * const user = await step(() => fetchUser(\"1\"), { key: \"user:1\" });\n * const posts = await step(() => fetchPosts(user.id), { key: `posts:${user.id}` });\n * return { user, posts };\n * });\n *\n * // Get collected state for persistence\n * const state = collector.getState();\n * // state.steps contains: 'user:1' and 'posts:1' entries\n *\n * // Save to database\n * await db.saveWorkflowState(workflowId, state);\n * ```\n *\n * @example\n * ```typescript\n * // Resume workflow from saved state\n * const savedState = await db.loadWorkflowState(workflowId);\n * const workflow = createWorkflow({ fetchUser, fetchPosts }, {\n * resumeState: savedState // Pre-populate cache from saved state\n * });\n *\n * // Cached steps skip execution, new steps run normally\n * await workflow(async (step) => {\n * const user = await step(() => fetchUser(\"1\"), { key: \"user:1\" }); // Cache hit\n * const posts = await step(() => fetchPosts(user.id), { key: `posts:${user.id}` }); // Cache hit\n * return { user, posts };\n * });\n * ```\n */\nexport function createStepCollector(): {\n handleEvent: (event: WorkflowEvent<unknown>) => void;\n getState: () => ResumeState;\n clear: () => void;\n} {\n const steps = new Map<string, ResumeStateEntry>();\n\n return {\n handleEvent: (event: WorkflowEvent<unknown>) => {\n if (isStepComplete(event)) {\n steps.set(event.stepKey, { result: event.result, meta: event.meta });\n }\n },\n getState: () => ({ steps: new Map(steps) }),\n clear: () => steps.clear(),\n };\n}\n\n// =============================================================================\n// Cache Entry Encoding (preserves StepFailureMeta for proper replay)\n// =============================================================================\n\n/**\n * Marker for cached error entries that include step failure metadata.\n * This allows us to preserve origin:\"throw\" vs origin:\"result\" when replaying,\n * while also preserving the original cause value for direct cache access.\n * @internal\n */\ninterface CachedErrorCause<C = unknown> {\n __cachedMeta: true;\n /** The original cause from the step result (preserved for direct access) */\n originalCause: C;\n /** Metadata for proper replay behavior */\n meta: StepFailureMeta;\n}\n\nfunction isCachedErrorCause(cause: unknown): cause is CachedErrorCause {\n return (\n typeof cause === \"object\" &&\n cause !== null &&\n (cause as CachedErrorCause).__cachedMeta === true\n );\n}\n\n/**\n * Encode an error result for caching, preserving both the original cause\n * and metadata needed for proper replay.\n */\nfunction encodeCachedError<E, C>(\n error: E,\n meta: StepFailureMeta,\n originalCause: C\n): Result<never, E, CachedErrorCause<C>> {\n return err(error, {\n cause: { __cachedMeta: true, originalCause, meta } as CachedErrorCause<C>,\n });\n}\n\nfunction decodeCachedMeta(cause: unknown): StepFailureMeta {\n if (isCachedErrorCause(cause)) {\n return cause.meta;\n }\n // Fallback for any non-encoded cause (shouldn't happen, but safe default)\n return { origin: \"result\", resultCause: cause };\n}\n\n// =============================================================================\n// createWorkflow Types\n// =============================================================================\n\n/**\n * Constraint for Result-returning functions\n * Used by createWorkflow to ensure only valid functions are passed\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyResultFn = (...args: any[]) => Result<any, any, any> | Promise<Result<any, any, any>>;\n\n/**\n * Extract union of error types from a deps object\n * Example: ErrorsOfDeps<{ fetchUser: typeof fetchUser, fetchPosts: typeof fetchPosts }>\n * yields: 'NOT_FOUND' | 'FETCH_ERROR'\n */\nexport type ErrorsOfDeps<Deps extends Record<string, AnyResultFn>> = {\n [K in keyof Deps]: ErrorOf<Deps[K]>;\n}[keyof Deps];\n\n/**\n * Extract union of cause types from a deps object.\n * Example: CausesOfDeps<{ fetchUser: typeof fetchUser }> where fetchUser returns Result<User, \"NOT_FOUND\", Error>\n * yields: Error\n *\n * Note: This represents the domain cause types from declared functions.\n * However, workflow results may also have unknown causes from step.try failures\n * or uncaught exceptions, so the actual Result cause type is `unknown`.\n */\nexport type CausesOfDeps<Deps extends Record<string, AnyResultFn>> =\n CauseOf<Deps[keyof Deps]>;\n\n/**\n * Non-strict workflow options\n * Returns E | UnexpectedError (safe default)\n */\nexport type WorkflowOptions<E, C = void> = {\n onError?: (error: E | UnexpectedError, stepName?: string) => void;\n /** Unified event stream for workflow and step lifecycle */\n onEvent?: (event: WorkflowEvent<E | UnexpectedError>, ctx: C) => void;\n /** Create per-run context for event correlation */\n createContext?: () => C;\n /** Step result cache - only steps with a `key` option are cached */\n cache?: StepCache;\n /** Pre-populate cache from saved state for workflow resume */\n resumeState?: ResumeState | (() => ResumeState | Promise<ResumeState>);\n catchUnexpected?: never; // prevent footgun: can't use without strict: true\n strict?: false; // default\n};\n\n/**\n * Strict workflow options\n * Returns E | U (closed error union)\n */\nexport type WorkflowOptionsStrict<E, U, C = void> = {\n strict: true; // discriminator\n catchUnexpected: (cause: unknown) => U;\n onError?: (error: E | U, stepName?: string) => void;\n /** Unified event stream for workflow and step lifecycle */\n onEvent?: (event: WorkflowEvent<E | U>, ctx: C) => void;\n /** Create per-run context for event correlation */\n createContext?: () => C;\n /** Step result cache - only steps with a `key` option are cached */\n cache?: StepCache;\n /** Pre-populate cache from saved state for workflow resume */\n resumeState?: ResumeState | (() => ResumeState | Promise<ResumeState>);\n};\n\n/**\n * Workflow return type (non-strict)\n * Supports both argument-less and argument-passing call patterns\n *\n * Note: Cause type is `unknown` because:\n * - step.try errors have thrown values as cause\n * - Uncaught exceptions produce unknown causes\n * - Different steps may have different cause types\n * The cause IS preserved at runtime; narrow based on error type if needed.\n */\nexport interface Workflow<E, Deps> {\n /**\n * Execute workflow without arguments (original API)\n */\n <T>(fn: (step: RunStep<E>, deps: Deps) => T | Promise<T>): AsyncResult<T, E | UnexpectedError, unknown>;\n\n /**\n * Execute workflow with typed arguments\n * @param args - Typed arguments passed to the callback (type inferred at call site)\n * @param fn - Callback receives (step, deps, args)\n */\n <T, Args>(\n args: Args,\n fn: (step: RunStep<E>, deps: Deps, args: Args) => T | Promise<T>\n ): AsyncResult<T, E | UnexpectedError, unknown>;\n}\n\n/**\n * Workflow return type (strict)\n * Supports both argument-less and argument-passing call patterns\n *\n * Note: Cause type is `unknown` because catchUnexpected receives thrown\n * values which have unknown type.\n */\nexport interface WorkflowStrict<E, U, Deps> {\n /**\n * Execute workflow without arguments (original API)\n */\n <T>(fn: (step: RunStep<E>, deps: Deps) => T | Promise<T>): AsyncResult<T, E | U, unknown>;\n\n /**\n * Execute workflow with typed arguments\n * @param args - Typed arguments passed to the callback (type inferred at call site)\n * @param fn - Callback receives (step, deps, args)\n */\n <T, Args>(\n args: Args,\n fn: (step: RunStep<E>, deps: Deps, args: Args) => T | Promise<T>\n ): AsyncResult<T, E | U, unknown>;\n}\n\n// =============================================================================\n// createWorkflow - Automatic Error Type Inference\n// =============================================================================\n\n/**\n * Create a typed workflow with automatic error inference.\n *\n * ## When to Use `createWorkflow`\n *\n * Use `createWorkflow` when you have:\n * - **Multiple dependent async operations** that need to run sequentially\n * - **Complex error handling** where you want type-safe error unions\n * - **Need for observability** via event streams (onEvent)\n * - **Step caching** requirements for expensive operations\n * - **Resume/replay** capabilities for long-running workflows\n * - **Human-in-the-loop** workflows requiring approvals\n *\n * ## Why Use `createWorkflow` Instead of `run()`\n *\n * 1. **Automatic Error Type Inference**: Errors are computed from your declared functions\n * - No manual error union management\n * - TypeScript ensures all possible errors are handled\n * - Refactoring is safer - adding/removing functions updates error types automatically\n *\n * 2. **Step Caching**: Expensive operations can be cached by key\n * - Prevents duplicate API calls\n * - Useful for idempotent operations\n * - Supports resume state for workflow replay\n *\n * 3. **Event Stream**: Built-in observability via `onEvent`\n * - Track workflow and step lifecycle\n * - Monitor performance (durationMs)\n * - Build dashboards and debugging tools\n *\n * 4. **Resume State**: Save and replay workflows\n * - Useful for long-running processes\n * - Supports human-in-the-loop workflows\n * - Enables workflow persistence across restarts\n *\n * ## How It Works\n *\n * 1. **Declare Dependencies**: Pass an object of Result-returning functions\n * 2. **Automatic Inference**: Error types are extracted from function return types\n * 3. **Execute Workflow**: Call the returned workflow function with your logic\n * 4. **Early Exit**: `step()` unwraps Results - on error, workflow exits immediately\n *\n * ## Error Type Inference\n *\n * The error union is automatically computed from all declared functions:\n * - Each function's error type is extracted\n * - Union of all errors is created\n * - `UnexpectedError` is added for uncaught exceptions (unless strict mode)\n *\n * ## Strict Mode\n *\n * Use `strict: true` with `catchUnexpected` for closed error unions:\n * - Removes `UnexpectedError` from the union\n * - All errors must be explicitly handled\n * - Useful for production code where you want exhaustive error handling\n *\n * @param deps - Object mapping names to Result-returning functions.\n * These functions must return `Result<T, E>` or `Promise<Result<T, E>>`.\n * The error types (`E`) from all functions are automatically combined into a union.\n * @param options - Optional configuration:\n * - `onEvent`: Callback for workflow/step lifecycle events\n * - `onError`: Callback for error logging/debugging\n * - `cache`: Step result cache (Map or custom StepCache implementation)\n * - `resumeState`: Pre-populated step results for workflow replay\n * - `createContext`: Factory for per-run context (passed to onEvent)\n * - `strict`: Enable strict mode (requires `catchUnexpected`)\n * - `catchUnexpected`: Map uncaught exceptions to typed errors (required in strict mode)\n *\n * @returns A workflow function that accepts your workflow logic and returns an AsyncResult.\n * The error type is automatically inferred from the `deps` parameter.\n *\n * @example\n * ```typescript\n * // Basic usage - automatic error inference\n * const fetchUser = async (id: string): AsyncResult<User, 'NOT_FOUND'> =>\n * id === '1' ? ok({ id, name: 'Alice' }) : err('NOT_FOUND');\n *\n * const fetchPosts = async (userId: string): AsyncResult<Post[], 'FETCH_ERROR'> =>\n * ok([{ id: 1, title: 'Hello' }]);\n *\n * const getPosts = createWorkflow({ fetchUser, fetchPosts });\n *\n * const result = await getPosts(async (step) => {\n * const user = await step(fetchUser('1'));\n * const posts = await step(fetchPosts(user.id));\n * return { user, posts };\n * });\n * // result.error: 'NOT_FOUND' | 'FETCH_ERROR' | UnexpectedError\n * ```\n *\n * @example\n * ```typescript\n * // With destructuring in callback (optional but convenient)\n * const result = await getPosts(async (step, { fetchUser, fetchPosts }) => {\n * const user = await step(fetchUser('1'));\n * const posts = await step(fetchPosts(user.id));\n * return { user, posts };\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Strict mode - closed error union (no UnexpectedError)\n * const getPosts = createWorkflow(\n * { fetchUser, fetchPosts },\n * {\n * strict: true,\n * catchUnexpected: () => 'UNEXPECTED' as const\n * }\n * );\n * // result.error: 'NOT_FOUND' | 'FETCH_ERROR' | 'UNEXPECTED' (exactly)\n * ```\n *\n * @example\n * ```typescript\n * // With step caching\n * const cache = new Map<string, Result<unknown, unknown>>();\n * const workflow = createWorkflow({ fetchUser }, { cache });\n *\n * const result = await workflow(async (step) => {\n * // First call executes fetchUser\n * const user1 = await step(() => fetchUser('1'), { key: 'user:1' });\n * // Second call with same key uses cache (fetchUser not called again)\n * const user2 = await step(() => fetchUser('1'), { key: 'user:1' });\n * return user1; // user1 === user2\n * });\n * ```\n *\n * @example\n * ```typescript\n * // With event stream for observability\n * const workflow = createWorkflow({ fetchUser }, {\n * onEvent: (event) => {\n * if (event.type === 'step_start') {\n * console.log(`Step ${event.name} started`);\n * }\n * if (event.type === 'step_success') {\n * console.log(`Step ${event.name} completed in ${event.durationMs}ms`);\n * }\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // With resume state for workflow replay\n * const savedState = { steps: new Map([['user:1', { result: ok({ id: '1', name: 'Alice' }) }]]) };\n * const workflow = createWorkflow({ fetchUser }, { resumeState: savedState });\n *\n * const result = await workflow(async (step) => {\n * // This step uses cached result from savedState (fetchUser not called)\n * const user = await step(() => fetchUser('1'), { key: 'user:1' });\n * return user;\n * });\n * ```\n *\n * @example\n * ```typescript\n * // With typed arguments (new API)\n * const workflow = createWorkflow({ fetchUser, fetchPosts });\n *\n * const result = await workflow(\n * { userId: '1' }, // Typed arguments\n * async (step, { fetchUser, fetchPosts }, { userId }) => {\n * const user = await step(fetchUser(userId));\n * const posts = await step(fetchPosts(user.id));\n * return { user, posts };\n * }\n * );\n * ```\n */\nexport function createWorkflow<\n const Deps extends Readonly<Record<string, AnyResultFn>>,\n C = void\n>(\n deps: Deps,\n options?: WorkflowOptions<ErrorsOfDeps<Deps>, C>\n): Workflow<ErrorsOfDeps<Deps>, Deps>;\n\nexport function createWorkflow<\n const Deps extends Readonly<Record<string, AnyResultFn>>,\n U,\n C = void\n>(\n deps: Deps,\n options: WorkflowOptionsStrict<ErrorsOfDeps<Deps>, U, C>\n): WorkflowStrict<ErrorsOfDeps<Deps>, U, Deps>;\n\n// Implementation\nexport function createWorkflow<\n const Deps extends Readonly<Record<string, AnyResultFn>>,\n U = never,\n C = void\n>(\n deps: Deps,\n options?: WorkflowOptions<ErrorsOfDeps<Deps>, C> | WorkflowOptionsStrict<ErrorsOfDeps<Deps>, U, C>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): any {\n type E = ErrorsOfDeps<Deps>;\n\n // Overloaded workflow executor function\n // Signature 1: No args (original API)\n function workflowExecutor<T>(\n fn: (step: RunStep<E>, deps: Deps) => T | Promise<T>\n ): Promise<Result<T, E | U | UnexpectedError, unknown>>;\n // Signature 2: With args (new API)\n function workflowExecutor<T, Args>(\n args: Args,\n fn: (step: RunStep<E>, deps: Deps, args: Args) => T | Promise<T>\n ): Promise<Result<T, E | U | UnexpectedError, unknown>>;\n // Implementation\n async function workflowExecutor<T, Args = undefined>(\n fnOrArgs: ((step: RunStep<E>, deps: Deps) => T | Promise<T>) | Args,\n maybeFn?: (step: RunStep<E>, deps: Deps, args: Args) => T | Promise<T>\n ): Promise<Result<T, E | U | UnexpectedError, unknown>> {\n // Detect calling pattern: if second arg is a function, first arg is args\n // This correctly handles functions as args (e.g., workflow(requestFactory, callback))\n const hasArgs = typeof maybeFn === \"function\";\n const args = hasArgs ? (fnOrArgs as Args) : undefined;\n const userFn = hasArgs\n ? maybeFn\n : (fnOrArgs as (step: RunStep<E>, deps: Deps) => T | Promise<T>);\n // Generate workflowId for this run\n const workflowId = crypto.randomUUID();\n\n // Create context for this run\n const context = options?.createContext?.() as C;\n\n // Helper to emit workflow events\n const emitEvent = (event: WorkflowEvent<E | U | UnexpectedError>) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (options as any)?.onEvent?.(event, context);\n };\n\n // Emit workflow_start\n const startTs = Date.now();\n const startTime = performance.now();\n emitEvent({\n type: \"workflow_start\",\n workflowId,\n ts: startTs,\n });\n\n // Get cache from options (or create one if resumeState is provided)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const resumeStateOption = (options as any)?.resumeState as\n | ResumeState\n | (() => ResumeState | Promise<ResumeState>)\n | undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let cache = (options as any)?.cache as StepCache | undefined;\n\n // If resumeState is provided but cache isn't, auto-create an in-memory cache\n if (resumeStateOption && !cache) {\n cache = new Map<string, Result<unknown, unknown, unknown>>();\n }\n\n // Pre-populate cache from resumeState\n if (resumeStateOption && cache) {\n const resumeState =\n typeof resumeStateOption === \"function\"\n ? await resumeStateOption()\n : resumeStateOption;\n\n for (const [key, entry] of resumeState.steps) {\n const { result, meta } = entry;\n if (result.ok) {\n cache.set(key, result);\n } else {\n // Encode error results with metadata for proper replay\n // Use provided meta if available, otherwise default to origin:\"result\"\n const effectiveMeta = meta ?? { origin: \"result\" as const, resultCause: result.cause };\n // Preserve original cause alongside metadata\n cache.set(key, encodeCachedError(result.error, effectiveMeta, result.cause));\n }\n }\n }\n\n // Helper to parse step options\n const parseStepOptions = (opts?: StepOptions | string): { name?: string; key?: string } => {\n if (typeof opts === \"string\") return { name: opts };\n return opts ?? {};\n };\n\n // Create a cached step wrapper\n const createCachedStep = (realStep: RunStep<E>): RunStep<E> => {\n if (!cache) {\n // No cache configured, just return real step\n return realStep;\n }\n\n // Wrap the main step function\n const cachedStepFn = async <StepT, StepE extends E, StepC = unknown>(\n operationOrResult:\n | (() => Result<StepT, StepE, StepC> | AsyncResult<StepT, StepE, StepC>)\n | Result<StepT, StepE, StepC>\n | AsyncResult<StepT, StepE, StepC>,\n stepOptions?: StepOptions | string\n ): Promise<StepT> => {\n const { name, key } = parseStepOptions(stepOptions);\n\n // Only use cache if key is provided\n if (key && cache.has(key)) {\n // Cache hit\n emitEvent({\n type: \"step_cache_hit\",\n workflowId,\n stepKey: key,\n name,\n ts: Date.now(),\n });\n\n const cached = cache.get(key)!;\n if (cached.ok) {\n return cached.value as StepT;\n }\n // Cached error - throw early exit with preserved metadata (origin + cause)\n // This bypasses realStep to avoid replaying step_start/step_error events\n const meta = decodeCachedMeta(cached.cause);\n throw createEarlyExit(cached.error as StepE, meta);\n }\n\n // Cache miss - emit event if key was provided\n if (key) {\n emitEvent({\n type: \"step_cache_miss\",\n workflowId,\n stepKey: key,\n name,\n ts: Date.now(),\n });\n }\n\n // Execute the real step - wrap in function form to satisfy overload\n const wrappedOp = typeof operationOrResult === \"function\"\n ? operationOrResult\n : () => operationOrResult;\n\n try {\n const value = await realStep(wrappedOp, stepOptions);\n // Cache successful result if key provided\n if (key) {\n cache.set(key, ok(value));\n }\n return value;\n } catch (thrown) {\n // Cache error results with full metadata if key provided and this is an early exit\n if (key && isEarlyExit(thrown)) {\n const exit = thrown as EarlyExit<StepE>;\n // Extract original cause from metadata for preservation\n const originalCause =\n exit.meta.origin === \"result\"\n ? exit.meta.resultCause\n : exit.meta.thrown;\n cache.set(key, encodeCachedError(exit.error, exit.meta, originalCause));\n }\n throw thrown;\n }\n };\n\n // Wrap step.try\n cachedStepFn.try = async <StepT, Err extends E>(\n operation: () => StepT | Promise<StepT>,\n opts:\n | { error: Err; name?: string; key?: string }\n | { onError: (cause: unknown) => Err; name?: string; key?: string }\n ): Promise<StepT> => {\n const { name, key } = opts;\n\n // Only use cache if key is provided\n if (key && cache.has(key)) {\n // Cache hit\n emitEvent({\n type: \"step_cache_hit\",\n workflowId,\n stepKey: key,\n name,\n ts: Date.now(),\n });\n\n const cached = cache.get(key)!;\n if (cached.ok) {\n return cached.value as StepT;\n }\n // Cached error - throw early exit with preserved metadata (origin + thrown)\n // This bypasses realStep.try to avoid replaying instrumentation\n const meta = decodeCachedMeta(cached.cause);\n throw createEarlyExit(cached.error as Err, meta);\n }\n\n // Cache miss - emit event if key was provided\n if (key) {\n emitEvent({\n type: \"step_cache_miss\",\n workflowId,\n stepKey: key,\n name,\n ts: Date.now(),\n });\n }\n\n // Execute the real step.try\n try {\n const value = await realStep.try(operation, opts);\n // Cache successful result if key provided\n if (key) {\n cache.set(key, ok(value));\n }\n return value;\n } catch (thrown) {\n // Cache error results with full metadata if key provided and this is an early exit\n if (key && isEarlyExit(thrown)) {\n const exit = thrown as EarlyExit<Err>;\n // Extract original cause from metadata for preservation\n const originalCause =\n exit.meta.origin === \"result\"\n ? exit.meta.resultCause\n : exit.meta.thrown;\n cache.set(key, encodeCachedError(exit.error, exit.meta, originalCause));\n }\n throw thrown;\n }\n };\n\n // Wrap step.fromResult - delegate to real step (caching handled by key in opts)\n cachedStepFn.fromResult = async <StepT, ResultE, Err extends E>(\n operation: () => Result<StepT, ResultE, unknown> | AsyncResult<StepT, ResultE, unknown>,\n opts:\n | { error: Err; name?: string; key?: string }\n | { onError: (resultError: ResultE) => Err; name?: string; key?: string }\n ): Promise<StepT> => {\n const { name, key } = opts;\n\n // Only use cache if key is provided\n if (key && cache.has(key)) {\n // Cache hit\n emitEvent({\n type: \"step_cache_hit\",\n workflowId,\n stepKey: key,\n name,\n ts: Date.now(),\n });\n\n const cached = cache.get(key)!;\n if (cached.ok) {\n return cached.value as StepT;\n }\n // Cached error - throw early exit with preserved metadata\n const meta = decodeCachedMeta(cached.cause);\n throw createEarlyExit(cached.error as Err, meta);\n }\n\n // Cache miss - emit event if key was provided\n if (key) {\n emitEvent({\n type: \"step_cache_miss\",\n workflowId,\n stepKey: key,\n name,\n ts: Date.now(),\n });\n }\n\n // Execute the real step.fromResult\n try {\n const value = await realStep.fromResult(operation, opts);\n // Cache successful result if key provided\n if (key) {\n cache.set(key, ok(value));\n }\n return value;\n } catch (thrown) {\n // Cache error results with full metadata if key provided and this is an early exit\n if (key && isEarlyExit(thrown)) {\n const exit = thrown as EarlyExit<Err>;\n const originalCause =\n exit.meta.origin === \"result\"\n ? exit.meta.resultCause\n : exit.meta.thrown;\n cache.set(key, encodeCachedError(exit.error, exit.meta, originalCause));\n }\n throw thrown;\n }\n };\n\n // Wrap step.parallel - delegate to real step (no caching for scope wrappers)\n cachedStepFn.parallel = realStep.parallel;\n\n // Wrap step.race - delegate to real step (no caching for scope wrappers)\n cachedStepFn.race = realStep.race;\n\n // Wrap step.allSettled - delegate to real step (no caching for scope wrappers)\n cachedStepFn.allSettled = realStep.allSettled;\n\n // Wrap step.retry - use cachedStepFn to ensure caching/resume works with keyed steps\n cachedStepFn.retry = <StepT, StepE extends E, StepC = unknown>(\n operation: () => Result<StepT, StepE, StepC> | AsyncResult<StepT, StepE, StepC>,\n options: RetryOptions & { name?: string; key?: string; timeout?: TimeoutOptions }\n ): Promise<StepT> => {\n // Delegate to cachedStepFn with retry options merged into StepOptions\n // This ensures the cache layer is consulted for keyed steps\n return cachedStepFn(operation, {\n name: options.name,\n key: options.key,\n retry: {\n attempts: options.attempts,\n backoff: options.backoff,\n initialDelay: options.initialDelay,\n maxDelay: options.maxDelay,\n jitter: options.jitter,\n retryOn: options.retryOn,\n onRetry: options.onRetry,\n },\n timeout: options.timeout,\n });\n };\n\n // Wrap step.withTimeout - use cachedStepFn to ensure caching/resume works with keyed steps\n cachedStepFn.withTimeout = <StepT, StepE extends E, StepC = unknown>(\n operation:\n | (() => Result<StepT, StepE, StepC> | AsyncResult<StepT, StepE, StepC>)\n | ((signal: AbortSignal) => Result<StepT, StepE, StepC> | AsyncResult<StepT, StepE, StepC>),\n options: TimeoutOptions & { name?: string; key?: string }\n ): Promise<StepT> => {\n // Delegate to cachedStepFn with timeout options\n // This ensures the cache layer is consulted for keyed steps\n return cachedStepFn(\n operation as () => Result<StepT, StepE, StepC> | AsyncResult<StepT, StepE, StepC>,\n {\n name: options.name,\n key: options.key,\n timeout: options,\n }\n );\n };\n\n return cachedStepFn as RunStep<E>;\n };\n\n // Wrap the user's callback to pass cached step, deps, and args (when present)\n const wrappedFn = hasArgs\n ? (step: RunStep<E>) => (userFn as (step: RunStep<E>, deps: Deps, args: Args) => T | Promise<T>)(createCachedStep(step), deps, args as Args)\n : (step: RunStep<E>) => (userFn as (step: RunStep<E>, deps: Deps) => T | Promise<T>)(createCachedStep(step), deps);\n\n let result: Result<T, E | U | UnexpectedError, unknown>;\n\n if (options?.strict === true) {\n // Strict mode - use run.strict for closed error union\n const strictOptions = options as WorkflowOptionsStrict<E, U, C>;\n result = await run.strict<T, E | U, C>(wrappedFn as (step: RunStep<E | U>) => Promise<T> | T, {\n onError: strictOptions.onError,\n onEvent: strictOptions.onEvent as ((event: WorkflowEvent<E | U | UnexpectedError>, ctx: C) => void) | undefined,\n catchUnexpected: strictOptions.catchUnexpected,\n workflowId,\n context,\n });\n } else {\n // Non-strict mode - use run with onError for typed errors + UnexpectedError\n const normalOptions = options as WorkflowOptions<E, C> | undefined;\n result = await run<T, E, C>(wrappedFn as (step: RunStep<E | UnexpectedError>) => Promise<T> | T, {\n onError: normalOptions?.onError ?? (() => {}),\n onEvent: normalOptions?.onEvent,\n workflowId,\n context,\n });\n }\n\n // Emit workflow_success or workflow_error\n const durationMs = performance.now() - startTime;\n if (result.ok) {\n emitEvent({\n type: \"workflow_success\",\n workflowId,\n ts: Date.now(),\n durationMs,\n });\n } else {\n emitEvent({\n type: \"workflow_error\",\n workflowId,\n ts: Date.now(),\n durationMs,\n error: result.error,\n });\n }\n\n return result;\n }\n\n return workflowExecutor;\n}\n\n// =============================================================================\n// Type Guard Helpers\n// =============================================================================\n\n/**\n * Type guard to check if an event is a step_complete event.\n * Use this to filter events for state persistence.\n *\n * @param event - The workflow event to check\n * @returns `true` if the event is a step_complete event, `false` otherwise\n *\n * @example\n * ```typescript\n * const savedSteps = new Map<string, Result<unknown, unknown>>();\n *\n * const workflow = createWorkflow({ fetchUser }, {\n * onEvent: (event) => {\n * if (isStepComplete(event)) {\n * savedSteps.set(event.stepKey, event.result);\n * }\n * }\n * });\n * ```\n */\nexport function isStepComplete(\n event: WorkflowEvent<unknown>\n): event is Extract<WorkflowEvent<unknown>, { type: \"step_complete\" }> {\n return event.type === \"step_complete\";\n}\n\n// =============================================================================\n// Human-in-the-Loop (HITL) Support\n// =============================================================================\n\n/**\n * Standard error type for steps awaiting human approval.\n * Use this as the error type for approval-gated steps.\n *\n * @example\n * const requireApproval = async (userId: string): AsyncResult<Approval, PendingApproval> => {\n * const status = await checkApprovalStatus(userId);\n * if (status === 'pending') {\n * return err({ type: 'PENDING_APPROVAL', stepKey: `approval:${userId}` });\n * }\n * return ok(status.approval);\n * };\n */\nexport type PendingApproval = {\n type: \"PENDING_APPROVAL\";\n /** Step key for correlation when resuming */\n stepKey: string;\n /** Optional reason for the pending state */\n reason?: string;\n /** Optional metadata for the approval request */\n metadata?: Record<string, unknown>;\n};\n\n/**\n * Error returned when approval is rejected.\n */\nexport type ApprovalRejected = {\n type: \"APPROVAL_REJECTED\";\n /** Step key for correlation */\n stepKey: string;\n /** Reason the approval was rejected */\n reason: string;\n};\n\n/**\n * Type guard to check if an error is a PendingApproval.\n *\n * @param error - The error to check\n * @returns `true` if the error is a PendingApproval, `false` otherwise\n *\n * @example\n * ```typescript\n * const result = await workflow(...);\n * if (!result.ok && isPendingApproval(result.error)) {\n * console.log(`Waiting for approval: ${result.error.stepKey}`);\n * }\n * ```\n */\nexport function isPendingApproval(error: unknown): error is PendingApproval {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as PendingApproval).type === \"PENDING_APPROVAL\"\n );\n}\n\n/**\n * Type guard to check if an error is an ApprovalRejected.\n *\n * @param error - The error to check\n * @returns `true` if the error is an ApprovalRejected, `false` otherwise\n */\nexport function isApprovalRejected(error: unknown): error is ApprovalRejected {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as ApprovalRejected).type === \"APPROVAL_REJECTED\"\n );\n}\n\n/**\n * Create a PendingApproval error result.\n * Convenience helper for approval-gated steps.\n *\n * @param stepKey - Stable key for this approval step (used for resume)\n * @param options - Optional reason and metadata for the pending approval\n * @returns A Result with a PendingApproval error\n *\n * @example\n * ```typescript\n * const requireApproval = async (userId: string) => {\n * const status = await db.getApproval(userId);\n * if (!status) return pendingApproval(`approval:${userId}`);\n * return ok(status);\n * };\n * ```\n */\nexport function pendingApproval(\n stepKey: string,\n options?: { reason?: string; metadata?: Record<string, unknown> }\n): Result<never, PendingApproval> {\n return err({\n type: \"PENDING_APPROVAL\",\n stepKey,\n reason: options?.reason,\n metadata: options?.metadata,\n });\n}\n\n/**\n * Options for creating an approval-gated step.\n */\nexport interface ApprovalStepOptions<T> {\n /** Stable key for this approval step (used for resume) */\n key: string;\n /** Function to check current approval status from external source */\n checkApproval: () => Promise<\n | { status: \"pending\" }\n | { status: \"approved\"; value: T }\n | { status: \"rejected\"; reason: string }\n >;\n /** Optional reason shown when pending */\n pendingReason?: string;\n /** Optional metadata for the approval request */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Create a Result-returning function that checks external approval status.\n *\n * ## When to Use\n *\n * Use `createApprovalStep` when you need:\n * - **Human-in-the-loop workflows**: Steps that require human approval\n * - **External approval systems**: Integrate with approval databases/APIs\n * - **Workflow pausing**: Workflows that pause and resume after approval\n * - **Approval tracking**: Track who approved what and when\n *\n * ## Why Use This Instead of Manual Approval Checks\n *\n * - **Standardized pattern**: Consistent approval step interface\n * - **Type-safe**: Returns typed `PendingApproval` or `ApprovalRejected` errors\n * - **Resume-friendly**: Works seamlessly with `injectApproval()` and resume state\n * - **Metadata support**: Can include approval reason and metadata\n *\n * ## How It Works\n *\n * 1. Create approval step with `checkApproval` function\n * 2. `checkApproval` returns one of:\n * - `{ status: 'pending' }` - Approval not yet granted (workflow pauses)\n * - `{ status: 'approved', value: T }` - Approval granted (workflow continues)\n * - `{ status: 'rejected', reason: string }` - Approval rejected (workflow fails)\n * 3. Use in workflow with `step()` - workflow pauses if pending\n * 4. When approval granted externally, use `injectApproval()` to resume\n *\n * ## Typical Approval Flow\n *\n * 1. Workflow executes → reaches approval step\n * 2. `checkApproval()` called → returns `{ status: 'pending' }`\n * 3. Workflow returns `PendingApproval` error\n * 4. Save workflow state → persist for later resume\n * 5. Show approval UI → user sees pending approval\n * 6. User grants/rejects → update approval system\n * 7. Inject approval → call `injectApproval()` with approved value\n * 8. Resume workflow → continue from approval step\n *\n * @param options - Configuration for the approval step:\n * - `key`: Stable key for this approval (must match step key in workflow)\n * - `checkApproval`: Async function that checks current approval status\n * - `pendingReason`: Optional reason shown when approval is pending\n * - `metadata`: Optional metadata attached to the approval request\n *\n * @returns A function that returns an AsyncResult checking approval status.\n * The function can be used directly with `step()` in workflows.\n *\n * @example\n * ```typescript\n * // Create approval step that checks database\n * const requireManagerApproval = createApprovalStep<{ approvedBy: string }>({\n * key: 'manager-approval',\n * checkApproval: async () => {\n * const approval = await db.getApproval('manager-approval');\n * if (!approval) {\n * return { status: 'pending' }; // Workflow pauses here\n * }\n * if (approval.rejected) {\n * return { status: 'rejected', reason: approval.reason };\n * }\n * return {\n * status: 'approved',\n * value: { approvedBy: approval.approvedBy }\n * };\n * },\n * pendingReason: 'Waiting for manager approval',\n * });\n *\n * // Use in workflow\n * const workflow = createWorkflow({ requireManagerApproval });\n * const result = await workflow(async (step) => {\n * const approval = await step(requireManagerApproval, { key: 'manager-approval' });\n * // If pending, workflow exits with PendingApproval error\n * // If approved, continues with approval value\n * return approval;\n * });\n *\n * // Handle pending state\n * if (!result.ok && isPendingApproval(result.error)) {\n * // Workflow paused - show approval UI\n * showApprovalUI(result.error.stepKey);\n * }\n * ```\n *\n * @example\n * ```typescript\n * // With approval injection for resume\n * const collector = createHITLCollector();\n * const workflow = createWorkflow({ requireApproval }, {\n * onEvent: collector.handleEvent,\n * });\n *\n * const result = await workflow(async (step) => {\n * const approval = await step(requireApproval, { key: 'approval:1' });\n * return approval;\n * });\n *\n * // When approval granted externally\n * if (collector.hasPendingApprovals()) {\n * const resumeState = collector.injectApproval('approval:1', {\n * approvedBy: 'admin@example.com'\n * });\n *\n * // Resume workflow\n * const workflow2 = createWorkflow({ requireApproval }, { resumeState });\n * const result2 = await workflow2(async (step) => {\n * const approval = await step(requireApproval, { key: 'approval:1' });\n * return approval; // Now succeeds with injected value\n * });\n * }\n * ```\n */\nexport function createApprovalStep<T>(\n options: ApprovalStepOptions<T>\n): () => AsyncResult<T, PendingApproval | ApprovalRejected> {\n return async (): AsyncResult<T, PendingApproval | ApprovalRejected> => {\n const result = await options.checkApproval();\n\n switch (result.status) {\n case \"pending\":\n return err({\n type: \"PENDING_APPROVAL\",\n stepKey: options.key,\n reason: options.pendingReason,\n metadata: options.metadata,\n });\n case \"rejected\":\n return err({\n type: \"APPROVAL_REJECTED\",\n stepKey: options.key,\n reason: result.reason,\n });\n case \"approved\":\n return ok(result.value);\n }\n };\n}\n\n// =============================================================================\n// Resume State Helpers for HITL\n// =============================================================================\n\n/**\n * Inject an approved value into resume state.\n * Use this when an external approval is granted and you want to resume the workflow.\n *\n * @param state - The resume state to update\n * @param options - Object with stepKey and the approved value\n * @returns A new ResumeState with the approval injected\n *\n * @example\n * ```typescript\n * // When approval is granted externally:\n * const updatedState = injectApproval(savedState, {\n * stepKey: 'deploy:prod',\n * value: { approvedBy: 'admin', approvedAt: Date.now() }\n * });\n *\n * // Resume workflow with the approval injected\n * const workflow = createWorkflow({ ... }, { resumeState: updatedState });\n * ```\n */\nexport function injectApproval<T>(\n state: ResumeState,\n options: { stepKey: string; value: T }\n): ResumeState {\n const newSteps = new Map(state.steps);\n newSteps.set(options.stepKey, {\n result: ok(options.value),\n });\n return { steps: newSteps };\n}\n\n/**\n * Remove a step from resume state (e.g., to force re-execution).\n *\n * @param state - The resume state to update\n * @param stepKey - The key of the step to remove\n * @returns A new ResumeState with the step removed\n *\n * @example\n * ```typescript\n * // Force a step to re-execute on resume\n * const updatedState = clearStep(savedState, 'approval:123');\n * ```\n */\nexport function clearStep(state: ResumeState, stepKey: string): ResumeState {\n const newSteps = new Map(state.steps);\n newSteps.delete(stepKey);\n return { steps: newSteps };\n}\n\n/**\n * Check if a step in resume state has a pending approval error.\n *\n * @param state - The resume state to check\n * @param stepKey - The key of the step to check\n * @returns `true` if the step has a pending approval, `false` otherwise\n *\n * @example\n * ```typescript\n * if (hasPendingApproval(savedState, 'deploy:prod')) {\n * // Show approval UI\n * }\n * ```\n */\nexport function hasPendingApproval(\n state: ResumeState,\n stepKey: string\n): boolean {\n const entry = state.steps.get(stepKey);\n if (!entry || entry.result.ok) return false;\n return isPendingApproval(entry.result.error);\n}\n\n/**\n * Get all pending approval step keys from resume state.\n *\n * @param state - The resume state to check\n * @returns Array of step keys that have pending approvals\n *\n * @example\n * ```typescript\n * const pendingKeys = getPendingApprovals(savedState);\n * // ['deploy:prod', 'deploy:staging']\n * ```\n */\nexport function getPendingApprovals(state: ResumeState): string[] {\n const pending: string[] = [];\n for (const [key, entry] of state.steps) {\n if (!entry.result.ok && isPendingApproval(entry.result.error)) {\n pending.push(key);\n }\n }\n return pending;\n}\n\n// =============================================================================\n// Enhanced Collector for HITL\n// =============================================================================\n\n/**\n * Extended step collector that tracks pending approvals.\n * Use this for HITL workflows that need to track approval state.\n *\n * @returns An object with methods to handle events, get state, and manage approvals\n *\n * @example\n * ```typescript\n * const collector = createHITLCollector();\n *\n * const workflow = createWorkflow({ fetchUser, requireApproval }, {\n * onEvent: collector.handleEvent,\n * });\n *\n * const result = await workflow(async (step) => {\n * const user = await step(() => fetchUser(\"1\"), { key: \"user:1\" });\n * const approval = await step(requireApproval, { key: \"approval:1\" });\n * return { user, approval };\n * });\n *\n * // Check for pending approvals\n * if (collector.hasPendingApprovals()) {\n * const pending = collector.getPendingApprovals();\n * // pending: [{ stepKey: 'approval:1', error: PendingApproval }]\n * await saveToDatabase(collector.getState());\n * }\n *\n * // Later, when approved:\n * const resumeState = collector.injectApproval('approval:1', { approvedBy: 'admin' });\n * ```\n */\nexport function createHITLCollector(): {\n /** Handle workflow events (pass to onEvent option) */\n handleEvent: (event: WorkflowEvent<unknown>) => void;\n /** Get collected resume state */\n getState: () => ResumeState;\n /** Clear all collected state */\n clear: () => void;\n /** Check if any steps have pending approvals */\n hasPendingApprovals: () => boolean;\n /** Get all pending approval entries with their errors */\n getPendingApprovals: () => Array<{ stepKey: string; error: PendingApproval }>;\n /** Inject an approval result, updating the collector's internal state. Returns a copy for use as resumeState. */\n injectApproval: <T>(stepKey: string, value: T) => ResumeState;\n} {\n const steps = new Map<string, ResumeStateEntry>();\n\n return {\n handleEvent: (event: WorkflowEvent<unknown>) => {\n if (isStepComplete(event)) {\n steps.set(event.stepKey, { result: event.result, meta: event.meta });\n }\n },\n getState: () => ({ steps: new Map(steps) }),\n clear: () => steps.clear(),\n hasPendingApprovals: () => {\n for (const entry of steps.values()) {\n if (!entry.result.ok && isPendingApproval(entry.result.error)) {\n return true;\n }\n }\n return false;\n },\n getPendingApprovals: () => {\n const pending: Array<{ stepKey: string; error: PendingApproval }> = [];\n for (const [key, entry] of steps) {\n if (!entry.result.ok && isPendingApproval(entry.result.error)) {\n pending.push({ stepKey: key, error: entry.result.error as PendingApproval });\n }\n }\n return pending;\n },\n injectApproval: <T>(stepKey: string, value: T): ResumeState => {\n // Mutate internal state so collector reflects the approval\n steps.set(stepKey, { result: ok(value) });\n // Return a copy for use as resumeState\n return { steps: new Map(steps) };\n },\n };\n}\n","/**\n * @jagreehal/workflow/conditional\n *\n * Conditional step execution helpers for workflows.\n * These helpers allow you to conditionally execute steps based on runtime conditions,\n * with proper event emission for skipped steps.\n */\n\nimport type { WorkflowEvent } from \"./core\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Options for conditional execution.\n */\nexport type ConditionalOptions = {\n /**\n * Human-readable name for the conditional step.\n * Used in step_skipped events for debugging and visualization.\n */\n name?: string;\n\n /**\n * Stable identity key for the conditional step.\n * Used in step_skipped events for tracking and visualization.\n */\n key?: string;\n\n /**\n * Optional reason explaining why the step was skipped.\n * Included in step_skipped events.\n */\n reason?: string;\n};\n\n/**\n * Context for conditional execution, used to emit events.\n */\nexport type ConditionalContext = {\n /**\n * The workflow ID for event emission.\n */\n workflowId: string;\n\n /**\n * Event emitter function.\n */\n onEvent?: (event: WorkflowEvent<unknown>) => void;\n};\n\n/**\n * Type for operations that can be either sync or async.\n */\ntype MaybeAsync<T> = T | Promise<T>;\n\n/**\n * Type for the operation function passed to conditional helpers.\n */\ntype Operation<T> = () => MaybeAsync<T>;\n\n// =============================================================================\n// Internal Helpers\n// =============================================================================\n\n/**\n * Generate a unique decision ID for tracking conditional decisions.\n * @internal\n */\nfunction generateDecisionId(): string {\n return `decision_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * Emit a step_skipped event.\n * @internal\n */\nfunction emitSkipped(\n ctx: ConditionalContext | undefined,\n options: ConditionalOptions | undefined,\n decisionId: string\n): void {\n if (!ctx?.onEvent) return;\n\n ctx.onEvent({\n type: \"step_skipped\",\n workflowId: ctx.workflowId,\n stepKey: options?.key,\n name: options?.name,\n reason: options?.reason,\n decisionId,\n ts: Date.now(),\n });\n}\n\n// =============================================================================\n// Conditional Helpers\n// =============================================================================\n\n/**\n * Run a step only if condition is true, return undefined if skipped.\n *\n * Use this when you want to conditionally execute a step and handle\n * the undefined case yourself. For a version with a default value,\n * use `whenOr`.\n *\n * @param condition - Boolean condition to evaluate\n * @param operation - Function that performs the step (only called if condition is true)\n * @param options - Optional configuration for the conditional step\n * @param ctx - Optional context for event emission\n * @returns The result of the operation if condition is true, undefined otherwise\n *\n * @example\n * ```typescript\n * const result = await workflow(async (step) => {\n * const user = await step(fetchUser(id));\n *\n * // Only runs if user is premium\n * const premium = await when(\n * user.isPremium,\n * () => step(() => fetchPremiumData(user.id), { name: 'premium-data' }),\n * { name: 'check-premium', reason: 'User is not premium' }\n * );\n *\n * return { user, premium };\n * });\n * ```\n */\nexport function when<T>(\n condition: boolean,\n operation: Operation<T>,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): Promise<T | undefined>;\n\n/**\n * Synchronous overload for when the operation returns a non-Promise value.\n */\nexport function when<T>(\n condition: boolean,\n operation: () => T,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): T | undefined | Promise<T | undefined>;\n\nexport function when<T>(\n condition: boolean,\n operation: Operation<T>,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): MaybeAsync<T | undefined> {\n if (condition) {\n return operation();\n }\n\n const decisionId = generateDecisionId();\n emitSkipped(ctx, options, decisionId);\n return undefined;\n}\n\n/**\n * Run a step only if condition is false, return undefined if skipped.\n *\n * Use this when you want to conditionally execute a step when a condition\n * is NOT met. For a version with a default value, use `unlessOr`.\n *\n * @param condition - Boolean condition to evaluate\n * @param operation - Function that performs the step (only called if condition is false)\n * @param options - Optional configuration for the conditional step\n * @param ctx - Optional context for event emission\n * @returns The result of the operation if condition is false, undefined otherwise\n *\n * @example\n * ```typescript\n * const result = await workflow(async (step) => {\n * const user = await step(fetchUser(id));\n *\n * // Only runs if user is NOT verified\n * const verification = await unless(\n * user.isVerified,\n * () => step(() => sendVerificationEmail(user.email), { name: 'send-verification' }),\n * { name: 'check-verification', reason: 'User is already verified' }\n * );\n *\n * return { user, verification };\n * });\n * ```\n */\nexport function unless<T>(\n condition: boolean,\n operation: Operation<T>,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): Promise<T | undefined>;\n\n/**\n * Synchronous overload for unless when the operation returns a non-Promise value.\n */\nexport function unless<T>(\n condition: boolean,\n operation: () => T,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): T | undefined | Promise<T | undefined>;\n\nexport function unless<T>(\n condition: boolean,\n operation: Operation<T>,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): MaybeAsync<T | undefined> {\n return when(!condition, operation, options, ctx);\n}\n\n/**\n * Run a step only if condition is true, return default value if skipped.\n *\n * Use this when you want to conditionally execute a step and provide\n * a fallback value when the condition is not met.\n *\n * @param condition - Boolean condition to evaluate\n * @param operation - Function that performs the step (only called if condition is true)\n * @param defaultValue - Value to return if condition is false\n * @param options - Optional configuration for the conditional step\n * @param ctx - Optional context for event emission\n * @returns The result of the operation if condition is true, defaultValue otherwise\n *\n * @example\n * ```typescript\n * const result = await workflow(async (step) => {\n * const user = await step(fetchUser(id));\n *\n * // Get premium limits or use default for non-premium users\n * const limits = await whenOr(\n * user.isPremium,\n * () => step(() => fetchPremiumLimits(user.id), { name: 'premium-limits' }),\n * { maxRequests: 100, maxStorage: 1000 }, // default for non-premium\n * { name: 'check-premium-limits', reason: 'Using default limits for non-premium user' }\n * );\n *\n * return { user, limits };\n * });\n * ```\n */\nexport function whenOr<T, D>(\n condition: boolean,\n operation: Operation<T>,\n defaultValue: D,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): Promise<T | D>;\n\n/**\n * Synchronous overload for whenOr when the operation returns a non-Promise value.\n */\nexport function whenOr<T, D>(\n condition: boolean,\n operation: () => T,\n defaultValue: D,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): T | D | Promise<T | D>;\n\nexport function whenOr<T, D>(\n condition: boolean,\n operation: Operation<T>,\n defaultValue: D,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): MaybeAsync<T | D> {\n if (condition) {\n return operation();\n }\n\n const decisionId = generateDecisionId();\n emitSkipped(ctx, options, decisionId);\n return defaultValue;\n}\n\n/**\n * Run a step only if condition is false, return default value if skipped.\n *\n * Use this when you want to conditionally execute a step when a condition\n * is NOT met, with a fallback value for when the condition is true.\n *\n * @param condition - Boolean condition to evaluate\n * @param operation - Function that performs the step (only called if condition is false)\n * @param defaultValue - Value to return if condition is true\n * @param options - Optional configuration for the conditional step\n * @param ctx - Optional context for event emission\n * @returns The result of the operation if condition is false, defaultValue otherwise\n *\n * @example\n * ```typescript\n * const result = await workflow(async (step) => {\n * const user = await step(fetchUser(id));\n *\n * // Generate new token if user is NOT authenticated, otherwise use existing\n * const token = await unlessOr(\n * user.isAuthenticated,\n * () => step(() => generateNewToken(user.id), { name: 'generate-token' }),\n * user.existingToken, // use existing token if authenticated\n * { name: 'check-auth-for-token', reason: 'Using existing token for authenticated user' }\n * );\n *\n * return { user, token };\n * });\n * ```\n */\nexport function unlessOr<T, D>(\n condition: boolean,\n operation: Operation<T>,\n defaultValue: D,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): Promise<T | D>;\n\n/**\n * Synchronous overload for unlessOr when the operation returns a non-Promise value.\n */\nexport function unlessOr<T, D>(\n condition: boolean,\n operation: () => T,\n defaultValue: D,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): T | D | Promise<T | D>;\n\nexport function unlessOr<T, D>(\n condition: boolean,\n operation: Operation<T>,\n defaultValue: D,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): MaybeAsync<T | D> {\n return whenOr(!condition, operation, defaultValue, options, ctx);\n}\n\n// =============================================================================\n// Factory Functions for Workflow Integration\n// =============================================================================\n\n/**\n * Create a set of conditional helpers bound to a workflow context.\n *\n * Use this factory when you want to automatically emit step_skipped events\n * to the workflow's event stream without passing context manually.\n *\n * @param ctx - The workflow context containing workflowId and onEvent\n * @returns Object with bound when, unless, whenOr, and unlessOr functions\n *\n * @example\n * ```typescript\n * const result = await run(async (step) => {\n * const ctx = { workflowId, onEvent };\n * const { when, whenOr } = createConditionalHelpers(ctx);\n *\n * const user = await step(fetchUser(id));\n *\n * const premium = await when(\n * user.isPremium,\n * () => step(() => fetchPremiumData(user.id)),\n * { name: 'premium-data' }\n * );\n *\n * return { user, premium };\n * }, { onEvent, workflowId });\n * ```\n */\nexport function createConditionalHelpers(ctx: ConditionalContext) {\n return {\n /**\n * Run a step only if condition is true, return undefined if skipped.\n */\n when: <T>(\n condition: boolean,\n operation: Operation<T>,\n options?: ConditionalOptions\n ): MaybeAsync<T | undefined> => when(condition, operation, options, ctx),\n\n /**\n * Run a step only if condition is false, return undefined if skipped.\n */\n unless: <T>(\n condition: boolean,\n operation: Operation<T>,\n options?: ConditionalOptions\n ): MaybeAsync<T | undefined> => unless(condition, operation, options, ctx),\n\n /**\n * Run a step only if condition is true, return default value if skipped.\n */\n whenOr: <T, D>(\n condition: boolean,\n operation: Operation<T>,\n defaultValue: D,\n options?: ConditionalOptions\n ): MaybeAsync<T | D> => whenOr(condition, operation, defaultValue, options, ctx),\n\n /**\n * Run a step only if condition is false, return default value if skipped.\n */\n unlessOr: <T, D>(\n condition: boolean,\n operation: Operation<T>,\n defaultValue: D,\n options?: ConditionalOptions\n ): MaybeAsync<T | D> => unlessOr(condition, operation, defaultValue, options, ctx),\n };\n}\n","/**\n * Circuit Breaker for Steps\n *\n * Prevents cascading failures by tracking step failure rates and\n * short-circuiting calls when a threshold is exceeded.\n *\n * Uses the circuit breaker pattern with three states:\n * - CLOSED: Normal operation (steps executing)\n * - OPEN: Fast-fail mode (steps blocked)\n * - HALF_OPEN: Testing if service recovered\n *\n * @example\n * ```typescript\n * import { createCircuitBreaker } from '@jagreehal/workflow';\n *\n * const breaker = createCircuitBreaker({\n * failureThreshold: 5,\n * resetTimeout: 30000,\n * halfOpenMax: 3,\n * });\n *\n * const result = await workflow(async (step) => {\n * const data = await breaker.execute(\n * () => step(() => callExternalApi()),\n * { name: 'external-api' }\n * );\n * return data;\n * });\n * ```\n */\n\nimport { err, type Result, type AsyncResult } from \"./core\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Circuit breaker state.\n */\nexport type CircuitState = \"CLOSED\" | \"OPEN\" | \"HALF_OPEN\";\n\n/**\n * Configuration for circuit breaker behavior.\n */\nexport interface CircuitBreakerConfig {\n /**\n * Number of failures within the window before opening the circuit.\n * @default 5\n */\n failureThreshold: number;\n\n /**\n * Time in ms to wait before transitioning from OPEN to HALF_OPEN.\n * @default 30000 (30 seconds)\n */\n resetTimeout: number;\n\n /**\n * Time window in ms for counting failures.\n * Failures older than this are discarded.\n * @default 60000 (1 minute)\n */\n windowSize: number;\n\n /**\n * Maximum number of test requests allowed in HALF_OPEN state.\n * If all succeed, circuit closes. If any fail, circuit reopens.\n * @default 3\n */\n halfOpenMax: number;\n\n /**\n * Optional callback when circuit state changes.\n */\n onStateChange?: (from: CircuitState, to: CircuitState, name?: string) => void;\n}\n\n/**\n * Error thrown when the circuit is open and calls are blocked.\n */\nexport class CircuitOpenError extends Error {\n readonly type = \"CIRCUIT_OPEN\" as const;\n readonly circuitName: string;\n readonly state: CircuitState;\n readonly retryAfterMs: number;\n\n constructor(options: {\n circuitName: string;\n state: CircuitState;\n retryAfterMs: number;\n message?: string;\n }) {\n super(\n options.message ??\n `Circuit breaker \"${options.circuitName}\" is ${options.state}. ` +\n `Retry after ${Math.ceil(options.retryAfterMs / 1000)}s`\n );\n this.name = \"CircuitOpenError\";\n this.circuitName = options.circuitName;\n this.state = options.state;\n this.retryAfterMs = options.retryAfterMs;\n }\n}\n\n/**\n * Type guard for CircuitOpenError.\n */\nexport function isCircuitOpenError(error: unknown): error is CircuitOpenError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as CircuitOpenError).type === \"CIRCUIT_OPEN\"\n );\n}\n\n/**\n * Failure record for tracking failures within the window.\n */\ninterface FailureRecord {\n timestamp: number;\n error: unknown;\n}\n\n/**\n * Circuit breaker statistics.\n */\nexport interface CircuitBreakerStats {\n state: CircuitState;\n failureCount: number;\n successCount: number;\n lastFailureTime: number | null;\n lastSuccessTime: number | null;\n halfOpenSuccesses: number;\n}\n\n// =============================================================================\n// Default Configuration\n// =============================================================================\n\nconst DEFAULT_CONFIG: CircuitBreakerConfig = {\n failureThreshold: 5,\n resetTimeout: 30_000,\n windowSize: 60_000,\n halfOpenMax: 3,\n};\n\n// =============================================================================\n// Circuit Breaker Implementation\n// =============================================================================\n\n/**\n * Circuit breaker instance for protecting external calls.\n */\nexport interface CircuitBreaker {\n /**\n * Execute an operation with circuit breaker protection.\n * Throws CircuitOpenError if the circuit is open.\n *\n * @param operation - The operation to execute\n * @param options - Optional name for logging/metrics\n * @returns The operation result\n * @throws CircuitOpenError if circuit is open\n */\n execute<T>(\n operation: () => T | Promise<T>,\n options?: { name?: string }\n ): Promise<T>;\n\n /**\n * Execute a Result-returning operation with circuit breaker protection.\n * Returns a CircuitOpenError result instead of throwing.\n *\n * @param operation - The operation returning a Result\n * @param options - Optional name for logging/metrics\n * @returns Result with the value or CircuitOpenError\n */\n executeResult<T, E>(\n operation: () => Result<T, E> | AsyncResult<T, E>,\n options?: { name?: string }\n ): AsyncResult<T, E | CircuitOpenError>;\n\n /**\n * Get current circuit state.\n */\n getState(): CircuitState;\n\n /**\n * Get circuit breaker statistics.\n */\n getStats(): CircuitBreakerStats;\n\n /**\n * Manually reset the circuit breaker to CLOSED state.\n */\n reset(): void;\n\n /**\n * Manually open the circuit (for testing or manual intervention).\n */\n forceOpen(): void;\n\n /**\n * Record a manual success (useful for health checks).\n */\n recordSuccess(): void;\n\n /**\n * Record a manual failure (useful for health checks).\n */\n recordFailure(error?: unknown): void;\n}\n\n/**\n * Create a circuit breaker instance.\n *\n * @param name - Name for this circuit breaker (used in errors and logging)\n * @param config - Configuration options\n * @returns A CircuitBreaker instance\n *\n * @example\n * ```typescript\n * const apiBreaker = createCircuitBreaker('external-api', {\n * failureThreshold: 5,\n * resetTimeout: 30000,\n * });\n *\n * // In workflow\n * const data = await apiBreaker.execute(() =>\n * step(() => fetchFromApi(id))\n * );\n * ```\n */\nexport function createCircuitBreaker(\n name: string,\n config?: Partial<CircuitBreakerConfig>\n): CircuitBreaker {\n const effectiveConfig: CircuitBreakerConfig = {\n ...DEFAULT_CONFIG,\n ...config,\n };\n\n let state: CircuitState = \"CLOSED\";\n let failures: FailureRecord[] = [];\n let lastFailureTime: number | null = null;\n let lastSuccessTime: number | null = null;\n let successCount = 0;\n let halfOpenSuccesses = 0;\n\n /**\n * Clean up old failures outside the time window.\n */\n function cleanupFailures(): void {\n const now = Date.now();\n failures = failures.filter(\n (f) => now - f.timestamp < effectiveConfig.windowSize\n );\n }\n\n /**\n * Transition to a new state.\n */\n function transitionTo(newState: CircuitState): void {\n if (state !== newState) {\n const oldState = state;\n state = newState;\n if (newState === \"HALF_OPEN\") {\n halfOpenSuccesses = 0;\n }\n effectiveConfig.onStateChange?.(oldState, newState, name);\n }\n }\n\n /**\n * Check if we should transition from OPEN to HALF_OPEN.\n */\n function checkOpenToHalfOpen(): boolean {\n if (state !== \"OPEN\" || lastFailureTime === null) {\n return false;\n }\n const now = Date.now();\n if (now - lastFailureTime >= effectiveConfig.resetTimeout) {\n transitionTo(\"HALF_OPEN\");\n return true;\n }\n return false;\n }\n\n /**\n * Record a successful operation.\n */\n function handleSuccess(): void {\n lastSuccessTime = Date.now();\n successCount++;\n\n if (state === \"HALF_OPEN\") {\n halfOpenSuccesses++;\n if (halfOpenSuccesses >= effectiveConfig.halfOpenMax) {\n // All test requests succeeded, close the circuit\n transitionTo(\"CLOSED\");\n failures = [];\n }\n }\n }\n\n /**\n * Record a failed operation.\n */\n function handleFailure(error: unknown): void {\n const now = Date.now();\n lastFailureTime = now;\n\n // Clean up old failures first\n cleanupFailures();\n\n // Add new failure\n failures.push({ timestamp: now, error });\n\n if (state === \"HALF_OPEN\") {\n // Any failure in HALF_OPEN reopens the circuit\n transitionTo(\"OPEN\");\n } else if (state === \"CLOSED\") {\n // Check if we should open the circuit\n if (failures.length >= effectiveConfig.failureThreshold) {\n transitionTo(\"OPEN\");\n }\n }\n }\n\n /**\n * Check if the circuit allows execution.\n * Returns the remaining wait time if blocked, or 0 if allowed.\n */\n function canExecute(): number {\n if (state === \"CLOSED\") {\n return 0;\n }\n\n if (state === \"OPEN\") {\n // Check if we should transition to HALF_OPEN\n if (checkOpenToHalfOpen()) {\n return 0; // Now in HALF_OPEN, allow execution\n }\n // Still OPEN, calculate remaining wait time\n const now = Date.now();\n const elapsed = lastFailureTime ? now - lastFailureTime : 0;\n return Math.max(0, effectiveConfig.resetTimeout - elapsed);\n }\n\n // HALF_OPEN - allow limited test requests\n return 0;\n }\n\n return {\n async execute<T>(\n operation: () => T | Promise<T>,\n _options?: { name?: string }\n ): Promise<T> {\n const waitTime = canExecute();\n if (waitTime > 0) {\n throw new CircuitOpenError({\n circuitName: name,\n state,\n retryAfterMs: waitTime,\n });\n }\n\n try {\n const result = await operation();\n handleSuccess();\n return result;\n } catch (error) {\n handleFailure(error);\n throw error;\n }\n },\n\n async executeResult<T, E>(\n operation: () => Result<T, E> | AsyncResult<T, E>,\n _options?: { name?: string }\n ): AsyncResult<T, E | CircuitOpenError> {\n const waitTime = canExecute();\n if (waitTime > 0) {\n return err(\n new CircuitOpenError({\n circuitName: name,\n state,\n retryAfterMs: waitTime,\n })\n );\n }\n\n try {\n const result = await operation();\n if (result.ok) {\n handleSuccess();\n } else {\n handleFailure(result.error);\n }\n return result;\n } catch (error) {\n handleFailure(error);\n throw error;\n }\n },\n\n getState(): CircuitState {\n // Check for automatic transition before returning\n if (state === \"OPEN\") {\n checkOpenToHalfOpen();\n }\n return state;\n },\n\n getStats(): CircuitBreakerStats {\n cleanupFailures();\n return {\n state: this.getState(),\n failureCount: failures.length,\n successCount,\n lastFailureTime,\n lastSuccessTime,\n halfOpenSuccesses,\n };\n },\n\n reset(): void {\n transitionTo(\"CLOSED\");\n failures = [];\n halfOpenSuccesses = 0;\n },\n\n forceOpen(): void {\n lastFailureTime = Date.now();\n transitionTo(\"OPEN\");\n },\n\n recordSuccess(): void {\n handleSuccess();\n },\n\n recordFailure(error?: unknown): void {\n handleFailure(error ?? new Error(\"Manual failure\"));\n },\n };\n}\n\n// =============================================================================\n// Presets\n// =============================================================================\n\n/**\n * Preset configurations for common use cases.\n */\nexport const circuitBreakerPresets = {\n /**\n * Aggressive circuit breaker for critical paths.\n * Opens quickly (3 failures) and recovers slowly (60s).\n */\n critical: {\n failureThreshold: 3,\n resetTimeout: 60_000,\n windowSize: 30_000,\n halfOpenMax: 1,\n } satisfies Partial<CircuitBreakerConfig>,\n\n /**\n * Standard circuit breaker for typical API calls.\n * Balanced between stability and availability.\n */\n standard: {\n failureThreshold: 5,\n resetTimeout: 30_000,\n windowSize: 60_000,\n halfOpenMax: 3,\n } satisfies Partial<CircuitBreakerConfig>,\n\n /**\n * Lenient circuit breaker for non-critical operations.\n * Opens slowly (10 failures) and recovers quickly (15s).\n */\n lenient: {\n failureThreshold: 10,\n resetTimeout: 15_000,\n windowSize: 120_000,\n halfOpenMax: 5,\n } satisfies Partial<CircuitBreakerConfig>,\n} as const;\n","/**\n * Saga / Compensation Pattern\n *\n * Define compensating actions for steps that need rollback on downstream failures.\n * When a workflow fails after some steps have completed, compensations run in\n * reverse order automatically.\n *\n * @example\n * ```typescript\n * import { createSagaWorkflow, ok, err } from '@jagreehal/workflow';\n *\n * const checkout = createSagaWorkflow({ reserveInventory, chargeCard, sendEmail });\n *\n * const result = await checkout(async (saga) => {\n * const reservation = await saga.step(\n * () => reserveInventory(items),\n * { compensate: (res) => releaseInventory(res.reservationId) }\n * );\n *\n * const payment = await saga.step(\n * () => chargeCard(amount),\n * { compensate: (p) => refundPayment(p.txId) }\n * );\n *\n * await saga.step(() => sendEmail(userId)); // No compensation needed\n *\n * return { reservation, payment };\n * });\n * // On failure: compensations run in reverse order automatically\n * ```\n */\n\nimport {\n ok,\n err,\n type Result,\n type AsyncResult,\n type UnexpectedError,\n isEarlyExit,\n createEarlyExit,\n type EarlyExit,\n type WorkflowEvent,\n} from \"./core\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * A compensation action to run on rollback.\n */\nexport type CompensationAction<T> = (value: T) => void | Promise<void>;\n\n/**\n * Options for a saga step.\n */\nexport interface SagaStepOptions<T> {\n /**\n * Name for the step (used in logging and events).\n */\n name?: string;\n\n /**\n * Compensation action to run if a later step fails.\n * Receives the value returned by this step.\n */\n compensate?: CompensationAction<T>;\n}\n\n/**\n * A recorded compensation with its value.\n */\ninterface RecordedCompensation<T = unknown> {\n name?: string;\n value: T;\n compensate: CompensationAction<T>;\n}\n\n/**\n * Error returned when compensation actions fail.\n */\nexport interface SagaCompensationError {\n type: \"SAGA_COMPENSATION_ERROR\";\n /** The original error that triggered the saga rollback */\n originalError: unknown;\n /** Errors from failed compensation actions */\n compensationErrors: Array<{\n stepName?: string;\n error: unknown;\n }>;\n}\n\n/**\n * Type guard for SagaCompensationError.\n */\nexport function isSagaCompensationError(\n error: unknown\n): error is SagaCompensationError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as SagaCompensationError).type === \"SAGA_COMPENSATION_ERROR\"\n );\n}\n\n/**\n * Saga execution context provided to the workflow callback.\n */\nexport interface SagaContext<E = unknown> {\n /**\n * Execute a step with optional compensation.\n *\n * @param operation - The operation to execute (returns Result)\n * @param options - Step options including compensation action\n * @returns The unwrapped success value\n */\n step: <T, StepE extends E, StepC = unknown>(\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n options?: SagaStepOptions<T>\n ) => Promise<T>;\n\n /**\n * Execute a throwing operation with optional compensation.\n *\n * @param operation - The operation to execute (may throw)\n * @param options - Step options including error mapping and compensation\n * @returns The success value\n */\n tryStep: <T, Err extends E>(\n operation: () => T | Promise<T>,\n options: {\n error: Err;\n name?: string;\n compensate?: CompensationAction<T>;\n } | {\n onError: (cause: unknown) => Err;\n name?: string;\n compensate?: CompensationAction<T>;\n }\n ) => Promise<T>;\n\n /**\n * Get all recorded compensations (for debugging/testing).\n */\n getCompensations: () => Array<{ name?: string; hasValue: boolean }>;\n}\n\n/**\n * Saga event types for observability.\n */\nexport type SagaEvent =\n | { type: \"saga_start\"; sagaId: string; ts: number }\n | { type: \"saga_success\"; sagaId: string; ts: number; durationMs: number }\n | { type: \"saga_error\"; sagaId: string; ts: number; durationMs: number; error: unknown }\n | { type: \"saga_compensation_start\"; sagaId: string; ts: number; stepCount: number }\n | { type: \"saga_compensation_step\"; sagaId: string; stepName?: string; ts: number; success: boolean; error?: unknown }\n | { type: \"saga_compensation_end\"; sagaId: string; ts: number; durationMs: number; success: boolean; failedCount: number };\n\n/**\n * Options for createSagaWorkflow.\n */\nexport interface SagaWorkflowOptions<E> {\n /**\n * Called when errors occur.\n */\n onError?: (error: E | UnexpectedError | SagaCompensationError, stepName?: string) => void;\n\n /**\n * Event stream for saga lifecycle events.\n */\n onEvent?: (event: SagaEvent | WorkflowEvent<E | UnexpectedError>) => void;\n\n /**\n * Whether to throw if compensation actions fail.\n * If false, original error is returned with compensation errors attached.\n * @default false\n */\n throwOnCompensationFailure?: boolean;\n}\n\n/**\n * Result type for saga workflow.\n */\nexport type SagaResult<T, E> = Result<T, E | UnexpectedError | SagaCompensationError, unknown>;\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Helper type for Result-returning functions.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyResultFn = (...args: any[]) => Result<any, any, any> | Promise<Result<any, any, any>>;\n\n/**\n * Extract union of error types from a deps object.\n */\ntype ErrorsOfDeps<Deps extends Record<string, AnyResultFn>> = {\n [K in keyof Deps]: Deps[K] extends (...args: never[]) => infer R\n ? R extends Promise<infer PR>\n ? PR extends { ok: false; error: infer E }\n ? E\n : never\n : R extends { ok: false; error: infer E }\n ? E\n : never\n : never;\n}[keyof Deps];\n\n/**\n * Create a saga workflow with automatic compensation on failure.\n *\n * @param deps - Object mapping names to Result-returning functions\n * @param options - Saga workflow options\n * @returns A saga executor function\n *\n * @example\n * ```typescript\n * const saga = createSagaWorkflow({ reserveInventory, chargeCard });\n *\n * const result = await saga(async (ctx) => {\n * const reservation = await ctx.step(\n * () => reserveInventory(items),\n * { compensate: (res) => releaseInventory(res.id) }\n * );\n *\n * const payment = await ctx.step(\n * () => chargeCard(amount),\n * { compensate: (p) => refundPayment(p.txId) }\n * );\n *\n * return { reservation, payment };\n * });\n * ```\n */\nexport function createSagaWorkflow<\n const Deps extends Readonly<Record<string, AnyResultFn>>\n>(\n deps: Deps,\n options?: SagaWorkflowOptions<ErrorsOfDeps<Deps>>\n): <T>(\n fn: (saga: SagaContext<ErrorsOfDeps<Deps>>, deps: Deps) => Promise<T>\n) => Promise<SagaResult<T, ErrorsOfDeps<Deps>>> {\n type E = ErrorsOfDeps<Deps>;\n\n return async <T>(\n fn: (saga: SagaContext<E>, deps: Deps) => Promise<T>\n ): Promise<SagaResult<T, E | UnexpectedError | SagaCompensationError>> => {\n const sagaId = crypto.randomUUID();\n const startTime = performance.now();\n const compensations: RecordedCompensation[] = [];\n\n const emitEvent = (event: SagaEvent | WorkflowEvent<E | UnexpectedError>) => {\n options?.onEvent?.(event);\n };\n\n emitEvent({\n type: \"saga_start\",\n sagaId,\n ts: Date.now(),\n });\n\n /**\n * Run all compensations in reverse order.\n */\n async function runCompensations(\n _originalError: unknown\n ): Promise<Array<{ stepName?: string; error: unknown }>> {\n const errors: Array<{ stepName?: string; error: unknown }> = [];\n\n emitEvent({\n type: \"saga_compensation_start\",\n sagaId,\n ts: Date.now(),\n stepCount: compensations.length,\n });\n\n const compensationStartTime = performance.now();\n\n // Run compensations in reverse order\n for (let i = compensations.length - 1; i >= 0; i--) {\n const comp = compensations[i];\n try {\n await comp.compensate(comp.value);\n emitEvent({\n type: \"saga_compensation_step\",\n sagaId,\n stepName: comp.name,\n ts: Date.now(),\n success: true,\n });\n } catch (error) {\n errors.push({ stepName: comp.name, error });\n emitEvent({\n type: \"saga_compensation_step\",\n sagaId,\n stepName: comp.name,\n ts: Date.now(),\n success: false,\n error,\n });\n }\n }\n\n emitEvent({\n type: \"saga_compensation_end\",\n sagaId,\n ts: Date.now(),\n durationMs: performance.now() - compensationStartTime,\n success: errors.length === 0,\n failedCount: errors.length,\n });\n\n return errors;\n }\n\n // Create saga context\n const sagaContext: SagaContext<E> = {\n async step<T, StepE extends E, StepC = unknown>(\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n stepOptions?: SagaStepOptions<T>\n ): Promise<T> {\n const result = await operation();\n\n if (result.ok) {\n // Record compensation if provided\n if (stepOptions?.compensate) {\n compensations.push({\n name: stepOptions.name,\n value: result.value,\n compensate: stepOptions.compensate as CompensationAction<unknown>,\n });\n }\n return result.value;\n }\n\n // Step failed - throw early exit to trigger compensation\n throw createEarlyExit(result.error as unknown as E, {\n origin: \"result\",\n resultCause: result.cause,\n });\n },\n\n async tryStep<T, Err extends E>(\n operation: () => T | Promise<T>,\n opts: {\n error: Err;\n name?: string;\n compensate?: CompensationAction<T>;\n } | {\n onError: (cause: unknown) => Err;\n name?: string;\n compensate?: CompensationAction<T>;\n }\n ): Promise<T> {\n const mapToError = \"error\" in opts ? () => opts.error : opts.onError;\n\n try {\n const value = await operation();\n\n // Record compensation if provided\n if (opts.compensate) {\n compensations.push({\n name: opts.name,\n value,\n compensate: opts.compensate as CompensationAction<unknown>,\n });\n }\n\n return value;\n } catch (thrown) {\n const mapped = mapToError(thrown);\n throw createEarlyExit(mapped as unknown as E, {\n origin: \"throw\",\n thrown,\n });\n }\n },\n\n getCompensations() {\n return compensations.map((c) => ({\n name: c.name,\n hasValue: c.value !== undefined,\n }));\n },\n };\n\n try {\n const result = await fn(sagaContext, deps);\n\n const durationMs = performance.now() - startTime;\n emitEvent({\n type: \"saga_success\",\n sagaId,\n ts: Date.now(),\n durationMs,\n });\n\n return ok(result);\n } catch (thrown) {\n const durationMs = performance.now() - startTime;\n\n // Extract the actual error from early exit\n let originalError: unknown;\n if (isEarlyExit(thrown)) {\n originalError = (thrown as EarlyExit<E>).error;\n } else {\n originalError = thrown;\n }\n\n emitEvent({\n type: \"saga_error\",\n sagaId,\n ts: Date.now(),\n durationMs,\n error: originalError,\n });\n\n // Run compensations\n const compensationErrors = await runCompensations(originalError);\n\n // Handle compensation failures\n if (compensationErrors.length > 0) {\n const sagaError: SagaCompensationError = {\n type: \"SAGA_COMPENSATION_ERROR\",\n originalError,\n compensationErrors,\n };\n\n options?.onError?.(sagaError);\n\n if (options?.throwOnCompensationFailure) {\n throw sagaError;\n }\n\n return err(sagaError);\n }\n\n // Compensation succeeded - return original error\n options?.onError?.(originalError as E);\n\n // Wrap non-typed errors as UnexpectedError\n if (!isEarlyExit(thrown)) {\n return err({\n type: \"UNEXPECTED_ERROR\",\n cause: { type: \"UNCAUGHT_EXCEPTION\", thrown },\n } as UnexpectedError);\n }\n\n return err(originalError as E);\n }\n };\n}\n\n/**\n * Run a saga with explicit compensation registration.\n *\n * Lower-level API for when you don't want automatic error inference\n * from a deps object.\n *\n * @example\n * ```typescript\n * const result = await runSaga<CheckoutResult, CheckoutError>(async (saga) => {\n * const reservation = await saga.step(\n * () => reserveInventory(items),\n * { compensate: (res) => releaseInventory(res.id) }\n * );\n * return { reservation };\n * });\n * ```\n */\nexport async function runSaga<T, E>(\n fn: (saga: SagaContext<E>) => Promise<T>,\n options?: Omit<SagaWorkflowOptions<E>, \"onEvent\"> & {\n onEvent?: (event: SagaEvent) => void;\n }\n): Promise<SagaResult<T, E>> {\n const sagaId = crypto.randomUUID();\n const startTime = performance.now();\n const compensations: RecordedCompensation[] = [];\n\n const emitEvent = (event: SagaEvent) => {\n options?.onEvent?.(event);\n };\n\n emitEvent({\n type: \"saga_start\",\n sagaId,\n ts: Date.now(),\n });\n\n /**\n * Run all compensations in reverse order.\n */\n async function runCompensations(\n _originalError: unknown\n ): Promise<Array<{ stepName?: string; error: unknown }>> {\n const errors: Array<{ stepName?: string; error: unknown }> = [];\n\n emitEvent({\n type: \"saga_compensation_start\",\n sagaId,\n ts: Date.now(),\n stepCount: compensations.length,\n });\n\n const compensationStartTime = performance.now();\n\n // Run compensations in reverse order\n for (let i = compensations.length - 1; i >= 0; i--) {\n const comp = compensations[i];\n try {\n await comp.compensate(comp.value);\n emitEvent({\n type: \"saga_compensation_step\",\n sagaId,\n stepName: comp.name,\n ts: Date.now(),\n success: true,\n });\n } catch (error) {\n errors.push({ stepName: comp.name, error });\n emitEvent({\n type: \"saga_compensation_step\",\n sagaId,\n stepName: comp.name,\n ts: Date.now(),\n success: false,\n error,\n });\n }\n }\n\n emitEvent({\n type: \"saga_compensation_end\",\n sagaId,\n ts: Date.now(),\n durationMs: performance.now() - compensationStartTime,\n success: errors.length === 0,\n failedCount: errors.length,\n });\n\n return errors;\n }\n\n // Create saga context\n const sagaContext: SagaContext<E> = {\n async step<T, StepE extends E, StepC = unknown>(\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n stepOptions?: SagaStepOptions<T>\n ): Promise<T> {\n const result = await operation();\n\n if (result.ok) {\n if (stepOptions?.compensate) {\n compensations.push({\n name: stepOptions.name,\n value: result.value,\n compensate: stepOptions.compensate as CompensationAction<unknown>,\n });\n }\n return result.value;\n }\n\n throw createEarlyExit(result.error as unknown as E, {\n origin: \"result\",\n resultCause: result.cause,\n });\n },\n\n async tryStep<T, Err extends E>(\n operation: () => T | Promise<T>,\n opts: {\n error: Err;\n name?: string;\n compensate?: CompensationAction<T>;\n } | {\n onError: (cause: unknown) => Err;\n name?: string;\n compensate?: CompensationAction<T>;\n }\n ): Promise<T> {\n const mapToError = \"error\" in opts ? () => opts.error : opts.onError;\n\n try {\n const value = await operation();\n\n if (opts.compensate) {\n compensations.push({\n name: opts.name,\n value,\n compensate: opts.compensate as CompensationAction<unknown>,\n });\n }\n\n return value;\n } catch (thrown) {\n const mapped = mapToError(thrown);\n throw createEarlyExit(mapped as unknown as E, {\n origin: \"throw\",\n thrown,\n });\n }\n },\n\n getCompensations() {\n return compensations.map((c) => ({\n name: c.name,\n hasValue: c.value !== undefined,\n }));\n },\n };\n\n try {\n const result = await fn(sagaContext);\n\n const durationMs = performance.now() - startTime;\n emitEvent({\n type: \"saga_success\",\n sagaId,\n ts: Date.now(),\n durationMs,\n });\n\n return ok(result);\n } catch (thrown) {\n const durationMs = performance.now() - startTime;\n\n let originalError: unknown;\n if (isEarlyExit(thrown)) {\n originalError = (thrown as EarlyExit<E>).error;\n } else {\n originalError = thrown;\n }\n\n emitEvent({\n type: \"saga_error\",\n sagaId,\n ts: Date.now(),\n durationMs,\n error: originalError,\n });\n\n const compensationErrors = await runCompensations(originalError);\n\n if (compensationErrors.length > 0) {\n const sagaError: SagaCompensationError = {\n type: \"SAGA_COMPENSATION_ERROR\",\n originalError,\n compensationErrors,\n };\n\n options?.onError?.(sagaError);\n\n if (options?.throwOnCompensationFailure) {\n throw sagaError;\n }\n\n return err(sagaError);\n }\n\n options?.onError?.(originalError as E);\n\n if (!isEarlyExit(thrown)) {\n return err({\n type: \"UNEXPECTED_ERROR\",\n cause: { type: \"UNCAUGHT_EXCEPTION\", thrown },\n } as UnexpectedError);\n }\n\n return err(originalError as E);\n }\n}\n","/**\n * Rate Limiting / Concurrency Control\n *\n * Control throughput for steps that hit rate-limited APIs or shared resources.\n *\n * @example\n * ```typescript\n * import { createRateLimiter, createConcurrencyLimiter } from '@jagreehal/workflow';\n *\n * // Rate limiting (requests per second)\n * const rateLimiter = createRateLimiter({ maxPerSecond: 10 });\n *\n * // Concurrency limiting (max concurrent)\n * const concurrencyLimiter = createConcurrencyLimiter({ maxConcurrent: 5 });\n *\n * const result = await workflow(async (step) => {\n * // Wrap operations with rate limiting\n * const data = await rateLimiter.execute(() =>\n * step(() => callRateLimitedApi())\n * );\n *\n * // Wrap batch operations with concurrency control\n * const results = await concurrencyLimiter.executeAll(\n * ids.map(id => () => step(() => fetchItem(id)))\n * );\n *\n * return { data, results };\n * });\n * ```\n */\n\nimport { err, type Result, type AsyncResult } from \"./core\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Configuration for rate limiter.\n */\nexport interface RateLimiterConfig {\n /**\n * Maximum operations per second.\n */\n maxPerSecond: number;\n\n /**\n * Burst capacity - allows brief spikes above the rate.\n * @default maxPerSecond * 2\n */\n burstCapacity?: number;\n\n /**\n * Strategy when rate limit is exceeded.\n * - 'wait': Wait until a slot is available (default)\n * - 'reject': Reject immediately with error\n * @default 'wait'\n */\n strategy?: \"wait\" | \"reject\";\n}\n\n/**\n * Configuration for concurrency limiter.\n */\nexport interface ConcurrencyLimiterConfig {\n /**\n * Maximum concurrent operations.\n */\n maxConcurrent: number;\n\n /**\n * Strategy when limit is reached.\n * - 'queue': Queue and wait (default)\n * - 'reject': Reject immediately\n * @default 'queue'\n */\n strategy?: \"queue\" | \"reject\";\n\n /**\n * Maximum queue size (only for 'queue' strategy).\n * @default Infinity\n */\n maxQueueSize?: number;\n}\n\n/**\n * Error when rate/concurrency limit is exceeded and strategy is 'reject'.\n */\nexport interface RateLimitExceededError {\n type: \"RATE_LIMIT_EXCEEDED\";\n limiterName: string;\n retryAfterMs?: number;\n}\n\n/**\n * Error when concurrency limit queue is full.\n */\nexport interface QueueFullError {\n type: \"QUEUE_FULL\";\n limiterName: string;\n queueSize: number;\n maxQueueSize: number;\n}\n\n/**\n * Type guard for RateLimitExceededError.\n */\nexport function isRateLimitExceededError(\n error: unknown\n): error is RateLimitExceededError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as RateLimitExceededError).type === \"RATE_LIMIT_EXCEEDED\"\n );\n}\n\n/**\n * Type guard for QueueFullError.\n */\nexport function isQueueFullError(error: unknown): error is QueueFullError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as QueueFullError).type === \"QUEUE_FULL\"\n );\n}\n\n/**\n * Statistics for rate limiter.\n */\nexport interface RateLimiterStats {\n availableTokens: number;\n maxTokens: number;\n tokensPerSecond: number;\n waitingCount: number;\n}\n\n/**\n * Statistics for concurrency limiter.\n */\nexport interface ConcurrencyLimiterStats {\n activeCount: number;\n maxConcurrent: number;\n queueSize: number;\n maxQueueSize: number;\n}\n\n// =============================================================================\n// Rate Limiter (Token Bucket)\n// =============================================================================\n\n/**\n * Rate limiter interface.\n */\nexport interface RateLimiter {\n /**\n * Execute an operation with rate limiting.\n * @param operation - The operation to execute\n * @returns The operation result\n */\n execute<T>(operation: () => T | Promise<T>): Promise<T>;\n\n /**\n * Execute a Result-returning operation with rate limiting.\n */\n executeResult<T, E>(\n operation: () => Result<T, E> | AsyncResult<T, E>\n ): AsyncResult<T, E | RateLimitExceededError>;\n\n /**\n * Get current statistics.\n */\n getStats(): RateLimiterStats;\n\n /**\n * Reset the rate limiter.\n */\n reset(): void;\n}\n\n/**\n * Create a token bucket rate limiter.\n *\n * @param name - Name for the limiter (used in errors)\n * @param config - Rate limiter configuration\n * @returns A RateLimiter instance\n *\n * @example\n * ```typescript\n * const limiter = createRateLimiter('api-calls', {\n * maxPerSecond: 10,\n * burstCapacity: 20,\n * });\n *\n * // In workflow\n * const data = await limiter.execute(() =>\n * step(() => callApi())\n * );\n * ```\n */\nexport function createRateLimiter(\n name: string,\n config: RateLimiterConfig\n): RateLimiter {\n const { maxPerSecond, strategy = \"wait\" } = config;\n const maxTokens = config.burstCapacity ?? maxPerSecond * 2;\n\n let tokens = maxTokens;\n let lastRefill = Date.now();\n const refillRate = maxPerSecond / 1000; // tokens per ms\n\n // Queue for waiting requests\n const waitQueue: Array<() => void> = [];\n\n /**\n * Refill tokens based on elapsed time.\n */\n function refill(): void {\n const now = Date.now();\n const elapsed = now - lastRefill;\n const tokensToAdd = elapsed * refillRate;\n tokens = Math.min(maxTokens, tokens + tokensToAdd);\n lastRefill = now;\n }\n\n /**\n * Try to consume a token.\n * Returns remaining wait time if no tokens available.\n */\n function tryConsume(): number {\n refill();\n if (tokens >= 1) {\n tokens -= 1;\n return 0;\n }\n // Calculate wait time for next token\n const tokensNeeded = 1 - tokens;\n return Math.ceil(tokensNeeded / refillRate);\n }\n\n /**\n * Wait for a token to be available.\n */\n async function waitForToken(): Promise<void> {\n return new Promise((resolve) => {\n const check = () => {\n const waitTime = tryConsume();\n if (waitTime === 0) {\n resolve();\n } else {\n waitQueue.push(check);\n setTimeout(() => {\n const idx = waitQueue.indexOf(check);\n if (idx !== -1) {\n waitQueue.splice(idx, 1);\n check();\n }\n }, waitTime);\n }\n };\n check();\n });\n }\n\n return {\n async execute<T>(operation: () => T | Promise<T>): Promise<T> {\n const waitTime = tryConsume();\n\n if (waitTime > 0) {\n if (strategy === \"reject\") {\n throw {\n type: \"RATE_LIMIT_EXCEEDED\",\n limiterName: name,\n retryAfterMs: waitTime,\n } as RateLimitExceededError;\n }\n\n // Wait strategy\n await waitForToken();\n }\n\n return operation();\n },\n\n async executeResult<T, E>(\n operation: () => Result<T, E> | AsyncResult<T, E>\n ): AsyncResult<T, E | RateLimitExceededError> {\n const waitTime = tryConsume();\n\n if (waitTime > 0) {\n if (strategy === \"reject\") {\n return err({\n type: \"RATE_LIMIT_EXCEEDED\",\n limiterName: name,\n retryAfterMs: waitTime,\n });\n }\n\n // Wait strategy\n await waitForToken();\n }\n\n return operation();\n },\n\n getStats(): RateLimiterStats {\n refill();\n return {\n availableTokens: Math.floor(tokens),\n maxTokens,\n tokensPerSecond: maxPerSecond,\n waitingCount: waitQueue.length,\n };\n },\n\n reset(): void {\n tokens = maxTokens;\n lastRefill = Date.now();\n // Clear wait queue\n waitQueue.length = 0;\n },\n };\n}\n\n// =============================================================================\n// Concurrency Limiter\n// =============================================================================\n\n/**\n * Concurrency limiter interface.\n */\nexport interface ConcurrencyLimiter {\n /**\n * Execute an operation with concurrency limiting.\n * @param operation - The operation to execute\n * @returns The operation result\n */\n execute<T>(operation: () => T | Promise<T>): Promise<T>;\n\n /**\n * Execute multiple operations with concurrency control.\n * @param operations - Array of operation factories\n * @returns Array of results (in order)\n */\n executeAll<T>(operations: Array<() => T | Promise<T>>): Promise<T[]>;\n\n /**\n * Execute a Result-returning operation with concurrency limiting.\n */\n executeResult<T, E>(\n operation: () => Result<T, E> | AsyncResult<T, E>\n ): AsyncResult<T, E | QueueFullError>;\n\n /**\n * Get current statistics.\n */\n getStats(): ConcurrencyLimiterStats;\n\n /**\n * Reset the concurrency limiter.\n */\n reset(): void;\n}\n\n/**\n * Create a concurrency limiter.\n *\n * @param name - Name for the limiter (used in errors)\n * @param config - Concurrency limiter configuration\n * @returns A ConcurrencyLimiter instance\n *\n * @example\n * ```typescript\n * const limiter = createConcurrencyLimiter('db-pool', {\n * maxConcurrent: 10,\n * });\n *\n * // Execute with concurrency control\n * const results = await limiter.executeAll(\n * ids.map(id => () => fetchItem(id))\n * );\n * ```\n */\nexport function createConcurrencyLimiter(\n name: string,\n config: ConcurrencyLimiterConfig\n): ConcurrencyLimiter {\n const { maxConcurrent, strategy = \"queue\", maxQueueSize = Infinity } = config;\n\n let activeCount = 0;\n const queue: Array<{ resolve: () => void; reject: (e: unknown) => void }> = [];\n\n /**\n * Acquire a slot.\n */\n async function acquire(): Promise<void> {\n if (activeCount < maxConcurrent) {\n activeCount++;\n return;\n }\n\n if (strategy === \"reject\") {\n throw {\n type: \"QUEUE_FULL\",\n limiterName: name,\n queueSize: queue.length,\n maxQueueSize,\n } as QueueFullError;\n }\n\n // Queue strategy\n if (queue.length >= maxQueueSize) {\n throw {\n type: \"QUEUE_FULL\",\n limiterName: name,\n queueSize: queue.length,\n maxQueueSize,\n } as QueueFullError;\n }\n\n return new Promise<void>((resolve, reject) => {\n queue.push({ resolve, reject });\n });\n }\n\n /**\n * Release a slot.\n */\n function release(): void {\n activeCount--;\n if (queue.length > 0 && activeCount < maxConcurrent) {\n activeCount++;\n const next = queue.shift();\n next?.resolve();\n }\n }\n\n return {\n async execute<T>(operation: () => T | Promise<T>): Promise<T> {\n await acquire();\n try {\n return await operation();\n } finally {\n release();\n }\n },\n\n async executeAll<T>(operations: Array<() => T | Promise<T>>): Promise<T[]> {\n const results: T[] = new Array(operations.length);\n const executing: Promise<void>[] = [];\n\n for (let i = 0; i < operations.length; i++) {\n const index = i;\n const promise = this.execute(operations[index]).then((result) => {\n results[index] = result;\n });\n executing.push(promise);\n }\n\n await Promise.all(executing);\n return results;\n },\n\n async executeResult<T, E>(\n operation: () => Result<T, E> | AsyncResult<T, E>\n ): AsyncResult<T, E | QueueFullError> {\n try {\n await acquire();\n } catch (error) {\n if (isQueueFullError(error)) {\n return err(error);\n }\n throw error;\n }\n\n try {\n return await operation();\n } finally {\n release();\n }\n },\n\n getStats(): ConcurrencyLimiterStats {\n return {\n activeCount,\n maxConcurrent,\n queueSize: queue.length,\n maxQueueSize,\n };\n },\n\n reset(): void {\n activeCount = 0;\n // Reject all queued operations\n while (queue.length > 0) {\n const item = queue.shift();\n item?.reject(new Error(\"Limiter reset\"));\n }\n },\n };\n}\n\n// =============================================================================\n// Combined Limiter\n// =============================================================================\n\n/**\n * Configuration for combined rate + concurrency limiter.\n */\nexport interface CombinedLimiterConfig {\n /**\n * Rate limiting configuration.\n */\n rate?: RateLimiterConfig;\n\n /**\n * Concurrency limiting configuration.\n */\n concurrency?: ConcurrencyLimiterConfig;\n}\n\n/**\n * Create a combined rate + concurrency limiter.\n *\n * Operations are first rate-limited, then concurrency-limited.\n *\n * @param name - Name for the limiter\n * @param config - Combined limiter configuration\n * @returns An object with both limiters and a combined execute function\n *\n * @example\n * ```typescript\n * const limiter = createCombinedLimiter('api', {\n * rate: { maxPerSecond: 10 },\n * concurrency: { maxConcurrent: 5 },\n * });\n *\n * const result = await limiter.execute(() => callApi());\n * ```\n */\nexport function createCombinedLimiter(\n name: string,\n config: CombinedLimiterConfig\n): {\n rate?: RateLimiter;\n concurrency?: ConcurrencyLimiter;\n execute: <T>(operation: () => T | Promise<T>) => Promise<T>;\n} {\n const rate = config.rate ? createRateLimiter(`${name}-rate`, config.rate) : undefined;\n const concurrency = config.concurrency\n ? createConcurrencyLimiter(`${name}-concurrency`, config.concurrency)\n : undefined;\n\n return {\n rate,\n concurrency,\n\n async execute<T>(operation: () => T | Promise<T>): Promise<T> {\n // Apply rate limiting first\n let op = operation;\n if (rate) {\n const originalOp = op;\n op = () => rate.execute(originalOp);\n }\n\n // Then apply concurrency limiting\n if (concurrency) {\n return concurrency.execute(op);\n }\n\n return op();\n },\n };\n}\n\n// =============================================================================\n// Presets\n// =============================================================================\n\n/**\n * Preset configurations for common use cases.\n */\nexport const rateLimiterPresets = {\n /**\n * Typical API rate limit (10 req/s).\n */\n api: {\n maxPerSecond: 10,\n burstCapacity: 20,\n strategy: \"wait\",\n } satisfies RateLimiterConfig,\n\n /**\n * Database pool limit (concurrent connections).\n */\n database: {\n maxConcurrent: 10,\n strategy: \"queue\",\n maxQueueSize: 100,\n } satisfies ConcurrencyLimiterConfig,\n\n /**\n * Aggressive rate limit for external APIs (5 req/s).\n */\n external: {\n maxPerSecond: 5,\n burstCapacity: 10,\n strategy: \"wait\",\n } satisfies RateLimiterConfig,\n} as const;\n","/**\n * Workflow Versioning and Migration\n *\n * Handle schema changes when resuming workflows that were persisted\n * with older step shapes.\n *\n * @example\n * ```typescript\n * import { createVersionedWorkflow } from '@jagreehal/workflow';\n *\n * const workflow = createVersionedWorkflow(\n * { fetchUser, chargeCard },\n * {\n * version: 2,\n * migrations: {\n * 1: (state) => migrateV1ToV2(state),\n * },\n * resumeState: loadState(runId),\n * }\n * );\n * ```\n */\n\nimport type { ResumeState, ResumeStateEntry } from \"./workflow\";\nimport { ok, err, type Result } from \"./core\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Version number type.\n */\nexport type Version = number;\n\n/**\n * Migration function that transforms state from one version to the next.\n */\nexport type MigrationFn = (state: ResumeState) => ResumeState | Promise<ResumeState>;\n\n/**\n * Map of migrations keyed by the source version.\n * Migration at key N transforms state from version N to version N+1.\n */\nexport type Migrations = Record<Version, MigrationFn>;\n\n/**\n * Versioned state includes the version number.\n */\nexport interface VersionedState {\n version: Version;\n state: ResumeState;\n}\n\n/**\n * Configuration for versioned workflow.\n */\nexport interface VersionedWorkflowConfig {\n /**\n * Current workflow version.\n */\n version: Version;\n\n /**\n * Migrations for upgrading old states.\n * Key is the source version, value transforms to next version.\n */\n migrations?: Migrations;\n\n /**\n * Strict mode - fail if state version is higher than current.\n * @default true\n */\n strictVersioning?: boolean;\n}\n\n/**\n * Error when version migration fails.\n */\nexport interface MigrationError {\n type: \"MIGRATION_ERROR\";\n fromVersion: Version;\n toVersion: Version;\n cause: unknown;\n}\n\n/**\n * Error when state version is incompatible.\n */\nexport interface VersionIncompatibleError {\n type: \"VERSION_INCOMPATIBLE\";\n stateVersion: Version;\n currentVersion: Version;\n reason: string;\n}\n\n/**\n * Type guard for MigrationError.\n */\nexport function isMigrationError(error: unknown): error is MigrationError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as MigrationError).type === \"MIGRATION_ERROR\"\n );\n}\n\n/**\n * Type guard for VersionIncompatibleError.\n */\nexport function isVersionIncompatibleError(\n error: unknown\n): error is VersionIncompatibleError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as VersionIncompatibleError).type === \"VERSION_INCOMPATIBLE\"\n );\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Migrate state from one version to another.\n *\n * @param state - The versioned state to migrate\n * @param targetVersion - The target version\n * @param migrations - Migration functions\n * @returns The migrated state or an error\n *\n * @example\n * ```typescript\n * const migrated = await migrateState(\n * { version: 1, state: oldState },\n * 3,\n * {\n * 1: (s) => transformV1ToV2(s),\n * 2: (s) => transformV2ToV3(s),\n * }\n * );\n * ```\n */\nexport async function migrateState(\n state: VersionedState,\n targetVersion: Version,\n migrations: Migrations\n): Promise<Result<VersionedState, MigrationError | VersionIncompatibleError>> {\n let currentState = state.state;\n let currentVersion = state.version;\n\n // Check if state is from the future\n if (currentVersion > targetVersion) {\n return err({\n type: \"VERSION_INCOMPATIBLE\",\n stateVersion: currentVersion,\n currentVersion: targetVersion,\n reason: \"State version is higher than current workflow version. Cannot downgrade.\",\n });\n }\n\n // Already at target version\n if (currentVersion === targetVersion) {\n return ok({ version: currentVersion, state: currentState });\n }\n\n // Apply migrations sequentially\n while (currentVersion < targetVersion) {\n const migration = migrations[currentVersion];\n\n if (!migration) {\n return err({\n type: \"VERSION_INCOMPATIBLE\",\n stateVersion: state.version,\n currentVersion: targetVersion,\n reason: `No migration found for version ${currentVersion} to ${currentVersion + 1}`,\n });\n }\n\n try {\n currentState = await migration(currentState);\n currentVersion++;\n } catch (cause) {\n return err({\n type: \"MIGRATION_ERROR\",\n fromVersion: currentVersion,\n toVersion: currentVersion + 1,\n cause,\n });\n }\n }\n\n return ok({ version: currentVersion, state: currentState });\n}\n\n/**\n * Create a versioned resume state loader.\n *\n * This wraps a state loader to automatically apply migrations\n * when loading older state versions.\n *\n * @param config - Versioning configuration\n * @returns A function that loads and migrates state\n *\n * @example\n * ```typescript\n * const loadVersionedState = createVersionedStateLoader({\n * version: 3,\n * migrations: {\n * 1: migrateV1ToV2,\n * 2: migrateV2ToV3,\n * },\n * });\n *\n * // In workflow\n * const workflow = createWorkflow(deps, {\n * resumeState: () => loadVersionedState(savedState),\n * });\n * ```\n */\nexport function createVersionedStateLoader(\n config: VersionedWorkflowConfig\n): (\n versionedState: VersionedState | null | undefined\n) => Promise<Result<ResumeState | undefined, MigrationError | VersionIncompatibleError>> {\n const { version, migrations = {}, strictVersioning = true } = config;\n\n return async (\n versionedState: VersionedState | null | undefined\n ): Promise<Result<ResumeState | undefined, MigrationError | VersionIncompatibleError>> => {\n // No saved state\n if (!versionedState) {\n return ok(undefined);\n }\n\n // Check for future version\n if (strictVersioning && versionedState.version > version) {\n return err({\n type: \"VERSION_INCOMPATIBLE\",\n stateVersion: versionedState.version,\n currentVersion: version,\n reason: \"Saved state is from a newer workflow version\",\n });\n }\n\n // Same version - no migration needed\n if (versionedState.version === version) {\n return ok(versionedState.state);\n }\n\n // Apply migrations\n const result = await migrateState(versionedState, version, migrations);\n if (!result.ok) {\n return result;\n }\n\n return ok(result.value.state);\n };\n}\n\n/**\n * Create versioned state from current resume state.\n *\n * Use this when saving state to storage.\n *\n * @param state - The current resume state\n * @param version - The current workflow version\n * @returns A versioned state object\n *\n * @example\n * ```typescript\n * const collector = createStepCollector();\n * // ... run workflow ...\n *\n * const versionedState = createVersionedState(collector.getState(), 2);\n * await db.saveWorkflowState(workflowId, versionedState);\n * ```\n */\nexport function createVersionedState(\n state: ResumeState,\n version: Version\n): VersionedState {\n return { version, state };\n}\n\n/**\n * Parse versioned state from JSON.\n *\n * Handles the serialization/deserialization of ResumeState with Map.\n *\n * @param json - The JSON string or parsed object\n * @returns The versioned state or null if invalid\n *\n * @example\n * ```typescript\n * const json = await db.loadWorkflowState(workflowId);\n * const versionedState = parseVersionedState(json);\n * if (versionedState) {\n * const loader = createVersionedStateLoader(config);\n * const state = await loader(versionedState);\n * }\n * ```\n */\ninterface SerializedVersionedState {\n version: number;\n state: { steps: Array<[string, ResumeStateEntry]> };\n}\n\nexport function parseVersionedState(\n json: string | SerializedVersionedState | null | undefined\n): VersionedState | null {\n if (!json) return null;\n\n try {\n const parsed: unknown = typeof json === \"string\" ? JSON.parse(json) : json;\n\n // Type guard\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n !(\"version\" in parsed) ||\n typeof (parsed as SerializedVersionedState).version !== \"number\" ||\n !(\"state\" in parsed) ||\n !(parsed as SerializedVersionedState).state ||\n !Array.isArray((parsed as SerializedVersionedState).state.steps)\n ) {\n return null;\n }\n\n const typedParsed = parsed as SerializedVersionedState;\n\n // Convert steps array back to Map\n const steps = new Map<string, ResumeStateEntry>(typedParsed.state.steps);\n\n return {\n version: typedParsed.version,\n state: { steps },\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Serialize versioned state to JSON.\n *\n * Converts the Map to an array for JSON serialization.\n *\n * @param state - The versioned state\n * @returns JSON string\n *\n * @example\n * ```typescript\n * const json = stringifyVersionedState(versionedState);\n * await db.saveWorkflowState(workflowId, json);\n * ```\n */\nexport function stringifyVersionedState(state: VersionedState): string {\n return JSON.stringify({\n version: state.version,\n state: {\n steps: Array.from(state.state.steps.entries()),\n },\n });\n}\n\n// =============================================================================\n// Migration Helpers\n// =============================================================================\n\n/**\n * Create a migration that renames step keys.\n *\n * @param renames - Map of old key to new key\n * @returns A migration function\n *\n * @example\n * ```typescript\n * const migrations = {\n * 1: createKeyRenameMigration({\n * 'user:fetch': 'user:load',\n * 'order:create': 'order:submit',\n * }),\n * };\n * ```\n */\nexport function createKeyRenameMigration(\n renames: Record<string, string>\n): MigrationFn {\n return (state: ResumeState): ResumeState => {\n const newSteps = new Map<string, ResumeStateEntry>();\n\n for (const [key, entry] of state.steps) {\n const newKey = renames[key] ?? key;\n newSteps.set(newKey, entry);\n }\n\n return { steps: newSteps };\n };\n}\n\n/**\n * Create a migration that removes specific step keys.\n *\n * @param keysToRemove - Array of keys to remove\n * @returns A migration function\n *\n * @example\n * ```typescript\n * const migrations = {\n * 1: createKeyRemoveMigration(['deprecated:step', 'old:cache']),\n * };\n * ```\n */\nexport function createKeyRemoveMigration(keysToRemove: string[]): MigrationFn {\n const keysSet = new Set(keysToRemove);\n return (state: ResumeState): ResumeState => {\n const newSteps = new Map<string, ResumeStateEntry>();\n\n for (const [key, entry] of state.steps) {\n if (!keysSet.has(key)) {\n newSteps.set(key, entry);\n }\n }\n\n return { steps: newSteps };\n };\n}\n\n/**\n * Create a migration that transforms step values.\n *\n * @param transforms - Map of key to transform function\n * @returns A migration function\n *\n * @example\n * ```typescript\n * const migrations = {\n * 1: createValueTransformMigration({\n * 'user:fetch': (entry) => ({\n * ...entry,\n * result: entry.result.ok\n * ? ok({ ...entry.result.value, newField: 'default' })\n * : entry.result,\n * }),\n * }),\n * };\n * ```\n */\nexport function createValueTransformMigration(\n transforms: Record<string, (entry: ResumeStateEntry) => ResumeStateEntry>\n): MigrationFn {\n return (state: ResumeState): ResumeState => {\n const newSteps = new Map<string, ResumeStateEntry>();\n\n for (const [key, entry] of state.steps) {\n const transform = transforms[key];\n newSteps.set(key, transform ? transform(entry) : entry);\n }\n\n return { steps: newSteps };\n };\n}\n\n/**\n * Compose multiple migrations into a single migration.\n *\n * @param migrations - Array of migration functions\n * @returns A single migration function that applies all migrations in order\n *\n * @example\n * ```typescript\n * const migrations = {\n * 1: composeMigrations([\n * createKeyRenameMigration({ 'old': 'new' }),\n * createKeyRemoveMigration(['deprecated']),\n * ]),\n * };\n * ```\n */\nexport function composeMigrations(migrations: MigrationFn[]): MigrationFn {\n return async (state: ResumeState): Promise<ResumeState> => {\n let currentState = state;\n for (const migration of migrations) {\n currentState = await migration(currentState);\n }\n return currentState;\n };\n}\n","/**\n * Autotel Integration\n *\n * First-class OpenTelemetry spans and metrics from the workflow event stream\n * using the autotel library.\n *\n * @example\n * ```typescript\n * import { createAutotelAdapter } from '@jagreehal/workflow/autotel';\n * import { init } from 'autotel';\n *\n * // Initialize autotel\n * init({ service: 'checkout-api' });\n *\n * // Create adapter\n * const otel = createAutotelAdapter({ serviceName: 'checkout' });\n *\n * // Use with workflow\n * const workflow = createWorkflow(deps, { onEvent: otel.handleEvent });\n *\n * // Automatic spans: workflow > step > retry attempts\n * // Automatic metrics: step_duration, retry_count, error_rate\n * ```\n */\n\nimport type { WorkflowEvent } from \"./core\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Configuration for the autotel adapter.\n */\nexport interface AutotelAdapterConfig {\n /**\n * Service name for spans (used as prefix).\n */\n serviceName?: string;\n\n /**\n * Whether to create spans for each step.\n * @default true\n */\n createStepSpans?: boolean;\n\n /**\n * Whether to record metrics for steps.\n * @default true\n */\n recordMetrics?: boolean;\n\n /**\n * Custom attributes to add to all spans.\n */\n defaultAttributes?: Record<string, string | number | boolean>;\n\n /**\n * Whether to record retry events as span events.\n * @default true\n */\n recordRetryEvents?: boolean;\n\n /**\n * Whether to mark workflow errors as span errors.\n * @default true\n */\n markErrorsOnSpan?: boolean;\n}\n\n/**\n * Span tracking info.\n */\ninterface SpanInfo {\n workflowId: string;\n stepId?: string;\n stepKey?: string;\n name?: string;\n startTime: number;\n}\n\n/**\n * Active spans tracking.\n */\ninterface ActiveSpans {\n workflows: Map<string, SpanInfo>;\n steps: Map<string, SpanInfo>;\n}\n\n/**\n * Metrics collected by the adapter.\n */\nexport interface AutotelMetrics {\n stepDurations: Array<{ \n name: string; \n durationMs: number; \n success: boolean;\n attributes?: Record<string, string | number | boolean>;\n }>;\n retryCount: number;\n errorCount: number;\n cacheHits: number;\n cacheMisses: number;\n /** Default attributes applied to all metrics */\n defaultAttributes: Record<string, string | number | boolean>;\n}\n\n/**\n * Autotel adapter interface.\n */\nexport interface AutotelAdapter {\n /**\n * Handle workflow events (pass to onEvent option).\n */\n handleEvent: (event: WorkflowEvent<unknown>) => void;\n\n /**\n * Get current active spans count (for debugging).\n */\n getActiveSpansCount: () => { workflows: number; steps: number };\n\n /**\n * Get collected metrics.\n */\n getMetrics: () => AutotelMetrics;\n\n /**\n * Reset adapter state.\n */\n reset: () => void;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Create an autotel adapter for workflow observability.\n *\n * This adapter translates workflow events into metrics and tracking data.\n * When autotel is installed and initialized, it will also create OpenTelemetry spans.\n *\n * @param config - Adapter configuration\n * @returns An AutotelAdapter instance\n *\n * @example\n * ```typescript\n * import { createAutotelAdapter } from '@jagreehal/workflow/autotel';\n *\n * const otel = createAutotelAdapter({ serviceName: 'checkout' });\n *\n * const workflow = createWorkflow(deps, {\n * onEvent: otel.handleEvent,\n * });\n * ```\n */\nexport function createAutotelAdapter(\n config: AutotelAdapterConfig = {}\n): AutotelAdapter {\n const {\n serviceName = \"workflow\",\n createStepSpans = true,\n recordMetrics = true,\n defaultAttributes = {},\n recordRetryEvents = true,\n markErrorsOnSpan = true,\n } = config;\n\n // Track active spans\n const activeSpans: ActiveSpans = {\n workflows: new Map(),\n steps: new Map(),\n };\n\n // Metrics counters\n const metrics: AutotelMetrics = {\n stepDurations: [],\n retryCount: 0,\n errorCount: 0,\n cacheHits: 0,\n cacheMisses: 0,\n defaultAttributes,\n };\n\n /**\n * Get span name for a step.\n */\n function getSpanName(name?: string, stepKey?: string): string {\n if (name) return `${serviceName}.${name}`;\n if (stepKey) return `${serviceName}.step.${stepKey}`;\n return `${serviceName}.step`;\n }\n\n /**\n * Handle workflow event.\n */\n function handleEvent(event: WorkflowEvent<unknown>): void {\n switch (event.type) {\n case \"workflow_start\":\n activeSpans.workflows.set(event.workflowId, {\n workflowId: event.workflowId,\n startTime: event.ts,\n });\n break;\n\n case \"workflow_success\":\n case \"workflow_error\":\n {\n const span = activeSpans.workflows.get(event.workflowId);\n if (span) {\n activeSpans.workflows.delete(event.workflowId);\n }\n\n if (event.type === \"workflow_error\" && markErrorsOnSpan) {\n metrics.errorCount++;\n }\n }\n break;\n\n case \"step_start\":\n if (createStepSpans) {\n const stepId = event.stepId;\n activeSpans.steps.set(stepId, {\n workflowId: event.workflowId,\n stepId,\n stepKey: event.stepKey,\n name: event.name,\n startTime: event.ts,\n });\n }\n break;\n\n case \"step_success\":\n case \"step_error\":\n if (createStepSpans) {\n const stepId = event.stepId;\n const span = activeSpans.steps.get(stepId);\n if (span) {\n activeSpans.steps.delete(stepId);\n\n if (recordMetrics) {\n metrics.stepDurations.push({\n name: getSpanName(span.name, span.stepKey),\n durationMs: event.durationMs,\n success: event.type === \"step_success\",\n // Include default attributes with each step metric\n attributes: Object.keys(defaultAttributes).length > 0 \n ? { ...defaultAttributes } \n : undefined,\n });\n }\n\n if (event.type === \"step_error\" && markErrorsOnSpan) {\n metrics.errorCount++;\n }\n }\n }\n break;\n\n case \"step_retry\":\n if (recordRetryEvents) {\n metrics.retryCount++;\n }\n break;\n\n case \"step_cache_hit\":\n metrics.cacheHits++;\n break;\n\n case \"step_cache_miss\":\n metrics.cacheMisses++;\n break;\n\n default:\n // Other events (timeout, etc.) - can be extended\n break;\n }\n }\n\n return {\n handleEvent,\n\n getActiveSpansCount() {\n return {\n workflows: activeSpans.workflows.size,\n steps: activeSpans.steps.size,\n };\n },\n\n getMetrics() {\n return { ...metrics, stepDurations: [...metrics.stepDurations] };\n },\n\n reset() {\n activeSpans.workflows.clear();\n activeSpans.steps.clear();\n metrics.stepDurations.length = 0;\n metrics.retryCount = 0;\n metrics.errorCount = 0;\n metrics.cacheHits = 0;\n metrics.cacheMisses = 0;\n // Note: defaultAttributes are preserved across resets\n },\n };\n}\n\n/**\n * Create an event handler that integrates with autotel's trace() function.\n *\n * This is a higher-level integration that creates actual OpenTelemetry spans\n * for each workflow and step when autotel is initialized.\n *\n * @param options - Configuration options\n * @returns An event handler function\n *\n * @example\n * ```typescript\n * import { createAutotelEventHandler } from '@jagreehal/workflow/autotel';\n * import { init } from 'autotel';\n *\n * init({ service: 'checkout-api' });\n *\n * const handler = createAutotelEventHandler({ serviceName: 'checkout' });\n *\n * const workflow = createWorkflow(deps, {\n * onEvent: handler,\n * });\n * ```\n */\nexport function createAutotelEventHandler(options?: {\n serviceName?: string;\n includeStepDetails?: boolean;\n}): (event: WorkflowEvent<unknown>) => void {\n const { serviceName = \"workflow\", includeStepDetails = true } = options ?? {};\n\n // This is a simple pass-through handler\n // The actual autotel integration happens via the trace() wrapper\n return (event: WorkflowEvent<unknown>) => {\n // Log events for debugging when AUTOTEL_DEBUG is set\n if (process.env.AUTOTEL_DEBUG === \"true\") {\n const prefix = `[${serviceName}]`;\n switch (event.type) {\n case \"workflow_start\":\n console.log(`${prefix} Workflow started: ${event.workflowId}`);\n break;\n case \"workflow_success\":\n console.log(`${prefix} Workflow success: ${event.workflowId} (${event.durationMs}ms)`);\n break;\n case \"workflow_error\":\n console.log(`${prefix} Workflow error: ${event.workflowId}`, event.error);\n break;\n case \"step_start\":\n if (includeStepDetails) {\n console.log(`${prefix} Step started: ${event.name ?? event.stepKey ?? event.stepId}`);\n }\n break;\n case \"step_success\":\n if (includeStepDetails) {\n console.log(`${prefix} Step success: ${event.name ?? event.stepKey ?? event.stepId} (${event.durationMs}ms)`);\n }\n break;\n case \"step_error\":\n if (includeStepDetails) {\n console.log(`${prefix} Step error: ${event.name ?? event.stepKey ?? event.stepId}`, event.error);\n }\n break;\n case \"step_retry\":\n console.log(`${prefix} Step retry: ${event.name ?? event.stepKey ?? event.stepId} (attempt ${event.attempt}/${event.maxAttempts})`);\n break;\n }\n }\n };\n}\n\n/**\n * Wrapper type for autotel trace function.\n * Use this when you want to explicitly type the autotel integration.\n */\nexport type AutotelTraceFn = <T>(\n name: string,\n fn: (ctx: { setAttribute: (key: string, value: unknown) => void }) => T | Promise<T>\n) => T | Promise<T>;\n\n/**\n * Create a workflow wrapper that adds autotel tracing.\n *\n * This creates a higher-order function that wraps workflow execution\n * in an autotel trace span.\n *\n * @param traceFn - The autotel trace function\n * @param options - Wrapper options\n * @returns A workflow wrapper function\n *\n * @example\n * ```typescript\n * import { trace } from 'autotel';\n * import { withAutotelTracing } from '@jagreehal/workflow/autotel';\n *\n * const traced = withAutotelTracing(trace, { serviceName: 'checkout' });\n *\n * const result = await traced('process-order', async () => {\n * return workflow(async (step) => {\n * // ... workflow logic\n * });\n * });\n * ```\n */\nexport function withAutotelTracing(\n traceFn: AutotelTraceFn,\n options?: { serviceName?: string }\n): <T>(\n name: string,\n fn: () => T | Promise<T>,\n attributes?: Record<string, string | number | boolean>\n) => Promise<T> {\n const { serviceName = \"workflow\" } = options ?? {};\n\n return async <T>(\n name: string,\n fn: () => T | Promise<T>,\n attributes?: Record<string, string | number | boolean>\n ): Promise<T> => {\n const spanName = `${serviceName}.${name}`;\n return traceFn(spanName, async (ctx) => {\n if (attributes) {\n for (const [key, value] of Object.entries(attributes)) {\n ctx.setAttribute(key, value);\n }\n }\n return fn();\n }) as Promise<T>;\n };\n}\n","/**\n * @jagreehal/workflow/webhook\n *\n * Webhook and event trigger adapters for exposing workflows as HTTP endpoints.\n * Framework-agnostic handlers that work with Express, Hono, Fastify, etc.\n */\n\nimport { type Result, type AsyncResult, ok, err, isOk } from \"./core\";\nimport { type Workflow, type WorkflowStrict, type UnexpectedError } from \"./workflow\";\n\n// =============================================================================\n// Request/Response Types\n// =============================================================================\n\n/**\n * Generic HTTP request representation.\n * Abstracts away framework-specific request objects.\n */\nexport interface WebhookRequest<Body = unknown> {\n /** HTTP method (GET, POST, PUT, DELETE, etc.) */\n method: string;\n /** Request path (e.g., \"/api/checkout\") */\n path: string;\n /** Request headers */\n headers: Record<string, string | string[] | undefined>;\n /** Parsed request body (JSON) */\n body: Body;\n /** Query parameters */\n query: Record<string, string | string[] | undefined>;\n /** Path parameters (e.g., { id: \"123\" }) */\n params: Record<string, string>;\n /** Raw request object from framework (for advanced use cases) */\n raw?: unknown;\n}\n\n/**\n * Generic HTTP response representation.\n */\nexport interface WebhookResponse<T = unknown> {\n /** HTTP status code */\n status: number;\n /** Response headers */\n headers?: Record<string, string>;\n /** Response body (will be JSON serialized) */\n body: T;\n}\n\n/**\n * Error response body structure.\n */\nexport interface ErrorResponseBody {\n error: {\n type: string;\n message?: string;\n details?: unknown;\n };\n}\n\n// =============================================================================\n// Validation Types\n// =============================================================================\n\n/**\n * Input validation result.\n */\nexport type ValidationResult<T, E = string> = Result<T, E>;\n\n/**\n * Standard validation error type.\n */\nexport interface ValidationError {\n type: \"VALIDATION_ERROR\";\n message: string;\n field?: string;\n details?: unknown;\n}\n\n/**\n * Type guard for ValidationError.\n */\nexport function isValidationError(e: unknown): e is ValidationError {\n return (\n typeof e === \"object\" &&\n e !== null &&\n (e as ValidationError).type === \"VALIDATION_ERROR\"\n );\n}\n\n// =============================================================================\n// Handler Configuration\n// =============================================================================\n\n/**\n * Configuration for creating a webhook handler.\n *\n * @template TInput - The validated input type\n * @template TOutput - The workflow output type\n * @template TError - The workflow error type\n * @template TBody - The raw request body type\n */\nexport interface WebhookHandlerConfig<TInput, TOutput, TError, TBody = unknown> {\n /**\n * Validate and transform the incoming request.\n * Return ok(input) to proceed, or err(validationError) to reject.\n *\n * @param req - The incoming request\n * @returns Validated input or validation error\n */\n validateInput: (\n req: WebhookRequest<TBody>\n ) => ValidationResult<TInput, ValidationError> | Promise<ValidationResult<TInput, ValidationError>>;\n\n /**\n * Map workflow result to HTTP response.\n * Called for both success and error cases.\n *\n * @param result - The workflow result\n * @param req - The original request (for context)\n * @returns HTTP response\n */\n mapResult: (\n result: Result<TOutput, TError | UnexpectedError>,\n req: WebhookRequest<TBody>\n ) => WebhookResponse;\n\n /**\n * Optional: Map validation errors to HTTP response.\n * Defaults to 400 Bad Request with error details.\n *\n * @param error - The validation error\n * @param req - The original request\n * @returns HTTP response\n */\n mapValidationError?: (\n error: ValidationError,\n req: WebhookRequest<TBody>\n ) => WebhookResponse<ErrorResponseBody>;\n\n /**\n * Optional: Handle unexpected errors during request processing.\n * Defaults to 500 Internal Server Error.\n *\n * @param error - The unexpected error\n * @param req - The original request\n * @returns HTTP response\n */\n mapUnexpectedError?: (\n error: unknown,\n req: WebhookRequest<TBody>\n ) => WebhookResponse<ErrorResponseBody>;\n\n /**\n * Optional: Request middleware.\n * Transform or enrich the request before validation.\n *\n * @param req - The incoming request\n * @returns Transformed request\n */\n beforeValidation?: (\n req: WebhookRequest<TBody>\n ) => WebhookRequest<TBody> | Promise<WebhookRequest<TBody>>;\n\n /**\n * Optional: Response middleware.\n * Transform the response before sending.\n *\n * @param response - The response to send\n * @param req - The original request\n * @returns Transformed response\n */\n afterResponse?: (\n response: WebhookResponse,\n req: WebhookRequest<TBody>\n ) => WebhookResponse | Promise<WebhookResponse>;\n}\n\n/**\n * A webhook handler function that processes requests.\n */\nexport type WebhookHandler<TBody = unknown> = (\n req: WebhookRequest<TBody>\n) => Promise<WebhookResponse>;\n\n// =============================================================================\n// Default Mappers\n// =============================================================================\n\n/**\n * Default validation error mapper.\n * Returns 400 Bad Request with error details.\n */\nexport function defaultValidationErrorMapper(\n error: ValidationError\n): WebhookResponse<ErrorResponseBody> {\n return {\n status: 400,\n body: {\n error: {\n type: error.type,\n message: error.message,\n details: error.field ? { field: error.field } : error.details,\n },\n },\n };\n}\n\n/**\n * Default unexpected error mapper.\n * Returns 500 Internal Server Error.\n */\nexport function defaultUnexpectedErrorMapper(\n _error: unknown\n): WebhookResponse<ErrorResponseBody> {\n return {\n status: 500,\n body: {\n error: {\n type: \"INTERNAL_ERROR\",\n message: \"An unexpected error occurred\",\n },\n },\n };\n}\n\n// =============================================================================\n// Workflow Handler Factory\n// =============================================================================\n\n/**\n * Create a webhook handler for a workflow.\n *\n * This factory creates an HTTP handler function that:\n * 1. Validates the incoming request\n * 2. Executes the workflow with the validated input\n * 3. Maps the result to an HTTP response\n *\n * The handler is framework-agnostic and returns a standard response object.\n * Use framework adapters (createExpressHandler, createHonoHandler, etc.) to\n * integrate with specific frameworks.\n *\n * @template TInput - The validated input type passed to the workflow\n * @template TOutput - The workflow output type\n * @template TError - The workflow error type\n * @template TBody - The raw request body type\n * @template TDeps - The workflow dependencies type\n *\n * @param workflow - The workflow function to execute\n * @param workflowFn - The workflow body function (step, deps, input) => output\n * @param config - Handler configuration\n * @returns A webhook handler function\n *\n * @example\n * ```typescript\n * const checkoutWorkflow = createWorkflow({ chargeCard, sendEmail });\n *\n * const handler = createWebhookHandler(\n * checkoutWorkflow,\n * async (step, deps, input: CheckoutInput) => {\n * const charge = await step(() => deps.chargeCard(input.amount));\n * await step(() => deps.sendEmail(input.email, charge.receiptUrl));\n * return { chargeId: charge.id };\n * },\n * {\n * validateInput: (req) => {\n * const { amount, email } = req.body;\n * if (!amount || !email) {\n * return err({ type: 'VALIDATION_ERROR', message: 'Missing required fields' });\n * }\n * return ok({ amount, email });\n * },\n * mapResult: (result) => {\n * if (result.ok) {\n * return { status: 200, body: result.value };\n * }\n * if (result.error === 'CARD_DECLINED') {\n * return { status: 402, body: { error: { type: 'CARD_DECLINED' } } };\n * }\n * return { status: 500, body: { error: { type: 'UNKNOWN' } } };\n * },\n * }\n * );\n *\n * // Use with Express\n * app.post('/checkout', async (req, res) => {\n * const response = await handler(toWebhookRequest(req));\n * res.status(response.status).json(response.body);\n * });\n * ```\n */\nexport function createWebhookHandler<\n TInput,\n TOutput,\n TError,\n TBody = unknown,\n TDeps = unknown\n>(\n workflow: Workflow<TError, TDeps> | WorkflowStrict<TError, unknown, TDeps>,\n workflowFn: (\n step: Parameters<Parameters<Workflow<TError, TDeps>>[1]>[0],\n deps: TDeps,\n input: TInput\n ) => TOutput | Promise<TOutput>,\n config: WebhookHandlerConfig<TInput, TOutput, TError, TBody>\n): WebhookHandler<TBody> {\n const {\n validateInput,\n mapResult,\n mapValidationError = defaultValidationErrorMapper,\n mapUnexpectedError = defaultUnexpectedErrorMapper,\n beforeValidation,\n afterResponse,\n } = config;\n\n return async (req: WebhookRequest<TBody>): Promise<WebhookResponse> => {\n try {\n // Apply request middleware if provided\n const processedReq = beforeValidation ? await beforeValidation(req) : req;\n\n // Validate input\n const validationResult = await validateInput(processedReq);\n\n if (!isOk(validationResult)) {\n const response = mapValidationError(validationResult.error, processedReq);\n return afterResponse ? await afterResponse(response, processedReq) : response;\n }\n\n // Execute workflow with validated input\n const workflowResult = await (workflow as Workflow<TError, TDeps>)(\n validationResult.value,\n (step, deps) => workflowFn(step, deps, validationResult.value)\n );\n\n // Map result to response\n const response = mapResult(workflowResult, processedReq);\n\n // Apply response middleware if provided\n return afterResponse ? await afterResponse(response, processedReq) : response;\n } catch (error) {\n // Handle unexpected errors during request processing\n const response = mapUnexpectedError(error, req);\n return afterResponse ? await afterResponse(response, req) : response;\n }\n };\n}\n\n// =============================================================================\n// Simple Handler (No Workflow)\n// =============================================================================\n\n/**\n * Configuration for a simple webhook handler without workflow.\n */\nexport interface SimpleHandlerConfig<TInput, TOutput, TError, TBody = unknown> {\n validateInput: (\n req: WebhookRequest<TBody>\n ) => ValidationResult<TInput, ValidationError> | Promise<ValidationResult<TInput, ValidationError>>;\n\n handler: (input: TInput, req: WebhookRequest<TBody>) => AsyncResult<TOutput, TError>;\n\n mapResult: (\n result: Result<TOutput, TError>,\n req: WebhookRequest<TBody>\n ) => WebhookResponse;\n\n mapValidationError?: (\n error: ValidationError,\n req: WebhookRequest<TBody>\n ) => WebhookResponse<ErrorResponseBody>;\n\n mapUnexpectedError?: (\n error: unknown,\n req: WebhookRequest<TBody>\n ) => WebhookResponse<ErrorResponseBody>;\n}\n\n/**\n * Create a simple webhook handler without workflow orchestration.\n * Useful for simple endpoints that don't need step-based error handling.\n *\n * @example\n * ```typescript\n * const handler = createSimpleHandler({\n * validateInput: (req) => {\n * const { id } = req.params;\n * if (!id) return err({ type: 'VALIDATION_ERROR', message: 'Missing id' });\n * return ok({ id });\n * },\n * handler: async ({ id }) => {\n * const user = await db.findUser(id);\n * return user ? ok(user) : err('NOT_FOUND' as const);\n * },\n * mapResult: (result) => {\n * if (result.ok) return { status: 200, body: result.value };\n * return { status: 404, body: { error: { type: 'NOT_FOUND' } } };\n * },\n * });\n * ```\n */\nexport function createSimpleHandler<TInput, TOutput, TError, TBody = unknown>(\n config: SimpleHandlerConfig<TInput, TOutput, TError, TBody>\n): WebhookHandler<TBody> {\n const {\n validateInput,\n handler,\n mapResult,\n mapValidationError = defaultValidationErrorMapper,\n mapUnexpectedError = defaultUnexpectedErrorMapper,\n } = config;\n\n return async (req: WebhookRequest<TBody>): Promise<WebhookResponse> => {\n try {\n const validationResult = await validateInput(req);\n\n if (!isOk(validationResult)) {\n return mapValidationError(validationResult.error, req);\n }\n\n const result = await handler(validationResult.value, req);\n return mapResult(result, req);\n } catch (error) {\n return mapUnexpectedError(error, req);\n }\n };\n}\n\n// =============================================================================\n// Result Mappers (Helpers)\n// =============================================================================\n\n/**\n * Standard error mapping configuration.\n */\nexport interface ErrorMapping<TError> {\n /** The error value to match */\n error: TError;\n /** HTTP status code for this error */\n status: number;\n /** Optional custom message */\n message?: string;\n}\n\n/**\n * Create a result mapper from error mappings.\n * Provides a declarative way to map workflow errors to HTTP responses.\n *\n * @param mappings - Array of error mappings\n * @param defaultStatus - Default status for unmapped errors (default: 500)\n * @returns A mapResult function for use in handler config\n *\n * @example\n * ```typescript\n * const mapResult = createResultMapper<CheckoutOutput, CheckoutError>([\n * { error: 'NOT_FOUND', status: 404, message: 'Resource not found' },\n * { error: 'CARD_DECLINED', status: 402, message: 'Payment failed' },\n * { error: 'RATE_LIMITED', status: 429, message: 'Too many requests' },\n * ]);\n *\n * const handler = createWebhookHandler(workflow, workflowFn, {\n * validateInput,\n * mapResult,\n * });\n * ```\n */\nexport function createResultMapper<TOutput, TError>(\n mappings: ErrorMapping<TError>[],\n options: {\n defaultStatus?: number;\n successStatus?: number;\n } = {}\n): (result: Result<TOutput, TError | UnexpectedError>) => WebhookResponse {\n const { defaultStatus = 500, successStatus = 200 } = options;\n\n const errorMap = new Map<TError, ErrorMapping<TError>>();\n for (const mapping of mappings) {\n errorMap.set(mapping.error, mapping);\n }\n\n return (result: Result<TOutput, TError | UnexpectedError>): WebhookResponse => {\n if (result.ok) {\n return {\n status: successStatus,\n body: result.value,\n };\n }\n\n // Check if it's an UnexpectedError\n if (\n typeof result.error === \"object\" &&\n result.error !== null &&\n (result.error as { type?: string }).type === \"UNEXPECTED_ERROR\"\n ) {\n return {\n status: 500,\n body: {\n error: {\n type: \"INTERNAL_ERROR\",\n message: \"An unexpected error occurred\",\n },\n },\n };\n }\n\n // Check error mappings\n const mapping = errorMap.get(result.error as TError);\n if (mapping) {\n return {\n status: mapping.status,\n body: {\n error: {\n type: String(mapping.error),\n message: mapping.message,\n },\n },\n };\n }\n\n // Default error response\n return {\n status: defaultStatus,\n body: {\n error: {\n type: String(result.error),\n },\n },\n };\n };\n}\n\n// =============================================================================\n// Framework Adapters\n// =============================================================================\n\n/**\n * Express-style request object (minimal interface).\n */\nexport interface ExpressLikeRequest {\n method: string;\n path: string;\n headers: Record<string, string | string[] | undefined>;\n body: unknown;\n query: Record<string, string | string[] | undefined>;\n params: Record<string, string>;\n}\n\n/**\n * Express-style response object (minimal interface).\n */\nexport interface ExpressLikeResponse {\n status(code: number): ExpressLikeResponse;\n set(headers: Record<string, string>): ExpressLikeResponse;\n json(body: unknown): void;\n}\n\n/**\n * Convert an Express-like request to WebhookRequest.\n *\n * @param req - Express-like request object\n * @returns WebhookRequest\n *\n * @example\n * ```typescript\n * app.post('/checkout', async (req, res) => {\n * const webhookReq = toWebhookRequest(req);\n * const response = await handler(webhookReq);\n * res.status(response.status).json(response.body);\n * });\n * ```\n */\nexport function toWebhookRequest<TBody = unknown>(\n req: ExpressLikeRequest\n): WebhookRequest<TBody> {\n return {\n method: req.method,\n path: req.path,\n headers: req.headers,\n body: req.body as TBody,\n query: req.query,\n params: req.params,\n raw: req,\n };\n}\n\n/**\n * Send a WebhookResponse using an Express-like response object.\n *\n * @param res - Express-like response object\n * @param response - WebhookResponse to send\n *\n * @example\n * ```typescript\n * app.post('/checkout', async (req, res) => {\n * const response = await handler(toWebhookRequest(req));\n * sendWebhookResponse(res, response);\n * });\n * ```\n */\nexport function sendWebhookResponse(\n res: ExpressLikeResponse,\n response: WebhookResponse\n): void {\n if (response.headers) {\n res.set(response.headers);\n }\n res.status(response.status).json(response.body);\n}\n\n/**\n * Create an Express-compatible middleware from a webhook handler.\n *\n * @param handler - Webhook handler function\n * @returns Express middleware function\n *\n * @example\n * ```typescript\n * const handler = createWebhookHandler(workflow, workflowFn, config);\n * const middleware = createExpressHandler(handler);\n * app.post('/checkout', middleware);\n * ```\n */\nexport function createExpressHandler<TBody = unknown>(\n handler: WebhookHandler<TBody>\n): (req: ExpressLikeRequest, res: ExpressLikeResponse) => Promise<void> {\n return async (req: ExpressLikeRequest, res: ExpressLikeResponse) => {\n const webhookReq = toWebhookRequest<TBody>(req);\n const response = await handler(webhookReq);\n sendWebhookResponse(res, response);\n };\n}\n\n// =============================================================================\n// Validation Helpers\n// =============================================================================\n\n/**\n * Create a validation error.\n *\n * @param message - Error message\n * @param field - Optional field name\n * @param details - Optional additional details\n * @returns ValidationError\n */\nexport function validationError(\n message: string,\n field?: string,\n details?: unknown\n): ValidationError {\n return {\n type: \"VALIDATION_ERROR\",\n message,\n field,\n details,\n };\n}\n\n/**\n * Create a required field validator.\n *\n * @param fields - Field names to validate\n * @returns Validation function\n *\n * @example\n * ```typescript\n * const validateRequired = requireFields(['email', 'password']);\n *\n * const validateInput = (req) => {\n * const result = validateRequired(req.body);\n * if (!result.ok) return result;\n * return ok(req.body as LoginInput);\n * };\n * ```\n */\nexport function requireFields(\n fields: string[]\n): (body: Record<string, unknown>) => ValidationResult<void, ValidationError> {\n return (body: Record<string, unknown>) => {\n for (const field of fields) {\n if (body[field] === undefined || body[field] === null || body[field] === \"\") {\n return err(validationError(`Missing required field: ${field}`, field));\n }\n }\n return ok(undefined);\n };\n}\n\n/**\n * Compose multiple validators into a single validator.\n *\n * @param validators - Validators to compose\n * @returns Combined validator function\n *\n * @example\n * ```typescript\n * const validate = composeValidators(\n * requireFields(['email', 'password']),\n * validateEmailFormat,\n * validatePasswordStrength\n * );\n * ```\n */\nexport function composeValidators<T>(\n ...validators: Array<(input: T) => ValidationResult<void, ValidationError>>\n): (input: T) => ValidationResult<void, ValidationError> {\n return (input: T) => {\n for (const validator of validators) {\n const result = validator(input);\n if (!result.ok) return result;\n }\n return ok(undefined);\n };\n}\n\n// =============================================================================\n// Event Trigger Types (for message queues, etc.)\n// =============================================================================\n\n/**\n * Generic event message for queue-based triggers.\n */\nexport interface EventMessage<T = unknown> {\n /** Unique message ID */\n id: string;\n /** Event type/name */\n type: string;\n /** Event payload */\n payload: T;\n /** Event metadata */\n metadata?: {\n timestamp?: number;\n source?: string;\n correlationId?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Result of processing an event.\n */\nexport interface EventProcessingResult {\n /** Whether the event was processed successfully */\n success: boolean;\n /** Should the message be acknowledged (removed from queue)? */\n ack: boolean;\n /** Optional error details */\n error?: {\n type: string;\n message?: string;\n retryable?: boolean;\n };\n}\n\n/**\n * Configuration for event trigger handlers.\n */\nexport interface EventTriggerConfig<TPayload, TOutput, TError> {\n /** Validate the event payload */\n validatePayload: (\n event: EventMessage<TPayload>\n ) => ValidationResult<TPayload, ValidationError>;\n\n /** Map workflow result to processing result */\n mapResult: (\n result: Result<TOutput, TError | UnexpectedError>,\n event: EventMessage<TPayload>\n ) => EventProcessingResult;\n\n /** Optional: Determine if error is retryable */\n isRetryable?: (error: TError | UnexpectedError) => boolean;\n}\n\n/**\n * Event handler function type.\n */\nexport type EventHandler<TPayload = unknown> = (\n event: EventMessage<TPayload>\n) => Promise<EventProcessingResult>;\n\n/**\n * Create an event handler for queue-based triggers.\n *\n * @example\n * ```typescript\n * const handler = createEventHandler(\n * checkoutWorkflow,\n * async (step, deps, payload: CheckoutPayload) => {\n * const charge = await step(() => deps.chargeCard(payload.amount));\n * return { chargeId: charge.id };\n * },\n * {\n * validatePayload: (event) => {\n * if (!event.payload.amount) {\n * return err({ type: 'VALIDATION_ERROR', message: 'Missing amount' });\n * }\n * return ok(event.payload);\n * },\n * mapResult: (result) => ({\n * success: result.ok,\n * ack: result.ok || !isRetryableError(result.error),\n * error: result.ok ? undefined : { type: String(result.error) },\n * }),\n * }\n * );\n *\n * // Use with SQS, RabbitMQ, etc.\n * queue.consume(async (message) => {\n * const result = await handler(message);\n * if (result.ack) await message.ack();\n * else await message.nack();\n * });\n * ```\n */\nexport function createEventHandler<\n TPayload,\n TOutput,\n TError,\n TDeps = unknown\n>(\n workflow: Workflow<TError, TDeps> | WorkflowStrict<TError, unknown, TDeps>,\n workflowFn: (\n step: Parameters<Parameters<Workflow<TError, TDeps>>[1]>[0],\n deps: TDeps,\n payload: TPayload\n ) => TOutput | Promise<TOutput>,\n config: EventTriggerConfig<TPayload, TOutput, TError>\n): EventHandler<TPayload> {\n const { validatePayload, mapResult } = config;\n\n return async (event: EventMessage<TPayload>): Promise<EventProcessingResult> => {\n try {\n const validationResult = validatePayload(event);\n\n if (!isOk(validationResult)) {\n return {\n success: false,\n ack: true, // Don't retry validation errors\n error: {\n type: validationResult.error.type,\n message: validationResult.error.message,\n retryable: false,\n },\n };\n }\n\n const workflowResult = await (workflow as Workflow<TError, TDeps>)(\n validationResult.value,\n (step, deps) => workflowFn(step, deps, validationResult.value)\n );\n\n return mapResult(workflowResult, event);\n } catch (error) {\n return {\n success: false,\n ack: false, // Retry unexpected errors\n error: {\n type: \"UNEXPECTED_ERROR\",\n message: error instanceof Error ? error.message : String(error),\n retryable: true,\n },\n };\n }\n };\n}\n","/**\n * @jagreehal/workflow/policies\n *\n * Policy-Driven Step Middleware - Reusable bundles of StepOptions\n * that can be composed and applied per-workflow or per-step.\n */\n\nimport type { StepOptions, RetryOptions, TimeoutOptions } from \"./core\";\n\n// =============================================================================\n// Policy Types\n// =============================================================================\n\n/**\n * A policy is a partial StepOptions that can be merged with other policies.\n */\nexport type Policy = Partial<StepOptions>;\n\n/**\n * A policy factory that creates policies based on context.\n */\nexport type PolicyFactory<T = void> = T extends void\n ? () => Policy\n : (context: T) => Policy;\n\n/**\n * Named policy with metadata.\n */\nexport interface NamedPolicy {\n name: string;\n policy: Policy;\n description?: string;\n}\n\n// =============================================================================\n// Policy Composition\n// =============================================================================\n\n/**\n * Merge multiple policies into a single StepOptions object.\n * Later policies override earlier ones for conflicting properties.\n * Retry and timeout options are deep-merged.\n *\n * @param policies - Policies to merge (in order of precedence)\n * @returns Merged StepOptions\n *\n * @example\n * ```typescript\n * const merged = mergePolicies(\n * timeoutPolicies.api, // timeout: 5000ms\n * retryPolicies.transient, // retry: 3 attempts\n * { name: 'fetch-user' } // name override\n * );\n * ```\n */\nexport function mergePolicies(...policies: Policy[]): StepOptions {\n const result: StepOptions = {};\n\n for (const policy of policies) {\n if (policy.name !== undefined) result.name = policy.name;\n if (policy.key !== undefined) result.key = policy.key;\n\n // Deep merge retry options\n if (policy.retry !== undefined) {\n result.retry = result.retry\n ? { ...result.retry, ...policy.retry }\n : { ...policy.retry };\n }\n\n // Deep merge timeout options\n if (policy.timeout !== undefined) {\n result.timeout = result.timeout\n ? { ...result.timeout, ...policy.timeout }\n : { ...policy.timeout };\n }\n }\n\n return result;\n}\n\n/**\n * Create a policy applier that merges base policies with step-specific options.\n *\n * @param basePolicies - Base policies to apply to all steps\n * @returns A function that applies policies to step options\n *\n * @example\n * ```typescript\n * const applyPolicy = createPolicyApplier(\n * timeoutPolicies.api,\n * retryPolicies.transient\n * );\n *\n * // In workflow\n * const user = await step(\n * () => fetchUser(id),\n * applyPolicy({ name: 'fetch-user', key: 'user:' + id })\n * );\n * ```\n */\nexport function createPolicyApplier(\n ...basePolicies: Policy[]\n): (stepOptions?: StepOptions | string) => StepOptions {\n const basePolicy = mergePolicies(...basePolicies);\n\n return (stepOptions?: StepOptions | string): StepOptions => {\n const opts = typeof stepOptions === \"string\" ? { name: stepOptions } : (stepOptions ?? {});\n return mergePolicies(basePolicy, opts);\n };\n}\n\n/**\n * Create a named policy bundle for reuse across workflows.\n *\n * @param name - Policy bundle name\n * @param policies - Policies to include in the bundle\n * @returns Named policy object\n */\nexport function createPolicyBundle(\n name: string,\n ...policies: Policy[]\n): NamedPolicy {\n return {\n name,\n policy: mergePolicies(...policies),\n };\n}\n\n// =============================================================================\n// Retry Policies\n// =============================================================================\n\n/**\n * Create a retry policy with the given options.\n */\nexport function retryPolicy(options: RetryOptions): Policy {\n return { retry: options };\n}\n\n/**\n * Pre-built retry policies for common scenarios.\n */\nexport const retryPolicies = {\n /**\n * No retry - fail immediately on error.\n */\n none: retryPolicy({ attempts: 1 }),\n\n /**\n * Quick retry for transient errors (3 attempts, fast backoff).\n */\n transient: retryPolicy({\n attempts: 3,\n backoff: \"exponential\",\n initialDelay: 100,\n maxDelay: 1000,\n jitter: true,\n }),\n\n /**\n * Standard retry for API calls (3 attempts, moderate backoff).\n */\n standard: retryPolicy({\n attempts: 3,\n backoff: \"exponential\",\n initialDelay: 200,\n maxDelay: 5000,\n jitter: true,\n }),\n\n /**\n * Aggressive retry for critical operations (5 attempts, longer backoff).\n */\n aggressive: retryPolicy({\n attempts: 5,\n backoff: \"exponential\",\n initialDelay: 500,\n maxDelay: 30000,\n jitter: true,\n }),\n\n /**\n * Fixed interval retry (useful for polling).\n */\n fixed: (attempts: number, delayMs: number): Policy =>\n retryPolicy({\n attempts,\n backoff: \"fixed\",\n initialDelay: delayMs,\n jitter: false,\n }),\n\n /**\n * Linear backoff retry.\n */\n linear: (attempts: number, initialDelay: number): Policy =>\n retryPolicy({\n attempts,\n backoff: \"linear\",\n initialDelay,\n jitter: true,\n }),\n\n /**\n * Custom retry policy builder.\n */\n custom: (options: Partial<RetryOptions> & { attempts: number }): Policy =>\n retryPolicy({\n backoff: \"exponential\",\n initialDelay: 100,\n maxDelay: 30000,\n jitter: true,\n ...options,\n }),\n} as const;\n\n// =============================================================================\n// Timeout Policies\n// =============================================================================\n\n/**\n * Create a timeout policy with the given options.\n */\nexport function timeoutPolicy(options: TimeoutOptions): Policy {\n return { timeout: options };\n}\n\n/**\n * Pre-built timeout policies for common scenarios.\n */\nexport const timeoutPolicies = {\n /**\n * No timeout.\n */\n none: {} as Policy,\n\n /**\n * Fast timeout for quick operations (1 second).\n */\n fast: timeoutPolicy({ ms: 1000 }),\n\n /**\n * Standard API timeout (5 seconds).\n */\n api: timeoutPolicy({ ms: 5000 }),\n\n /**\n * Extended timeout for slower operations (30 seconds).\n */\n extended: timeoutPolicy({ ms: 30000 }),\n\n /**\n * Long timeout for batch operations (2 minutes).\n */\n long: timeoutPolicy({ ms: 120000 }),\n\n /**\n * Custom timeout in milliseconds.\n */\n ms: (ms: number): Policy => timeoutPolicy({ ms }),\n\n /**\n * Custom timeout in seconds.\n */\n seconds: (seconds: number): Policy => timeoutPolicy({ ms: seconds * 1000 }),\n\n /**\n * Timeout with custom error.\n */\n withError: <E>(ms: number, error: E): Policy =>\n timeoutPolicy({ ms, error }),\n\n /**\n * Timeout with AbortSignal support.\n */\n withSignal: (ms: number): Policy =>\n timeoutPolicy({ ms, signal: true }),\n} as const;\n\n// =============================================================================\n// Combined Policies\n// =============================================================================\n\n/**\n * Pre-built combined policies for common service patterns.\n */\nexport const servicePolicies = {\n /**\n * Policy for external HTTP APIs.\n * - 5 second timeout\n * - 3 retries with exponential backoff\n */\n httpApi: mergePolicies(\n timeoutPolicies.api,\n retryPolicies.standard\n ),\n\n /**\n * Policy for database operations.\n * - 30 second timeout\n * - 2 retries for transient errors\n */\n database: mergePolicies(\n timeoutPolicies.extended,\n retryPolicy({\n attempts: 2,\n backoff: \"exponential\",\n initialDelay: 100,\n maxDelay: 2000,\n jitter: true,\n })\n ),\n\n /**\n * Policy for cache operations.\n * - 1 second timeout\n * - No retry (cache misses are not errors)\n */\n cache: mergePolicies(\n timeoutPolicies.fast,\n retryPolicies.none\n ),\n\n /**\n * Policy for message queue operations.\n * - 30 second timeout\n * - 5 retries with longer backoff\n */\n messageQueue: mergePolicies(\n timeoutPolicies.extended,\n retryPolicies.aggressive\n ),\n\n /**\n * Policy for file operations.\n * - 2 minute timeout\n * - 3 retries\n */\n fileSystem: mergePolicies(\n timeoutPolicies.long,\n retryPolicies.standard\n ),\n\n /**\n * Policy for third-party services with rate limits.\n * - 10 second timeout\n * - 5 retries with linear backoff\n */\n rateLimited: mergePolicies(\n timeoutPolicy({ ms: 10000 }),\n retryPolicy({\n attempts: 5,\n backoff: \"linear\",\n initialDelay: 1000,\n maxDelay: 10000,\n jitter: true,\n })\n ),\n} as const;\n\n// =============================================================================\n// Policy Decorators\n// =============================================================================\n\n/**\n * Options for withPolicies workflow wrapper.\n */\nexport interface WithPoliciesOptions {\n /**\n * Base policies applied to all steps.\n */\n policies: Policy[];\n\n /**\n * Step-specific policy overrides by name or key pattern.\n */\n overrides?: Record<string, Policy>;\n}\n\n/**\n * Create step options with policies applied.\n * This is a helper for applying policies inline.\n *\n * @param policies - Policies to apply\n * @param stepOptions - Step-specific options\n * @returns Merged StepOptions\n *\n * @example\n * ```typescript\n * const user = await step(\n * () => fetchUser(id),\n * withPolicy(servicePolicies.httpApi, { name: 'fetch-user' })\n * );\n * ```\n */\nexport function withPolicy(\n policy: Policy,\n stepOptions?: StepOptions | string\n): StepOptions {\n const opts = typeof stepOptions === \"string\" ? { name: stepOptions } : (stepOptions ?? {});\n return mergePolicies(policy, opts);\n}\n\n/**\n * Create step options with multiple policies applied.\n *\n * @param policies - Policies to apply (in order)\n * @param stepOptions - Step-specific options\n * @returns Merged StepOptions\n *\n * @example\n * ```typescript\n * const user = await step(\n * () => fetchUser(id),\n * withPolicies([timeoutPolicies.api, retryPolicies.standard], { name: 'fetch-user' })\n * );\n * ```\n */\nexport function withPolicies(\n policies: Policy[],\n stepOptions?: StepOptions | string\n): StepOptions {\n const opts = typeof stepOptions === \"string\" ? { name: stepOptions } : (stepOptions ?? {});\n return mergePolicies(...policies, opts);\n}\n\n// =============================================================================\n// Conditional Policies\n// =============================================================================\n\n/**\n * Create a policy that applies conditionally.\n *\n * @param condition - Condition to check\n * @param policy - Policy to apply if condition is true\n * @param elsePolicy - Policy to apply if condition is false (optional)\n * @returns The selected policy\n *\n * @example\n * ```typescript\n * const policy = conditionalPolicy(\n * isProduction,\n * servicePolicies.httpApi, // Use in production\n * retryPolicies.none // Skip in development\n * );\n * ```\n */\nexport function conditionalPolicy(\n condition: boolean,\n policy: Policy,\n elsePolicy: Policy = {}\n): Policy {\n return condition ? policy : elsePolicy;\n}\n\n/**\n * Create a policy based on environment.\n *\n * @param envPolicies - Map of environment names to policies\n * @param currentEnv - Current environment (defaults to NODE_ENV)\n * @param defaultPolicy - Default policy if environment not found\n * @returns The selected policy\n *\n * @example\n * ```typescript\n * const policy = envPolicy({\n * production: servicePolicies.httpApi,\n * development: retryPolicies.none,\n * test: retryPolicies.none,\n * });\n * ```\n */\nexport function envPolicy(\n envPolicies: Record<string, Policy>,\n currentEnv: string = process.env.NODE_ENV ?? \"development\",\n defaultPolicy: Policy = {}\n): Policy {\n return envPolicies[currentEnv] ?? defaultPolicy;\n}\n\n// =============================================================================\n// Policy Registry\n// =============================================================================\n\n/**\n * A registry for managing named policies.\n */\nexport interface PolicyRegistry {\n /**\n * Register a named policy.\n */\n register(name: string, policy: Policy): void;\n\n /**\n * Get a policy by name.\n */\n get(name: string): Policy | undefined;\n\n /**\n * Check if a policy exists.\n */\n has(name: string): boolean;\n\n /**\n * Get all registered policy names.\n */\n names(): string[];\n\n /**\n * Create step options using a registered policy.\n */\n apply(policyName: string, stepOptions?: StepOptions | string): StepOptions;\n}\n\n/**\n * Create a policy registry for managing named policies.\n *\n * @returns PolicyRegistry instance\n *\n * @example\n * ```typescript\n * const registry = createPolicyRegistry();\n *\n * // Register policies\n * registry.register('api', servicePolicies.httpApi);\n * registry.register('db', servicePolicies.database);\n *\n * // Use in workflow\n * const user = await step(\n * () => fetchUser(id),\n * registry.apply('api', { name: 'fetch-user' })\n * );\n * ```\n */\nexport function createPolicyRegistry(): PolicyRegistry {\n const policies = new Map<string, Policy>();\n\n return {\n register(name: string, policy: Policy): void {\n policies.set(name, policy);\n },\n\n get(name: string): Policy | undefined {\n return policies.get(name);\n },\n\n has(name: string): boolean {\n return policies.has(name);\n },\n\n names(): string[] {\n return Array.from(policies.keys());\n },\n\n apply(policyName: string, stepOptions?: StepOptions | string): StepOptions {\n const policy = policies.get(policyName);\n if (!policy) {\n throw new Error(`Policy not found: ${policyName}`);\n }\n return withPolicy(policy, stepOptions);\n },\n };\n}\n\n// =============================================================================\n// Step Options Builder (Fluent API)\n// =============================================================================\n\n/**\n * Fluent builder for constructing step options.\n */\nexport interface StepOptionsBuilder {\n /**\n * Set step name.\n */\n name(name: string): StepOptionsBuilder;\n\n /**\n * Set step key for caching.\n */\n key(key: string): StepOptionsBuilder;\n\n /**\n * Apply a policy.\n */\n policy(policy: Policy): StepOptionsBuilder;\n\n /**\n * Set timeout in milliseconds.\n */\n timeout(ms: number): StepOptionsBuilder;\n\n /**\n * Set retry options.\n */\n retry(options: RetryOptions): StepOptionsBuilder;\n\n /**\n * Set retry attempts (with default exponential backoff).\n */\n retries(attempts: number): StepOptionsBuilder;\n\n /**\n * Build the final StepOptions.\n */\n build(): StepOptions;\n}\n\n/**\n * Create a fluent builder for step options.\n *\n * @returns StepOptionsBuilder instance\n *\n * @example\n * ```typescript\n * const options = stepOptions()\n * .name('fetch-user')\n * .key('user:123')\n * .timeout(5000)\n * .retries(3)\n * .build();\n *\n * const user = await step(() => fetchUser(id), options);\n * ```\n */\nexport function stepOptions(): StepOptionsBuilder {\n const policies: Policy[] = [];\n\n const builder: StepOptionsBuilder = {\n name(name: string) {\n policies.push({ name });\n return builder;\n },\n\n key(key: string) {\n policies.push({ key });\n return builder;\n },\n\n policy(policy: Policy) {\n policies.push(policy);\n return builder;\n },\n\n timeout(ms: number) {\n policies.push(timeoutPolicy({ ms }));\n return builder;\n },\n\n retry(options: RetryOptions) {\n policies.push(retryPolicy(options));\n return builder;\n },\n\n retries(attempts: number) {\n policies.push(retryPolicies.custom({ attempts }));\n return builder;\n },\n\n build(): StepOptions {\n return mergePolicies(...policies);\n },\n };\n\n return builder;\n}\n","/**\n * @jagreehal/workflow/persistence\n *\n * Pluggable Persistence Adapters for StepCache and ResumeState.\n * Provides adapters for Redis, file system, and in-memory storage,\n * plus helpers for JSON-safe serialization of causes.\n */\n\nimport type { Result, StepFailureMeta } from \"./core\";\nimport { ok, err } from \"./core\";\nimport type { StepCache, ResumeState, ResumeStateEntry } from \"./workflow\";\n\n// =============================================================================\n// Serialization Types\n// =============================================================================\n\n/**\n * JSON-safe representation of a Result.\n */\nexport interface SerializedResult {\n ok: boolean;\n value?: unknown;\n error?: unknown;\n cause?: SerializedCause;\n}\n\n/**\n * JSON-safe representation of a cause value.\n * Handles Error objects and other non-JSON-safe types.\n */\nexport interface SerializedCause {\n type: \"error\" | \"value\" | \"undefined\";\n errorName?: string;\n errorMessage?: string;\n errorStack?: string;\n value?: unknown;\n}\n\n/**\n * JSON-safe representation of StepFailureMeta.\n */\nexport interface SerializedMeta {\n origin: \"result\" | \"throw\";\n resultCause?: SerializedCause;\n thrown?: SerializedCause;\n}\n\n/**\n * JSON-safe representation of a ResumeStateEntry.\n */\nexport interface SerializedEntry {\n result: SerializedResult;\n meta?: SerializedMeta;\n}\n\n/**\n * JSON-safe representation of ResumeState.\n */\nexport interface SerializedState {\n version: number;\n entries: Record<string, SerializedEntry>;\n metadata?: Record<string, unknown>;\n}\n\n// =============================================================================\n// Serialization Helpers\n// =============================================================================\n\n/**\n * Serialize a cause value to a JSON-safe format.\n */\nexport function serializeCause(cause: unknown): SerializedCause {\n if (cause === undefined) {\n return { type: \"undefined\" };\n }\n\n if (cause instanceof Error) {\n return {\n type: \"error\",\n errorName: cause.name,\n errorMessage: cause.message,\n errorStack: cause.stack,\n };\n }\n\n // Try to serialize as JSON\n try {\n // Test if it's JSON-serializable\n JSON.stringify(cause);\n return { type: \"value\", value: cause };\n } catch {\n // Fall back to string representation\n return { type: \"value\", value: String(cause) };\n }\n}\n\n/**\n * Deserialize a cause value from JSON-safe format.\n */\nexport function deserializeCause(serialized: SerializedCause): unknown {\n if (serialized.type === \"undefined\") {\n return undefined;\n }\n\n if (serialized.type === \"error\") {\n const error = new Error(serialized.errorMessage ?? \"Unknown error\");\n error.name = serialized.errorName ?? \"Error\";\n if (serialized.errorStack) {\n error.stack = serialized.errorStack;\n }\n return error;\n }\n\n return serialized.value;\n}\n\n/**\n * Serialize a Result to a JSON-safe format.\n */\nexport function serializeResult(result: Result<unknown, unknown, unknown>): SerializedResult {\n if (result.ok) {\n return { ok: true, value: result.value };\n }\n\n return {\n ok: false,\n error: result.error,\n cause: result.cause !== undefined ? serializeCause(result.cause) : undefined,\n };\n}\n\n/**\n * Deserialize a Result from JSON-safe format.\n */\nexport function deserializeResult(serialized: SerializedResult): Result<unknown, unknown, unknown> {\n if (serialized.ok) {\n return ok(serialized.value);\n }\n\n const cause = serialized.cause ? deserializeCause(serialized.cause) : undefined;\n return err(serialized.error, cause !== undefined ? { cause } : undefined);\n}\n\n/**\n * Serialize StepFailureMeta to a JSON-safe format.\n */\nexport function serializeMeta(meta: StepFailureMeta): SerializedMeta {\n if (meta.origin === \"result\") {\n return {\n origin: \"result\",\n resultCause: meta.resultCause !== undefined ? serializeCause(meta.resultCause) : undefined,\n };\n }\n\n return {\n origin: \"throw\",\n thrown: serializeCause(meta.thrown),\n };\n}\n\n/**\n * Deserialize StepFailureMeta from JSON-safe format.\n */\nexport function deserializeMeta(serialized: SerializedMeta): StepFailureMeta {\n if (serialized.origin === \"result\") {\n return {\n origin: \"result\",\n resultCause: serialized.resultCause ? deserializeCause(serialized.resultCause) : undefined,\n };\n }\n\n return {\n origin: \"throw\",\n thrown: serialized.thrown ? deserializeCause(serialized.thrown) : undefined,\n };\n}\n\n/**\n * Serialize a ResumeStateEntry to a JSON-safe format.\n */\nexport function serializeEntry(entry: ResumeStateEntry): SerializedEntry {\n return {\n result: serializeResult(entry.result),\n meta: entry.meta ? serializeMeta(entry.meta) : undefined,\n };\n}\n\n/**\n * Deserialize a ResumeStateEntry from JSON-safe format.\n */\nexport function deserializeEntry(serialized: SerializedEntry): ResumeStateEntry {\n return {\n result: deserializeResult(serialized.result),\n meta: serialized.meta ? deserializeMeta(serialized.meta) : undefined,\n };\n}\n\n/**\n * Serialize ResumeState to a JSON-safe format.\n */\nexport function serializeState(state: ResumeState, metadata?: Record<string, unknown>): SerializedState {\n const entries: Record<string, SerializedEntry> = {};\n\n for (const [key, entry] of state.steps) {\n entries[key] = serializeEntry(entry);\n }\n\n return {\n version: 1,\n entries,\n metadata,\n };\n}\n\n/**\n * Deserialize ResumeState from JSON-safe format.\n */\nexport function deserializeState(serialized: SerializedState): ResumeState {\n const steps = new Map<string, ResumeStateEntry>();\n\n for (const [key, entry] of Object.entries(serialized.entries)) {\n steps.set(key, deserializeEntry(entry));\n }\n\n return { steps };\n}\n\n/**\n * Convert ResumeState to a JSON string.\n */\nexport function stringifyState(state: ResumeState, metadata?: Record<string, unknown>): string {\n return JSON.stringify(serializeState(state, metadata));\n}\n\n/**\n * Parse ResumeState from a JSON string.\n */\nexport function parseState(json: string): ResumeState {\n const serialized = JSON.parse(json) as SerializedState;\n return deserializeState(serialized);\n}\n\n// =============================================================================\n// In-Memory Adapter\n// =============================================================================\n\n/**\n * Options for the in-memory cache adapter.\n */\nexport interface MemoryCacheOptions {\n /**\n * Maximum number of entries to store.\n * Oldest entries are evicted when limit is reached.\n */\n maxSize?: number;\n\n /**\n * Time-to-live in milliseconds.\n * Entries are automatically removed after this duration.\n */\n ttl?: number;\n}\n\n/**\n * Create an in-memory StepCache with optional LRU eviction and TTL.\n *\n * @param options - Cache options\n * @returns StepCache implementation\n *\n * @example\n * ```typescript\n * const cache = createMemoryCache({ maxSize: 1000, ttl: 60000 });\n * const workflow = createWorkflow(deps, { cache });\n * ```\n */\nexport function createMemoryCache(options: MemoryCacheOptions = {}): StepCache {\n const { maxSize, ttl } = options;\n const cache = new Map<string, { result: Result<unknown, unknown, unknown>; timestamp: number }>();\n\n const isExpired = (timestamp: number): boolean => {\n if (!ttl) return false;\n return Date.now() - timestamp > ttl;\n };\n\n const evictExpired = (): void => {\n if (!ttl) return;\n for (const [key, entry] of cache) {\n if (isExpired(entry.timestamp)) {\n cache.delete(key);\n }\n }\n };\n\n const evictOldest = (): void => {\n if (!maxSize || cache.size < maxSize) return;\n\n // Find oldest entry\n let oldestKey: string | undefined;\n let oldestTime = Infinity;\n\n for (const [key, entry] of cache) {\n if (entry.timestamp < oldestTime) {\n oldestTime = entry.timestamp;\n oldestKey = key;\n }\n }\n\n if (oldestKey) {\n cache.delete(oldestKey);\n }\n };\n\n return {\n get(key: string): Result<unknown, unknown, unknown> | undefined {\n evictExpired();\n const entry = cache.get(key);\n if (!entry) return undefined;\n if (isExpired(entry.timestamp)) {\n cache.delete(key);\n return undefined;\n }\n return entry.result;\n },\n\n set(key: string, result: Result<unknown, unknown, unknown>): void {\n evictExpired();\n evictOldest();\n cache.set(key, { result, timestamp: Date.now() });\n },\n\n has(key: string): boolean {\n evictExpired();\n const entry = cache.get(key);\n if (!entry) return false;\n if (isExpired(entry.timestamp)) {\n cache.delete(key);\n return false;\n }\n return true;\n },\n\n delete(key: string): boolean {\n return cache.delete(key);\n },\n\n clear(): void {\n cache.clear();\n },\n };\n}\n\n// =============================================================================\n// File System Adapter\n// =============================================================================\n\n/**\n * Options for the file system cache adapter.\n */\nexport interface FileCacheOptions {\n /**\n * Directory to store cache files.\n */\n directory: string;\n\n /**\n * File extension for cache files.\n * @default '.json'\n */\n extension?: string;\n\n /**\n * Custom file system interface (for testing or custom implementations).\n */\n fs?: FileSystemInterface;\n}\n\n/**\n * Minimal file system interface for cache operations.\n */\nexport interface FileSystemInterface {\n readFile(path: string): Promise<string>;\n writeFile(path: string, data: string): Promise<void>;\n unlink(path: string): Promise<void>;\n exists(path: string): Promise<boolean>;\n readdir(path: string): Promise<string[]>;\n mkdir(path: string, options?: { recursive?: boolean }): Promise<void>;\n}\n\n/**\n * Create a file system-based StepCache.\n * Each step result is stored as a separate JSON file.\n *\n * @param options - Cache options\n * @returns StepCache implementation (async operations wrapped in sync interface)\n *\n * @example\n * ```typescript\n * import * as fs from 'fs/promises';\n *\n * const cache = createFileCache({\n * directory: './workflow-cache',\n * fs: {\n * readFile: (path) => fs.readFile(path, 'utf-8'),\n * writeFile: (path, data) => fs.writeFile(path, data, 'utf-8'),\n * unlink: fs.unlink,\n * exists: async (path) => fs.access(path).then(() => true).catch(() => false),\n * readdir: fs.readdir,\n * mkdir: fs.mkdir,\n * },\n * });\n * ```\n */\nexport function createFileCache(options: FileCacheOptions): StepCache & {\n /** Initialize the cache directory. Call before using the cache. */\n init(): Promise<void>;\n /** Get a result asynchronously. */\n getAsync(key: string): Promise<Result<unknown, unknown, unknown> | undefined>;\n /** Set a result asynchronously. */\n setAsync(key: string, result: Result<unknown, unknown, unknown>): Promise<void>;\n /** Delete a result asynchronously. */\n deleteAsync(key: string): Promise<boolean>;\n /** Clear all results asynchronously. */\n clearAsync(): Promise<void>;\n} {\n const { directory, extension = \".json\", fs } = options;\n\n if (!fs) {\n throw new Error(\"File system interface is required. Pass fs option with readFile, writeFile, etc.\");\n }\n\n const keyToPath = (key: string): string => {\n // Sanitize key for file system\n const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n return `${directory}/${safeKey}${extension}`;\n };\n\n // In-memory fallback for sync operations\n const memoryCache = new Map<string, Result<unknown, unknown, unknown>>();\n\n return {\n async init(): Promise<void> {\n await fs.mkdir(directory, { recursive: true });\n },\n\n get(key: string): Result<unknown, unknown, unknown> | undefined {\n // Sync operation uses memory cache\n return memoryCache.get(key);\n },\n\n async getAsync(key: string): Promise<Result<unknown, unknown, unknown> | undefined> {\n const path = keyToPath(key);\n try {\n if (!(await fs.exists(path))) return undefined;\n const data = await fs.readFile(path);\n const serialized = JSON.parse(data) as SerializedResult;\n const result = deserializeResult(serialized);\n memoryCache.set(key, result);\n return result;\n } catch {\n return undefined;\n }\n },\n\n set(key: string, result: Result<unknown, unknown, unknown>): void {\n // Sync operation updates memory cache\n memoryCache.set(key, result);\n },\n\n async setAsync(key: string, result: Result<unknown, unknown, unknown>): Promise<void> {\n const path = keyToPath(key);\n const serialized = serializeResult(result);\n await fs.writeFile(path, JSON.stringify(serialized, null, 2));\n memoryCache.set(key, result);\n },\n\n has(key: string): boolean {\n return memoryCache.has(key);\n },\n\n delete(key: string): boolean {\n return memoryCache.delete(key);\n },\n\n async deleteAsync(key: string): Promise<boolean> {\n const path = keyToPath(key);\n try {\n await fs.unlink(path);\n memoryCache.delete(key);\n return true;\n } catch {\n return false;\n }\n },\n\n clear(): void {\n memoryCache.clear();\n },\n\n async clearAsync(): Promise<void> {\n try {\n const files = await fs.readdir(directory);\n for (const file of files) {\n if (file.endsWith(extension)) {\n await fs.unlink(`${directory}/${file}`);\n }\n }\n memoryCache.clear();\n } catch {\n // Directory may not exist\n }\n },\n };\n}\n\n// =============================================================================\n// Key-Value Store Adapter\n// =============================================================================\n\n/**\n * Generic key-value store interface.\n * Implement this for Redis, DynamoDB, etc.\n */\nexport interface KeyValueStore {\n get(key: string): Promise<string | null>;\n set(key: string, value: string, options?: { ttl?: number }): Promise<void>;\n delete(key: string): Promise<boolean>;\n exists(key: string): Promise<boolean>;\n keys(pattern: string): Promise<string[]>;\n}\n\n/**\n * Options for key-value store cache adapter.\n */\nexport interface KVCacheOptions {\n /**\n * Key-value store implementation.\n */\n store: KeyValueStore;\n\n /**\n * Key prefix for all cache entries.\n * @default 'workflow:'\n */\n prefix?: string;\n\n /**\n * Time-to-live in seconds for cache entries.\n */\n ttl?: number;\n}\n\n/**\n * Create a StepCache backed by a key-value store (Redis, DynamoDB, etc.).\n *\n * @param options - Cache options\n * @returns StepCache implementation with async methods\n *\n * @example\n * ```typescript\n * // With Redis\n * import { createClient } from 'redis';\n *\n * const redis = createClient();\n * await redis.connect();\n *\n * const cache = createKVCache({\n * store: {\n * get: (key) => redis.get(key),\n * set: (key, value, opts) => redis.set(key, value, { EX: opts?.ttl }),\n * delete: (key) => redis.del(key).then(n => n > 0),\n * exists: (key) => redis.exists(key).then(n => n > 0),\n * keys: (pattern) => redis.keys(pattern),\n * },\n * prefix: 'myapp:workflow:',\n * ttl: 3600, // 1 hour\n * });\n * ```\n */\nexport function createKVCache(options: KVCacheOptions): StepCache & {\n /** Get a result asynchronously. */\n getAsync(key: string): Promise<Result<unknown, unknown, unknown> | undefined>;\n /** Set a result asynchronously. */\n setAsync(key: string, result: Result<unknown, unknown, unknown>): Promise<void>;\n /** Check if key exists asynchronously. */\n hasAsync(key: string): Promise<boolean>;\n /** Delete a result asynchronously. */\n deleteAsync(key: string): Promise<boolean>;\n /** Clear all results asynchronously. */\n clearAsync(): Promise<void>;\n} {\n const { store, prefix = \"workflow:\", ttl } = options;\n\n const prefixKey = (key: string): string => `${prefix}${key}`;\n\n // In-memory fallback for sync operations\n const memoryCache = new Map<string, Result<unknown, unknown, unknown>>();\n\n return {\n get(key: string): Result<unknown, unknown, unknown> | undefined {\n return memoryCache.get(key);\n },\n\n async getAsync(key: string): Promise<Result<unknown, unknown, unknown> | undefined> {\n const data = await store.get(prefixKey(key));\n if (!data) return undefined;\n\n try {\n const serialized = JSON.parse(data) as SerializedResult;\n const result = deserializeResult(serialized);\n memoryCache.set(key, result);\n return result;\n } catch {\n return undefined;\n }\n },\n\n set(key: string, result: Result<unknown, unknown, unknown>): void {\n memoryCache.set(key, result);\n },\n\n async setAsync(key: string, result: Result<unknown, unknown, unknown>): Promise<void> {\n const serialized = serializeResult(result);\n await store.set(prefixKey(key), JSON.stringify(serialized), ttl ? { ttl } : undefined);\n memoryCache.set(key, result);\n },\n\n has(key: string): boolean {\n return memoryCache.has(key);\n },\n\n async hasAsync(key: string): Promise<boolean> {\n return store.exists(prefixKey(key));\n },\n\n delete(key: string): boolean {\n return memoryCache.delete(key);\n },\n\n async deleteAsync(key: string): Promise<boolean> {\n memoryCache.delete(key);\n return store.delete(prefixKey(key));\n },\n\n clear(): void {\n memoryCache.clear();\n },\n\n async clearAsync(): Promise<void> {\n const keys = await store.keys(`${prefix}*`);\n for (const key of keys) {\n await store.delete(key);\n }\n memoryCache.clear();\n },\n };\n}\n\n// =============================================================================\n// State Persistence\n// =============================================================================\n\n/**\n * Interface for persisting workflow state.\n */\nexport interface StatePersistence {\n /**\n * Save workflow state.\n */\n save(runId: string, state: ResumeState, metadata?: Record<string, unknown>): Promise<void>;\n\n /**\n * Load workflow state.\n */\n load(runId: string): Promise<ResumeState | undefined>;\n\n /**\n * Delete workflow state.\n */\n delete(runId: string): Promise<boolean>;\n\n /**\n * List all saved workflow IDs.\n */\n list(): Promise<string[]>;\n}\n\n/**\n * Create a state persistence adapter using a key-value store.\n *\n * @param store - Key-value store implementation\n * @param prefix - Key prefix for state entries\n * @returns StatePersistence implementation\n */\nexport function createStatePersistence(\n store: KeyValueStore,\n prefix = \"workflow:state:\"\n): StatePersistence {\n const prefixKey = (runId: string): string => `${prefix}${runId}`;\n\n return {\n async save(runId: string, state: ResumeState, metadata?: Record<string, unknown>): Promise<void> {\n const serialized = serializeState(state, metadata);\n await store.set(prefixKey(runId), JSON.stringify(serialized));\n },\n\n async load(runId: string): Promise<ResumeState | undefined> {\n const data = await store.get(prefixKey(runId));\n if (!data) return undefined;\n\n try {\n const serialized = JSON.parse(data) as SerializedState;\n return deserializeState(serialized);\n } catch {\n return undefined;\n }\n },\n\n async delete(runId: string): Promise<boolean> {\n return store.delete(prefixKey(runId));\n },\n\n async list(): Promise<string[]> {\n const keys = await store.keys(`${prefix}*`);\n return keys.map((key) => key.slice(prefix.length));\n },\n };\n}\n\n// =============================================================================\n// Cache Wrapper with Async Hydration\n// =============================================================================\n\n/**\n * Create a cache that hydrates from persistent storage on first access.\n *\n * @param memoryCache - In-memory cache for fast access\n * @param persistence - Persistent storage for durability\n * @returns Hydrating cache implementation\n */\nexport function createHydratingCache(\n memoryCache: StepCache,\n persistence: StatePersistence,\n runId: string\n): StepCache & { hydrate(): Promise<void> } {\n let hydrated = false;\n\n return {\n async hydrate(): Promise<void> {\n if (hydrated) return;\n\n const state = await persistence.load(runId);\n if (state) {\n for (const [key, entry] of state.steps) {\n memoryCache.set(key, entry.result);\n }\n }\n hydrated = true;\n },\n\n get(key: string): Result<unknown, unknown, unknown> | undefined {\n return memoryCache.get(key);\n },\n\n set(key: string, result: Result<unknown, unknown, unknown>): void {\n memoryCache.set(key, result);\n },\n\n has(key: string): boolean {\n return memoryCache.has(key);\n },\n\n delete(key: string): boolean {\n return memoryCache.delete(key);\n },\n\n clear(): void {\n memoryCache.clear();\n },\n };\n}\n","/**\n * Timing utilities for workflow visualization.\n */\n\n/**\n * Format duration in milliseconds to a human-readable string.\n *\n * @example\n * formatDuration(23) // \"23ms\"\n * formatDuration(1500) // \"1.5s\"\n * formatDuration(65000) // \"1m 5s\"\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)}ms`;\n }\n\n if (ms < 60000) {\n const seconds = ms / 1000;\n // Show one decimal for seconds\n return `${seconds.toFixed(1).replace(/\\.0$/, \"\")}s`;\n }\n\n const minutes = Math.floor(ms / 60000);\n const seconds = Math.round((ms % 60000) / 1000);\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 (\"decisionId\" in node) {\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 { 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 groupEnd = Math.max(...currentGroup.map((s) => s.endTs));\n\n // Check if this step overlaps with current group\n if (step.startTs <= groupEnd + maxGapMs) {\n currentGroup.push(step);\n } else {\n // No overlap - finalize current group and start new one\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 \"../core\";\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} 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// =============================================================================\n// Builder State\n// =============================================================================\n\ninterface ActiveStep {\n id: string;\n name?: string;\n key?: string;\n startTs: number;\n retryCount: number;\n timedOut: boolean;\n timeoutMs?: number;\n}\n\ninterface ActiveScope {\n id: string;\n name?: string;\n type: ScopeType;\n startTs: number;\n children: FlowNode[];\n}\n\ninterface ActiveDecision {\n id: string;\n name?: string;\n condition?: string;\n decisionValue?: unknown;\n startTs: number;\n branches: Map<string, DecisionBranch>;\n branchTaken?: string | boolean;\n}\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 { detectParallel = true, parallelDetection } = options;\n\n // Current workflow state\n let workflowId: string | undefined;\n let workflowStartTs: 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 // 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 /**\n * Get the step ID from an event.\n * Uses stepId if available (new events), then falls back to stepKey or name,\n * and finally generates a random ID for backwards compatibility.\n */\n function getStepId(event: { stepId?: string; stepKey?: string; name?: string }): string {\n return event.stepId ?? event.stepKey ?? event.name ?? generateId();\n }\n\n /**\n * Add a completed node to the current scope or decision branch.\n */\n function addNode(node: FlowNode): void {\n // If we're in a decision, 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 // If no branch is marked as taken yet, add to the first branch\n // (this handles cases where steps execute before branch is marked)\n const firstBranch = Array.from(decision.branches.values())[0];\n if (firstBranch) {\n firstBranch.children.push(node);\n lastUpdatedAt = Date.now();\n return;\n }\n }\n\n // If we're in a scope, add to the scope\n if (scopeStack.length > 0) {\n // Add to the innermost scope\n scopeStack[scopeStack.length - 1].children.push(node);\n } else {\n // Add to the root level\n currentNodes.push(node);\n }\n lastUpdatedAt = Date.now();\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 workflowId = event.workflowId;\n workflowStartTs = event.ts;\n workflowState = \"running\";\n createdAt = Date.now();\n lastUpdatedAt = createdAt;\n break;\n\n case \"workflow_success\":\n workflowState = \"success\";\n workflowDurationMs = event.durationMs;\n lastUpdatedAt = Date.now();\n break;\n\n case \"workflow_error\":\n workflowState = \"error\";\n workflowError = event.error;\n workflowDurationMs = event.durationMs;\n lastUpdatedAt = Date.now();\n break;\n\n case \"step_start\": {\n const id = getStepId(event);\n activeSteps.set(id, {\n id,\n name: event.name,\n key: event.stepKey,\n startTs: event.ts,\n retryCount: 0,\n timedOut: false,\n });\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"step_success\": {\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n const node: StepNode = {\n type: \"step\",\n id: active.id,\n name: active.name,\n key: active.key,\n state: \"success\",\n startTs: active.startTs,\n endTs: event.ts,\n durationMs: event.durationMs,\n ...(active.retryCount > 0 && { retryCount: active.retryCount }),\n ...(active.timedOut && { timedOut: true, timeoutMs: active.timeoutMs }),\n };\n addNode(node);\n activeSteps.delete(id);\n }\n break;\n }\n\n case \"step_error\": {\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n const node: StepNode = {\n type: \"step\",\n id: active.id,\n name: active.name,\n key: active.key,\n state: \"error\",\n startTs: active.startTs,\n endTs: event.ts,\n durationMs: event.durationMs,\n error: event.error,\n ...(active.retryCount > 0 && { retryCount: active.retryCount }),\n ...(active.timedOut && { timedOut: true, timeoutMs: active.timeoutMs }),\n };\n addNode(node);\n activeSteps.delete(id);\n }\n break;\n }\n\n case \"step_aborted\": {\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n const node: StepNode = {\n type: \"step\",\n id: active.id,\n name: active.name,\n key: active.key,\n state: \"aborted\",\n startTs: active.startTs,\n endTs: event.ts,\n durationMs: event.durationMs,\n ...(active.retryCount > 0 && { retryCount: active.retryCount }),\n ...(active.timedOut && { timedOut: true, timeoutMs: active.timeoutMs }),\n };\n addNode(node);\n activeSteps.delete(id);\n }\n break;\n }\n\n case \"step_cache_hit\": {\n const id = getStepId(event);\n const node: StepNode = {\n type: \"step\",\n id,\n name: event.name,\n key: event.stepKey,\n state: \"cached\",\n startTs: event.ts,\n endTs: event.ts,\n durationMs: 0,\n };\n addNode(node);\n break;\n }\n\n case \"step_cache_miss\":\n // Cache miss just means the step will execute normally\n // We'll get a step_start event next\n break;\n\n case \"step_complete\":\n // step_complete is for state persistence, not visualization\n // We already handled the step via step_success/step_error\n break;\n\n case \"step_timeout\": {\n // Timeout is an intermediate event - step may retry or will get step_error\n // Track timeout info on the active step\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n active.timedOut = true;\n active.timeoutMs = event.timeoutMs;\n }\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"step_retry\": {\n // Retry is an intermediate event - increment retry counter\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n active.retryCount = (event.attempt ?? 1) - 1; // attempt is 1-indexed, retryCount is 0-indexed\n }\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"step_retries_exhausted\":\n // All retries exhausted - step_error will follow\n // The error state will be set by step_error handler\n lastUpdatedAt = Date.now();\n break;\n\n case \"step_skipped\": {\n const id = getStepId(event);\n const node: StepNode = {\n type: \"step\",\n id,\n name: event.name,\n key: event.stepKey,\n state: \"skipped\",\n startTs: event.ts,\n endTs: event.ts,\n durationMs: 0,\n };\n addNode(node);\n break;\n }\n }\n }\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 const scope = scopeStack.pop();\n if (scope) {\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 /**\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 });\n lastUpdatedAt = Date.now();\n } else if (event.type === \"decision_branch\") {\n const decision = decisionStack[decisionStack.length - 1];\n if (decision && decision.id === event.decisionId) {\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 } else {\n // Create new branch\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 const decision = decisionStack.pop();\n if (decision && decision.id === event.decisionId) {\n // Convert branches map to array\n const branches: DecisionBranch[] = Array.from(decision.branches.values());\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 ?? decision.branchTaken,\n branches,\n };\n addNode(node);\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.\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 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 ?? generateId(),\n workflowId: workflowId ?? \"unknown\",\n state: workflowState,\n startTs: workflowStartTs,\n durationMs: workflowDurationMs,\n children,\n error: workflowError,\n };\n\n return {\n root,\n metadata: {\n createdAt,\n lastUpdatedAt,\n },\n };\n }\n\n /**\n * Reset the builder state.\n */\n function reset(): void {\n workflowId = undefined;\n workflowStartTs = undefined;\n workflowState = \"pending\";\n workflowError = undefined;\n workflowDurationMs = undefined;\n activeSteps.clear();\n scopeStack.length = 0;\n decisionStack.length = 0;\n currentNodes = [];\n createdAt = Date.now();\n lastUpdatedAt = createdAt;\n }\n\n return {\n handleEvent,\n handleScopeEvent,\n handleDecisionEvent,\n getIR,\n reset,\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 };\n}\n\n/**\n * Type for the IR builder instance.\n */\nexport type IRBuilder = ReturnType<typeof createIRBuilder>;\n","/**\n * Workflow Visualization - Intermediate Representation Types\n *\n * The IR (Intermediate Representation) is a DSL that represents workflow\n * execution structure. Events are converted to IR, which can then be\n * rendered to various output formats (ASCII, Mermaid, JSON, etc.).\n */\n\n// =============================================================================\n// Step States\n// =============================================================================\n\n/**\n * Execution state of a step with semantic meaning for visualization.\n *\n * Color mapping:\n * - pending → white/clear (not started)\n * - running → yellow (currently executing)\n * - success → green (completed successfully)\n * - error → red (failed with error)\n * - aborted → gray (cancelled, e.g., in race)\n * - cached → blue (served from cache)\n * - skipped → dim gray (not executed due to conditional logic)\n */\nexport type StepState =\n | \"pending\"\n | \"running\"\n | \"success\"\n | \"error\"\n | \"aborted\"\n | \"cached\"\n | \"skipped\";\n\n// =============================================================================\n// Node Types\n// =============================================================================\n\n/**\n * Base properties shared by all IR nodes.\n */\nexport interface BaseNode {\n /** Unique identifier for this node */\n id: string;\n /** Human-readable name (from step options or inferred) */\n name?: string;\n /** Cache key if this is a keyed step */\n key?: string;\n /** Current execution state */\n state: StepState;\n /** Timestamp when execution started */\n startTs?: number;\n /** Timestamp when execution ended */\n endTs?: number;\n /** Duration in milliseconds */\n durationMs?: number;\n /** Error value if state is 'error' */\n error?: unknown;\n /** Input value that triggered this step (for decision understanding) */\n input?: unknown;\n /** Output value from this step (for decision understanding) */\n output?: unknown;\n /** Number of retry attempts made (0 = no retries, 1 = one retry, etc.) */\n retryCount?: number;\n /** Whether this step experienced a timeout (may have retried after) */\n timedOut?: boolean;\n /** Timeout duration in ms (if timed out) */\n timeoutMs?: number;\n}\n\n/**\n * A single step execution node.\n */\nexport interface StepNode extends BaseNode {\n type: \"step\";\n}\n\n/**\n * Sequential execution - steps run one after another.\n * This is the implicit structure when steps are awaited in sequence.\n */\nexport interface SequenceNode extends BaseNode {\n type: \"sequence\";\n children: FlowNode[];\n}\n\n/**\n * Parallel execution - all branches run simultaneously.\n * Created by allAsync() or allSettledAsync().\n */\nexport interface ParallelNode extends BaseNode {\n type: \"parallel\";\n children: FlowNode[];\n /**\n * Execution mode:\n * - 'all': Fails on first error (allAsync)\n * - 'allSettled': Collects all results (allSettledAsync)\n */\n mode: \"all\" | \"allSettled\";\n}\n\n/**\n * Race execution - first to complete wins.\n * Created by anyAsync().\n */\nexport interface RaceNode extends BaseNode {\n type: \"race\";\n children: FlowNode[];\n /** ID of the winning branch (first to succeed) */\n winnerId?: string;\n}\n\n/**\n * 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;\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}\n\n// =============================================================================\n// Scope Events (for parallel/race detection)\n// =============================================================================\n\n// Re-export ScopeType from core for consistency\nexport type { ScopeType } from \"../core\";\nimport type { ScopeType } from \"../core\";\n\n/**\n * Event emitted when entering a parallel/race scope.\n * This matches the scope_start event in WorkflowEvent.\n */\nexport interface ScopeStartEvent {\n type: \"scope_start\";\n workflowId: string;\n scopeId: string;\n scopeType: ScopeType;\n name?: string;\n ts: number;\n}\n\n/**\n * Event emitted when exiting a parallel/race scope.\n */\nexport interface ScopeEndEvent {\n type: \"scope_end\";\n workflowId: string;\n scopeId: string;\n ts: number;\n durationMs: number;\n /** For race scopes, the ID of the winning branch */\n winnerId?: string;\n}\n\n/**\n * Event emitted when a decision point is encountered.\n * Use this to track conditional logic (if/switch).\n */\nexport interface DecisionStartEvent {\n type: \"decision_start\";\n workflowId: string;\n decisionId: string;\n /** Condition being evaluated (e.g., \"user.role === 'admin'\") */\n condition?: string;\n /** Value being evaluated */\n decisionValue?: unknown;\n /** Name/label for this decision point */\n name?: string;\n ts: number;\n}\n\n/**\n * Event emitted when a decision branch is taken.\n */\nexport interface DecisionBranchEvent {\n type: \"decision_branch\";\n workflowId: string;\n decisionId: string;\n /** Label for this branch (e.g., \"if\", \"else\", \"case 'admin'\") */\n branchLabel: string;\n /** Condition for this branch */\n condition?: string;\n /** Whether this branch was taken */\n taken: boolean;\n ts: number;\n}\n\n/**\n * Event emitted when a decision point completes.\n */\nexport interface DecisionEndEvent {\n type: \"decision_end\";\n workflowId: string;\n decisionId: string;\n /** Which branch was taken */\n branchTaken?: string | boolean;\n ts: number;\n durationMs: number;\n}\n\n/**\n * Event emitted when a step is skipped due to conditional logic.\n */\nexport interface StepSkippedEvent {\n type: \"step_skipped\";\n workflowId: string;\n stepKey?: string;\n name?: string;\n /** Reason why this step was skipped (e.g., \"condition was false\") */\n reason?: string;\n /** The decision that caused this skip */\n decisionId?: string;\n ts: number;\n}\n\n/**\n * Union of scope-related events.\n */\nexport type ScopeEvent = ScopeStartEvent | ScopeEndEvent;\n\n/**\n * Union of decision-related events.\n */\nexport type DecisionEvent = DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent;\n\n// =============================================================================\n// Renderer Types\n// =============================================================================\n\n/**\n * Color scheme for rendering step states.\n */\nexport interface ColorScheme {\n pending: string;\n running: string;\n success: string;\n error: string;\n aborted: string;\n cached: string;\n skipped: string;\n}\n\n/**\n * Options passed to renderers.\n */\nexport interface RenderOptions {\n /** Show timing information (duration) */\n showTimings: boolean;\n /** Show step cache keys */\n showKeys: boolean;\n /** Terminal width for ASCII renderer */\n terminalWidth?: number;\n /** Color scheme */\n colors: ColorScheme;\n}\n\n/**\n * 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\";\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\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 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 * 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 * 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 type {\n FlowNode,\n ParallelNode,\n RaceNode,\n DecisionNode,\n Renderer,\n RenderOptions,\n StepNode,\n WorkflowIR,\n} from \"../types\";\nimport { isParallelNode, isRaceNode, isStepNode, isDecisionNode } 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// 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// 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 const remainingWidth = width - titleText.length;\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// 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 const width = options.terminalWidth ?? 60;\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 children\n const childLines = renderNodes(ir.root.children, options, colors, 0);\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 = ir.root.state === \"success\" ? \"Completed\" : \"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): string[] {\n const lines: string[] = [];\n\n for (const node of nodes) {\n if (isStepNode(node)) {\n lines.push(renderStepNode(node, options, colors));\n } else if (isParallelNode(node)) {\n lines.push(...renderParallelNode(node, options, colors, depth));\n } else if (isRaceNode(node)) {\n lines.push(...renderRaceNode(node, options, colors, depth));\n } else if (isDecisionNode(node)) {\n lines.push(...renderDecisionNode(node, options, colors, depth));\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): string {\n const symbol = getColoredSymbol(node.state, colors);\n const name = node.name ?? node.key ?? \"step\";\n const nameColored = colorByState(name, node.state, colors);\n\n let line = `${symbol} ${nameColored}`;\n\n // Add key if requested\n if (options.showKeys && node.key) {\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 : JSON.stringify(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 : JSON.stringify(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 line += dim(` [${formatDuration(node.durationMs)}]`);\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 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): 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 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)}`);\n } else {\n // Nested structure - recurse\n const nestedLines = renderNodes([child], options, colors, depth + 1);\n for (const line of nestedLines) {\n lines.push(`${indent}${BOX.vertical} ${line}`);\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): 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 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)}${winnerSuffix}`);\n } else {\n const nestedLines = renderNodes([child], options, colors, depth + 1);\n for (const line of nestedLines) {\n lines.push(`${indent}${BOX.vertical} ${line}`);\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): 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);\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 * 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 type {\n FlowNode,\n ParallelNode,\n RaceNode,\n DecisionNode,\n Renderer,\n RenderOptions,\n StepNode,\n StepState,\n WorkflowIR,\n} from \"../types\";\nimport { isParallelNode, isRaceNode, isStepNode, isDecisionNode } from \"../types\";\nimport { formatDuration } from \"../utils/timing\";\n\n// =============================================================================\n// Mermaid Style Definitions\n// =============================================================================\n\n/**\n * Get Mermaid class definition for step states.\n */\nfunction getStyleDefinitions(): string[] {\n return [\n \" classDef pending fill:#e5e7eb,stroke:#9ca3af,color:#374151\",\n \" classDef running fill:#fef3c7,stroke:#f59e0b,color:#92400e\",\n \" classDef success fill:#d1fae5,stroke:#10b981,color:#065f46\",\n \" classDef error fill:#fee2e2,stroke:#ef4444,color:#991b1b\",\n \" classDef aborted fill:#f3f4f6,stroke:#6b7280,color:#4b5563\",\n \" classDef cached fill:#dbeafe,stroke:#3b82f6,color:#1e40af\",\n // Note: Use a lighter fill color to distinguish skipped steps visually\n // Mermaid classDef only supports: fill, stroke, color, stroke-width (without px unit)\n \" classDef skipped fill:#f9fafb,stroke:#d1d5db,color:#6b7280\",\n ];\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// Node ID Generation\n// =============================================================================\n\nlet nodeCounter = 0;\n\nfunction generateNodeId(prefix: string = \"node\"): string {\n return `${prefix}_${++nodeCounter}`;\n}\n\nfunction resetNodeCounter(): void {\n nodeCounter = 0;\n}\n\n// =============================================================================\n// Mermaid Text Escaping\n// =============================================================================\n\n/**\n * Escape text for use in Mermaid diagrams.\n * Removes characters that break Mermaid parsing.\n * \n * Characters removed:\n * - {}[]() - Brackets and parentheses break parsing in labels\n * - <> - Angle brackets can cause issues\n * - \" - Double quotes replaced with single quotes\n * \n * @param text - Text to escape\n * @returns Escaped text safe for Mermaid\n */\nfunction escapeMermaidText(text: string): string {\n return text\n .replace(/[{}[\\]()]/g, \"\") // Remove brackets and parentheses (they break parsing)\n .replace(/[<>]/g, \"\") // Remove angle brackets\n .replace(/\"/g, \"'\") // Replace double quotes with single\n .trim();\n}\n\n/**\n * Escape text for use in Mermaid subgraph names.\n * Subgraph names in brackets need special handling.\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, \"\"); // Also remove brackets 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 // Diagram header\n lines.push(\"flowchart TD\");\n\n // Start node\n const startId = \"start\";\n lines.push(` ${startId}((Start))`);\n\n // Track the last node for connections\n let prevNodeId = startId;\n\n // Render children\n for (const child of ir.root.children) {\n const result = renderNode(child, options, lines);\n lines.push(` ${prevNodeId} --> ${result.entryId}`);\n prevNodeId = result.exitId;\n }\n\n // End node (if workflow completed)\n if (ir.root.state === \"success\" || ir.root.state === \"error\") {\n const endId = \"finish\";\n const endShape =\n ir.root.state === \"success\" ? `((Done))` : `((Failed))`;\n const endClass =\n ir.root.state === \"success\" ? \":::success\" : \":::error\";\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 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): RenderResult {\n if (isStepNode(node)) {\n return renderStepNode(node, options, lines);\n } else if (isParallelNode(node)) {\n return renderParallelNode(node, options, lines);\n } else if (isRaceNode(node)) {\n return renderRaceNode(node, options, lines);\n } else if (isDecisionNode(node)) {\n return renderDecisionNode(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): RenderResult {\n const id = node.key\n ? `step_${node.key.replace(/[^a-zA-Z0-9]/g, \"_\")}`\n : generateNodeId(\"step\");\n\n const label = escapeMermaidText(node.name ?? node.key ?? \"Step\");\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 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(JSON.stringify(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(JSON.stringify(node.output).slice(0, 20));\n ioInfo += `\\\\nout: ${outputStr}`;\n }\n\n // Add retry/timeout indicators\n let retryInfo = \"\";\n if (node.retryCount !== undefined && node.retryCount > 0) {\n retryInfo += `\\\\n↻${node.retryCount}`;\n }\n if (node.timedOut) {\n const timeoutStr = node.timeoutMs !== undefined ? `${node.timeoutMs}ms` : \"\";\n retryInfo += `\\\\n⏱${timeoutStr}`;\n }\n\n // Combine all label parts\n const escapedLabel = (label + ioInfo + retryInfo + timing).trim();\n\n const stateClass = getStateClass(node.state);\n\n // Use different shapes based on state\n let shape: string;\n switch (node.state) {\n case \"error\":\n shape = `{{${escapedLabel}}}`;\n break;\n case \"cached\":\n shape = `[(${escapedLabel})]`;\n break;\n case \"skipped\":\n shape = `[${escapedLabel}]:::skipped`;\n break;\n default:\n shape = `[${escapedLabel}]`;\n }\n\n lines.push(` ${id}${shape}:::${stateClass}`);\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): RenderResult {\n const subgraphId = generateNodeId(\"parallel\");\n const forkId = `${subgraphId}_fork`;\n const joinId = `${subgraphId}_join`;\n const name = escapeSubgraphName(node.name ?? \"Parallel\");\n\n // Subgraph for parallel block\n lines.push(` subgraph ${subgraphId}[${name}]`);\n lines.push(` direction TB`);\n\n // Fork node (diamond)\n lines.push(` ${forkId}{Fork}`);\n\n // Child branches\n const childExitIds: string[] = [];\n for (const child of node.children) {\n const result = renderNode(child, options, lines);\n lines.push(` ${forkId} --> ${result.entryId}`);\n childExitIds.push(result.exitId);\n }\n\n // Join node (diamond)\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 via a connecting node\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): 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 // 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\n lines.push(` ${startId}((Race))`);\n\n // Child branches\n const childExitIds: string[] = [];\n for (const child of node.children) {\n const result = renderNode(child, options, lines);\n lines.push(` ${startId} --> ${result.entryId}`);\n childExitIds.push(result.exitId);\n\n // Mark winner\n if (isStepNode(child) && node.winnerId === child.id) {\n lines.push(` ${result.exitId} -. winner .-> ${endId}`);\n }\n }\n\n // End node\n lines.push(` ${endId}((First))`);\n for (const exitId of childExitIds) {\n if (\n !node.winnerId ||\n !node.children.some((c) => isStepNode(c) && c.id === node.winnerId)\n ) {\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): RenderResult {\n const decisionId = node.key\n ? `decision_${node.key.replace(/[^a-zA-Z0-9]/g, \"_\")}`\n : generateNodeId(\"decision\");\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\n for (const branch of node.branches) {\n const branchId = `${decisionId}_${branch.label.replace(/[^a-zA-Z0-9]/g, \"_\")}`;\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);\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\nexport { mermaidRenderer as default };\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 '@jagreehal/workflow/visualize';\n *\n * const viz = createVisualizer({ workflowName: 'checkout' });\n * const workflow = createWorkflow(deps, { onEvent: viz.handleEvent });\n *\n * await workflow(async (step) => {\n * await step(() => validateCart(cart), 'Validate cart');\n * await step(() => processPayment(payment), 'Process payment');\n * });\n *\n * console.log(viz.render());\n * ```\n */\n\nimport type { WorkflowEvent } from \"../core\";\nimport type {\n OutputFormat,\n RenderOptions,\n ScopeEndEvent,\n ScopeStartEvent,\n DecisionStartEvent,\n DecisionBranchEvent,\n DecisionEndEvent,\n VisualizerOptions,\n WorkflowIR,\n} from \"./types\";\nimport { createIRBuilder } from \"./ir-builder\";\nimport { asciiRenderer, mermaidRenderer, defaultColorScheme } from \"./renderers\";\n\n// =============================================================================\n// Re-exports\n// =============================================================================\n\nexport * from \"./types\";\nexport { createIRBuilder, type IRBuilderOptions } from \"./ir-builder\";\nexport { asciiRenderer, mermaidRenderer, defaultColorScheme } from \"./renderers\";\nexport { detectParallelGroups, createParallelDetector, type ParallelDetectorOptions } from \"./parallel-detector\";\nexport { createLiveVisualizer, type LiveVisualizer } from \"./live-visualizer\";\nexport { trackDecision, trackIf, trackSwitch, type DecisionTracker, type IfTracker, type SwitchTracker } from \"./decision-tracker\";\n\n// =============================================================================\n// Visualizer Interface\n// =============================================================================\n\n/**\n * Workflow visualizer that processes events and renders output.\n */\nexport interface WorkflowVisualizer {\n /** Process a workflow event */\n handleEvent: (event: WorkflowEvent<unknown>) => void;\n\n /** Process a scope event (parallel/race) */\n handleScopeEvent: (event: ScopeStartEvent | ScopeEndEvent) => void;\n\n /** Process a decision event (conditional branches) */\n handleDecisionEvent: (event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent) => void;\n\n /** Get current IR state */\n getIR: () => WorkflowIR;\n\n /** Render current state using the default renderer */\n render: () => string;\n\n /** Render to a specific format */\n renderAs: (format: OutputFormat) => string;\n\n /** Reset state for a new workflow */\n reset: () => void;\n\n /** Subscribe to IR updates (for live visualization) */\n onUpdate: (callback: (ir: WorkflowIR) => void) => () => void;\n}\n\n// =============================================================================\n// Create Visualizer\n// =============================================================================\n\n/**\n * Create a workflow visualizer.\n *\n * @example\n * ```typescript\n * const viz = createVisualizer({ workflowName: 'my-workflow' });\n *\n * const workflow = createWorkflow(deps, {\n * onEvent: viz.handleEvent,\n * });\n *\n * await workflow(async (step) => { ... });\n *\n * console.log(viz.render());\n * ```\n */\nexport function createVisualizer(\n options: VisualizerOptions = {}\n): WorkflowVisualizer {\n const {\n workflowName,\n detectParallel = true,\n showTimings = true,\n showKeys = false,\n colors: customColors,\n } = options;\n\n const builder = createIRBuilder({ detectParallel });\n const updateCallbacks: Set<(ir: WorkflowIR) => void> = new Set();\n\n // Renderers\n const ascii = asciiRenderer();\n const mermaid = mermaidRenderer();\n\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 = builder.getIR();\n for (const callback of updateCallbacks) {\n callback(ir);\n }\n }\n }\n\n function handleEvent(event: WorkflowEvent<unknown>): void {\n // Route scope events to handleScopeEvent for proper IR building\n if (event.type === \"scope_start\" || event.type === \"scope_end\") {\n handleScopeEvent(event as ScopeStartEvent | ScopeEndEvent);\n return;\n }\n\n builder.handleEvent(event);\n\n // Set workflow name if provided\n if (event.type === \"workflow_start\" && workflowName) {\n // Note: We'd need to extend the builder to support setting name\n // For now, the name is passed in render options\n }\n\n notifyUpdate();\n }\n\n function handleScopeEvent(event: ScopeStartEvent | ScopeEndEvent): void {\n builder.handleScopeEvent(event);\n notifyUpdate();\n }\n\n function handleDecisionEvent(\n event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent\n ): void {\n builder.handleDecisionEvent(event);\n notifyUpdate();\n }\n\n function getIR(): WorkflowIR {\n const ir = builder.getIR();\n // Apply workflow name if provided\n if (workflowName && !ir.root.name) {\n ir.root.name = workflowName;\n }\n return ir;\n }\n\n function render(): string {\n const ir = getIR();\n return ascii.render(ir, renderOptions);\n }\n\n function renderAs(format: OutputFormat): string {\n const ir = getIR();\n\n switch (format) {\n case \"ascii\":\n return ascii.render(ir, renderOptions);\n\n case \"mermaid\":\n return mermaid.render(ir, renderOptions);\n\n case \"json\":\n return JSON.stringify(ir, null, 2);\n\n default:\n throw new Error(`Unknown format: ${format}`);\n }\n }\n\n function reset(): void {\n builder.reset();\n notifyUpdate();\n }\n\n function onUpdate(callback: (ir: WorkflowIR) => void): () => void {\n updateCallbacks.add(callback);\n return () => updateCallbacks.delete(callback);\n }\n\n return {\n handleEvent,\n handleScopeEvent,\n handleDecisionEvent,\n getIR,\n render,\n renderAs,\n reset,\n onUpdate,\n };\n}\n\n// =============================================================================\n// Convenience Functions\n// =============================================================================\n\n/**\n * Union type for all collectable/visualizable events (workflow + decision).\n */\nexport type CollectableEvent =\n | WorkflowEvent<unknown>\n | DecisionStartEvent\n | DecisionBranchEvent\n | DecisionEndEvent;\n\n/**\n * Visualize collected events (post-execution).\n *\n * Supports both workflow events (from onEvent) and decision events\n * (from trackDecision/trackIf/trackSwitch).\n *\n * @example\n * ```typescript\n * const events: CollectableEvent[] = [];\n * const workflow = createWorkflow(deps, {\n * onEvent: (e) => events.push(e),\n * });\n *\n * await workflow(async (step) => {\n * const decision = trackIf('check', condition, {\n * emit: (e) => events.push(e),\n * });\n * // ...\n * });\n *\n * console.log(visualizeEvents(events));\n * ```\n */\nexport function visualizeEvents(\n events: CollectableEvent[],\n options: VisualizerOptions = {}\n): string {\n const viz = createVisualizer(options);\n\n for (const event of events) {\n if (event.type.startsWith(\"decision_\")) {\n viz.handleDecisionEvent(event as DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent);\n } else {\n viz.handleEvent(event as WorkflowEvent<unknown>);\n }\n }\n\n return viz.render();\n}\n\n/**\n * Create an event collector for later visualization.\n *\n * Supports both workflow events (from onEvent) and decision events\n * (from trackDecision/trackIf/trackSwitch).\n *\n * @example\n * ```typescript\n * const collector = createEventCollector();\n *\n * const workflow = createWorkflow(deps, {\n * onEvent: collector.handleEvent,\n * });\n *\n * await workflow(async (step) => {\n * // Decision events can also be collected\n * const decision = trackIf('check', condition, {\n * emit: collector.handleDecisionEvent,\n * });\n * // ...\n * });\n *\n * console.log(collector.visualize());\n * ```\n */\nexport function createEventCollector(options: VisualizerOptions = {}) {\n const events: CollectableEvent[] = [];\n\n return {\n /** Handle a workflow event */\n handleEvent: (event: WorkflowEvent<unknown>) => {\n events.push(event);\n },\n\n /** Handle a decision event */\n handleDecisionEvent: (event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent) => {\n events.push(event);\n },\n\n /** Get all collected events */\n getEvents: () => [...events],\n\n /** Get workflow events only */\n getWorkflowEvents: () => events.filter((e): e is WorkflowEvent<unknown> =>\n !e.type.startsWith(\"decision_\")\n ),\n\n /** Get decision events only */\n getDecisionEvents: () => events.filter((e): e is DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent =>\n e.type.startsWith(\"decision_\")\n ),\n\n /** Clear collected events */\n clear: () => {\n events.length = 0;\n },\n\n /** Visualize collected events */\n visualize: () => {\n const viz = createVisualizer(options);\n for (const event of events) {\n if (event.type.startsWith(\"decision_\")) {\n viz.handleDecisionEvent(event as DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent);\n } else {\n viz.handleEvent(event as WorkflowEvent<unknown>);\n }\n }\n return viz.render();\n },\n\n /** Visualize in a specific format */\n visualizeAs: (format: OutputFormat) => {\n const viz = createVisualizer(options);\n for (const event of events) {\n if (event.type.startsWith(\"decision_\")) {\n viz.handleDecisionEvent(event as DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent);\n } else {\n viz.handleEvent(event as WorkflowEvent<unknown>);\n }\n }\n return viz.renderAs(format);\n },\n };\n}\n\n","/**\n * @jagreehal/workflow/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 \"./core\";\nimport type {\n OutputFormat,\n VisualizerOptions,\n DecisionStartEvent,\n DecisionBranchEvent,\n DecisionEndEvent,\n CollectableEvent,\n} from \"./visualize\";\nimport { createVisualizer } from \"./visualize\";\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(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 = \"\";\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}\n","/**\n * @jagreehal/workflow/hitl\n *\n * Human-in-the-Loop Orchestration Helpers.\n * Provides pollers, webhook handlers, and resume injectors\n * for production-ready approval workflows.\n */\n\nimport type { Result, WorkflowEvent } from \"./core\";\nimport type { ResumeState, Workflow } from \"./workflow\";\nimport { createHITLCollector, isPendingApproval, injectApproval } from \"./workflow\";\n\n// =============================================================================\n// Workflow Factory Types\n// =============================================================================\n\n/**\n * Options passed to the workflow factory by the HITL orchestrator.\n */\nexport interface HITLWorkflowFactoryOptions {\n /** Resume state for replaying completed steps */\n resumeState?: ResumeState;\n /** Event handler for tracking workflow events (required for HITL) */\n onEvent: (event: WorkflowEvent<unknown>) => void;\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Approval status returned from the approval store.\n */\nexport type ApprovalStatus<T = unknown> =\n | { status: \"pending\" }\n | { status: \"approved\"; value: T; approvedBy?: string; approvedAt?: number }\n | { status: \"rejected\"; reason: string; rejectedBy?: string; rejectedAt?: number }\n | { status: \"expired\"; expiredAt: number };\n\n/**\n * Interface for approval storage backends.\n */\nexport interface ApprovalStore {\n /**\n * Get the status of an approval.\n */\n getApproval(key: string): Promise<ApprovalStatus>;\n\n /**\n * Create or update a pending approval request.\n */\n createApproval(\n key: string,\n options?: {\n metadata?: Record<string, unknown>;\n expiresAt?: number;\n requestedBy?: string;\n }\n ): Promise<void>;\n\n /**\n * Grant an approval.\n */\n grantApproval<T>(\n key: string,\n value: T,\n options?: { approvedBy?: string }\n ): Promise<void>;\n\n /**\n * Reject an approval.\n */\n rejectApproval(\n key: string,\n reason: string,\n options?: { rejectedBy?: string }\n ): Promise<void>;\n\n /**\n * Cancel a pending approval.\n */\n cancelApproval(key: string): Promise<void>;\n\n /**\n * List all pending approvals.\n */\n listPending(options?: { prefix?: string }): Promise<string[]>;\n}\n\n/**\n * Saved workflow state for resumption.\n */\nexport interface SavedWorkflowState {\n /** Unique identifier for this workflow run */\n runId: string;\n /** Workflow name/type */\n workflowName: string;\n /** Resume state with step results */\n resumeState: ResumeState;\n /** Pending approval keys */\n pendingApprovals: string[];\n /** Input that was passed to the workflow */\n input?: unknown;\n /** Custom metadata */\n metadata?: Record<string, unknown>;\n /** When the workflow was started */\n startedAt: number;\n /** When the state was last updated */\n updatedAt: number;\n}\n\n/**\n * Interface for workflow state storage.\n */\nexport interface WorkflowStateStore {\n /**\n * Save workflow state.\n */\n save(state: SavedWorkflowState): Promise<void>;\n\n /**\n * Load workflow state by run ID.\n */\n load(runId: string): Promise<SavedWorkflowState | undefined>;\n\n /**\n * Delete workflow state.\n */\n delete(runId: string): Promise<void>;\n\n /**\n * List all saved workflow states.\n */\n list(options?: { workflowName?: string; hasPendingApprovals?: boolean }): Promise<string[]>;\n\n /**\n * Find workflows waiting for a specific approval.\n */\n findByPendingApproval(approvalKey: string): Promise<string[]>;\n}\n\n/**\n * Options for the HITL orchestrator.\n */\nexport interface HITLOrchestratorOptions {\n /** Approval store for managing approval states */\n approvalStore: ApprovalStore;\n /** Workflow state store for persisting workflow state */\n workflowStateStore: WorkflowStateStore;\n /** Default expiration time for approvals (in milliseconds) */\n defaultExpirationMs?: number;\n /** Logger function */\n logger?: (message: string) => void;\n}\n\n/**\n * Result of executing a workflow that may pause for approval.\n * Uses unknown for error type since workflows add UnexpectedError to the union.\n */\nexport type HITLExecutionResult<T, E> =\n | { status: \"completed\"; result: Result<T, E | unknown> }\n | { status: \"paused\"; runId: string; pendingApprovals: string[]; reason?: string }\n | { status: \"resumed\"; runId: string; result: Result<T, E | unknown> };\n\n/**\n * Poller configuration.\n */\nexport interface PollerOptions {\n /** Polling interval in milliseconds */\n intervalMs: number;\n /** Maximum number of polls (undefined = unlimited) */\n maxPolls?: number;\n /** Timeout for the entire polling operation */\n timeoutMs?: number;\n /** Callback when polling starts */\n onPollStart?: () => void;\n /** Callback when a poll completes */\n onPollComplete?: (result: ApprovalStatus) => void;\n}\n\n// =============================================================================\n// In-Memory Stores (for development/testing)\n// =============================================================================\n\n/**\n * Create an in-memory approval store for development/testing.\n */\nexport function createMemoryApprovalStore(): ApprovalStore {\n const approvals = new Map<string, ApprovalStatus & { metadata?: Record<string, unknown>; expiresAt?: number }>();\n\n return {\n async getApproval(key: string): Promise<ApprovalStatus> {\n const approval = approvals.get(key);\n if (!approval) {\n return { status: \"pending\" };\n }\n\n // Check expiration\n if (approval.expiresAt && Date.now() > approval.expiresAt) {\n return { status: \"expired\", expiredAt: approval.expiresAt };\n }\n\n return approval;\n },\n\n async createApproval(\n key: string,\n options?: { metadata?: Record<string, unknown>; expiresAt?: number }\n ): Promise<void> {\n approvals.set(key, {\n status: \"pending\",\n metadata: options?.metadata,\n expiresAt: options?.expiresAt,\n });\n },\n\n async grantApproval<T>(\n key: string,\n value: T,\n options?: { approvedBy?: string }\n ): Promise<void> {\n approvals.set(key, {\n status: \"approved\",\n value,\n approvedBy: options?.approvedBy,\n approvedAt: Date.now(),\n });\n },\n\n async rejectApproval(\n key: string,\n reason: string,\n options?: { rejectedBy?: string }\n ): Promise<void> {\n approvals.set(key, {\n status: \"rejected\",\n reason,\n rejectedBy: options?.rejectedBy,\n rejectedAt: Date.now(),\n });\n },\n\n async cancelApproval(key: string): Promise<void> {\n approvals.delete(key);\n },\n\n async listPending(options?: { prefix?: string }): Promise<string[]> {\n const pending: string[] = [];\n for (const [key, approval] of approvals) {\n if (approval.status === \"pending\") {\n if (!options?.prefix || key.startsWith(options.prefix)) {\n pending.push(key);\n }\n }\n }\n return pending;\n },\n };\n}\n\n/**\n * Create an in-memory workflow state store for development/testing.\n */\nexport function createMemoryWorkflowStateStore(): WorkflowStateStore {\n const states = new Map<string, SavedWorkflowState>();\n\n return {\n async save(state: SavedWorkflowState): Promise<void> {\n states.set(state.runId, { ...state, updatedAt: Date.now() });\n },\n\n async load(runId: string): Promise<SavedWorkflowState | undefined> {\n return states.get(runId);\n },\n\n async delete(runId: string): Promise<void> {\n states.delete(runId);\n },\n\n async list(options?: { workflowName?: string; hasPendingApprovals?: boolean }): Promise<string[]> {\n const results: string[] = [];\n for (const [runId, state] of states) {\n if (options?.workflowName && state.workflowName !== options.workflowName) {\n continue;\n }\n if (options?.hasPendingApprovals !== undefined) {\n const hasPending = state.pendingApprovals.length > 0;\n if (options.hasPendingApprovals !== hasPending) {\n continue;\n }\n }\n results.push(runId);\n }\n return results;\n },\n\n async findByPendingApproval(approvalKey: string): Promise<string[]> {\n const results: string[] = [];\n for (const [runId, state] of states) {\n if (state.pendingApprovals.includes(approvalKey)) {\n results.push(runId);\n }\n }\n return results;\n },\n };\n}\n\n// =============================================================================\n// HITL Orchestrator\n// =============================================================================\n\n/**\n * HITL orchestrator interface.\n */\nexport interface HITLOrchestrator {\n /**\n * Execute a workflow that may pause for approvals.\n * If the workflow pauses, state is automatically saved.\n * \n * The workflowFactory receives options including onEvent handler which MUST be\n * passed to createWorkflow for HITL tracking to work.\n */\n execute<T, E, TInput>(\n workflowName: string,\n workflowFactory: (options: HITLWorkflowFactoryOptions) => Workflow<E, unknown>,\n workflowFn: (step: unknown, deps: unknown, input: TInput) => Promise<T>,\n input: TInput,\n options?: { runId?: string; metadata?: Record<string, unknown> }\n ): Promise<HITLExecutionResult<T, E>>;\n\n /**\n * Resume a paused workflow after approvals have been granted.\n */\n resume<T, E, TInput>(\n runId: string,\n workflowFactory: (options: HITLWorkflowFactoryOptions) => Workflow<E, unknown>,\n workflowFn: (step: unknown, deps: unknown, input: TInput) => Promise<T>\n ): Promise<HITLExecutionResult<T, E>>;\n\n /**\n * Grant an approval and automatically resume any waiting workflows.\n */\n grantApproval<T>(\n approvalKey: string,\n value: T,\n options?: { approvedBy?: string; autoResume?: boolean }\n ): Promise<{ grantedAt: number; resumedWorkflows: string[] }>;\n\n /**\n * Reject an approval.\n */\n rejectApproval(\n approvalKey: string,\n reason: string,\n options?: { rejectedBy?: string }\n ): Promise<void>;\n\n /**\n * Poll for an approval to be granted.\n */\n pollApproval<T>(\n approvalKey: string,\n options?: PollerOptions\n ): Promise<ApprovalStatus<T>>;\n\n /**\n * Get the status of a workflow run.\n */\n getWorkflowStatus(runId: string): Promise<SavedWorkflowState | undefined>;\n\n /**\n * List all pending workflows.\n */\n listPendingWorkflows(workflowName?: string): Promise<string[]>;\n\n /**\n * Clean up completed workflows older than the specified age.\n */\n cleanup(maxAgeMs: number): Promise<number>;\n}\n\n/**\n * Create a HITL orchestrator for managing approval workflows.\n *\n * @example\n * ```typescript\n * const orchestrator = createHITLOrchestrator({\n * approvalStore: createMemoryApprovalStore(),\n * workflowStateStore: createMemoryWorkflowStateStore(),\n * });\n *\n * // Execute workflow - IMPORTANT: pass onEvent to createWorkflow!\n * const result = await orchestrator.execute(\n * 'order-approval',\n * ({ resumeState, onEvent }) => createWorkflow(deps, { resumeState, onEvent }),\n * async (step, deps, input) => {\n * const order = await step(() => deps.createOrder(input));\n * const approval = await step(() => deps.requireApproval(order.id), { key: `approval:${order.id}` });\n * await step(() => deps.processOrder(order.id));\n * return { orderId: order.id, approvedBy: approval.approvedBy };\n * },\n * { items: [...], total: 100 }\n * );\n *\n * if (result.status === 'paused') {\n * console.log(`Workflow paused, waiting for: ${result.pendingApprovals}`);\n * }\n *\n * // Later, grant approval\n * await orchestrator.grantApproval(\n * `approval:${orderId}`,\n * { approvedBy: 'manager@example.com' },\n * { autoResume: true }\n * );\n * ```\n */\nexport function createHITLOrchestrator(options: HITLOrchestratorOptions): HITLOrchestrator {\n const {\n approvalStore,\n workflowStateStore,\n defaultExpirationMs = 7 * 24 * 60 * 60 * 1000, // 7 days\n logger = () => {},\n } = options;\n\n async function execute<T, E, TInput>(\n workflowName: string,\n workflowFactory: (options: HITLWorkflowFactoryOptions) => Workflow<E, unknown>,\n workflowFn: (step: unknown, deps: unknown, input: TInput) => Promise<T>,\n input: TInput,\n opts?: { runId?: string; metadata?: Record<string, unknown> }\n ): Promise<HITLExecutionResult<T, E>> {\n const runId = opts?.runId ?? crypto.randomUUID();\n const collector = createHITLCollector();\n\n // Create workflow with collector's event handler wired in\n const workflow = workflowFactory({\n onEvent: collector.handleEvent,\n });\n\n // Execute workflow - collector tracks pending approvals via events\n const result = await (workflow as Workflow<E, unknown>)(\n input,\n workflowFn as (step: unknown, deps: unknown, args: TInput) => Promise<T>\n );\n\n // Check for pending approvals\n const pendingApprovals = collector.getPendingApprovals().map((p) => p.stepKey);\n\n if (pendingApprovals.length > 0) {\n // Save state for later resumption\n const state: SavedWorkflowState = {\n runId,\n workflowName,\n resumeState: collector.getState(),\n pendingApprovals,\n input,\n metadata: opts?.metadata,\n startedAt: Date.now(),\n updatedAt: Date.now(),\n };\n\n await workflowStateStore.save(state);\n\n // Create approval requests in the store\n for (const key of pendingApprovals) {\n await approvalStore.createApproval(key, {\n metadata: { runId, workflowName },\n expiresAt: Date.now() + defaultExpirationMs,\n });\n }\n\n logger(`Workflow ${runId} paused, waiting for: ${pendingApprovals.join(\", \")}`);\n\n // Find the reason from the error if available\n let reason: string | undefined;\n if (!result.ok && isPendingApproval(result.error)) {\n reason = result.error.reason;\n }\n\n return {\n status: \"paused\",\n runId,\n pendingApprovals,\n reason,\n };\n }\n\n // Workflow completed\n logger(`Workflow ${runId} completed`);\n return { status: \"completed\", result };\n }\n\n async function resume<T, E, TInput>(\n runId: string,\n workflowFactory: (options: HITLWorkflowFactoryOptions) => Workflow<E, unknown>,\n workflowFn: (step: unknown, deps: unknown, input: TInput) => Promise<T>\n ): Promise<HITLExecutionResult<T, E>> {\n const savedState = await workflowStateStore.load(runId);\n if (!savedState) {\n throw new Error(`Workflow run not found: ${runId}`);\n }\n\n // Check if all approvals have been granted\n for (const key of savedState.pendingApprovals) {\n const status = await approvalStore.getApproval(key);\n if (status.status === \"pending\") {\n return {\n status: \"paused\",\n runId,\n pendingApprovals: savedState.pendingApprovals,\n };\n }\n if (status.status === \"rejected\") {\n // Inject rejection as error\n // This would need custom handling\n }\n if (status.status === \"expired\") {\n throw new Error(`Approval ${key} has expired`);\n }\n }\n\n // Inject approved values into resume state\n let resumeState = savedState.resumeState;\n for (const key of savedState.pendingApprovals) {\n const status = await approvalStore.getApproval(key);\n if (status.status === \"approved\") {\n resumeState = injectApproval(resumeState, {\n stepKey: key,\n value: status.value,\n });\n }\n }\n\n // Create collector for tracking any new pending approvals\n const collector = createHITLCollector();\n\n // Create workflow with resume state and event handler\n const workflow = workflowFactory({\n resumeState,\n onEvent: collector.handleEvent,\n });\n\n // Execute workflow\n const result = await (workflow as Workflow<E, unknown>)(\n savedState.input as TInput,\n workflowFn as (step: unknown, deps: unknown, args: TInput) => Promise<T>\n );\n\n // Check for NEW pending approvals (workflow paused again at a different step)\n const newPendingApprovals = collector.getPendingApprovals().map((p) => p.stepKey);\n\n if (newPendingApprovals.length > 0) {\n // Update saved state with new pending approvals\n const updatedState: SavedWorkflowState = {\n ...savedState,\n resumeState: collector.getState(),\n pendingApprovals: newPendingApprovals,\n updatedAt: Date.now(),\n };\n\n await workflowStateStore.save(updatedState);\n\n // Create approval requests for new approvals\n for (const key of newPendingApprovals) {\n await approvalStore.createApproval(key, {\n metadata: { runId, workflowName: savedState.workflowName },\n expiresAt: Date.now() + defaultExpirationMs,\n });\n }\n\n logger(`Workflow ${runId} paused again, waiting for: ${newPendingApprovals.join(\", \")}`);\n\n // Find the reason from the error if available\n let reason: string | undefined;\n if (!result.ok && isPendingApproval(result.error)) {\n reason = result.error.reason;\n }\n\n return {\n status: \"paused\",\n runId,\n pendingApprovals: newPendingApprovals,\n reason,\n };\n }\n\n // Workflow completed - clean up saved state\n await workflowStateStore.delete(runId);\n\n logger(`Workflow ${runId} resumed and completed`);\n return { status: \"resumed\", runId, result };\n }\n\n async function grantApprovalFn<T>(\n approvalKey: string,\n value: T,\n opts?: { approvedBy?: string; autoResume?: boolean }\n ): Promise<{ grantedAt: number; resumedWorkflows: string[] }> {\n await approvalStore.grantApproval(approvalKey, value, {\n approvedBy: opts?.approvedBy,\n });\n\n const grantedAt = Date.now();\n const resumedWorkflows: string[] = [];\n\n if (opts?.autoResume !== false) {\n // Find workflows waiting for this approval\n const waitingWorkflows = await workflowStateStore.findByPendingApproval(approvalKey);\n\n for (const runId of waitingWorkflows) {\n // Note: Auto-resume would need the workflow factory, which we don't have here\n // This is a simplified implementation\n resumedWorkflows.push(runId);\n }\n }\n\n logger(`Approval ${approvalKey} granted by ${opts?.approvedBy ?? \"unknown\"}`);\n return { grantedAt, resumedWorkflows };\n }\n\n async function rejectApprovalFn(\n approvalKey: string,\n reason: string,\n opts?: { rejectedBy?: string }\n ): Promise<void> {\n await approvalStore.rejectApproval(approvalKey, reason, {\n rejectedBy: opts?.rejectedBy,\n });\n logger(`Approval ${approvalKey} rejected: ${reason}`);\n }\n\n async function pollApproval<T>(\n approvalKey: string,\n opts?: PollerOptions\n ): Promise<ApprovalStatus<T>> {\n const {\n intervalMs = 1000,\n maxPolls,\n timeoutMs,\n onPollStart,\n onPollComplete,\n } = opts ?? {};\n\n const startTime = Date.now();\n let pollCount = 0;\n\n while (true) {\n onPollStart?.();\n const status = (await approvalStore.getApproval(approvalKey)) as ApprovalStatus<T>;\n onPollComplete?.(status);\n\n if (status.status !== \"pending\") {\n return status;\n }\n\n pollCount++;\n if (maxPolls !== undefined && pollCount >= maxPolls) {\n return { status: \"pending\" };\n }\n\n if (timeoutMs !== undefined && Date.now() - startTime >= timeoutMs) {\n return { status: \"pending\" };\n }\n\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n }\n\n async function getWorkflowStatus(runId: string): Promise<SavedWorkflowState | undefined> {\n return workflowStateStore.load(runId);\n }\n\n async function listPendingWorkflows(workflowName?: string): Promise<string[]> {\n return workflowStateStore.list({\n workflowName,\n hasPendingApprovals: true,\n });\n }\n\n async function cleanup(maxAgeMs: number): Promise<number> {\n const allWorkflows = await workflowStateStore.list();\n let cleaned = 0;\n const cutoff = Date.now() - maxAgeMs;\n\n for (const runId of allWorkflows) {\n const state = await workflowStateStore.load(runId);\n if (state && state.updatedAt < cutoff && state.pendingApprovals.length === 0) {\n await workflowStateStore.delete(runId);\n cleaned++;\n }\n }\n\n logger(`Cleaned up ${cleaned} old workflow states`);\n return cleaned;\n }\n\n return {\n execute,\n resume,\n grantApproval: grantApprovalFn,\n rejectApproval: rejectApprovalFn,\n pollApproval,\n getWorkflowStatus,\n listPendingWorkflows,\n cleanup,\n };\n}\n\n// =============================================================================\n// Webhook Handlers for Approvals\n// =============================================================================\n\n/**\n * Approval webhook request body.\n */\nexport interface ApprovalWebhookRequest {\n /** Approval key */\n key: string;\n /** Action: approve, reject, or cancel */\n action: \"approve\" | \"reject\" | \"cancel\";\n /** Value to inject (for approve) */\n value?: unknown;\n /** Reason (for reject) */\n reason?: string;\n /** Who performed this action */\n actorId?: string;\n}\n\n/**\n * Approval webhook response.\n */\nexport interface ApprovalWebhookResponse {\n success: boolean;\n message: string;\n data?: {\n key: string;\n action: string;\n timestamp: number;\n };\n}\n\n/**\n * Create a webhook handler for approval actions.\n *\n * @example\n * ```typescript\n * const handleApproval = createApprovalWebhookHandler(approvalStore);\n *\n * // Express\n * app.post('/api/approvals', async (req, res) => {\n * const result = await handleApproval(req.body);\n * res.json(result);\n * });\n * ```\n */\nexport function createApprovalWebhookHandler(\n store: ApprovalStore\n): (request: ApprovalWebhookRequest) => Promise<ApprovalWebhookResponse> {\n return async (request: ApprovalWebhookRequest): Promise<ApprovalWebhookResponse> => {\n const { key, action, value, reason, actorId } = request;\n\n try {\n switch (action) {\n case \"approve\":\n await store.grantApproval(key, value, { approvedBy: actorId });\n return {\n success: true,\n message: `Approval ${key} granted`,\n data: { key, action, timestamp: Date.now() },\n };\n\n case \"reject\":\n if (!reason) {\n return { success: false, message: \"Reason is required for rejection\" };\n }\n await store.rejectApproval(key, reason, { rejectedBy: actorId });\n return {\n success: true,\n message: `Approval ${key} rejected`,\n data: { key, action, timestamp: Date.now() },\n };\n\n case \"cancel\":\n await store.cancelApproval(key);\n return {\n success: true,\n message: `Approval ${key} cancelled`,\n data: { key, action, timestamp: Date.now() },\n };\n\n default:\n return { success: false, message: `Unknown action: ${action}` };\n }\n } catch (error) {\n return {\n success: false,\n message: error instanceof Error ? error.message : String(error),\n };\n }\n };\n}\n\n// =============================================================================\n// Approval Status Checker\n// =============================================================================\n\n/**\n * Create an approval checker function for use in approval steps.\n * This wraps the approval store with the standard checkApproval interface.\n *\n * @example\n * ```typescript\n * const checkApproval = createApprovalChecker(approvalStore);\n *\n * const requireManagerApproval = createApprovalStep<{ approvedBy: string }>({\n * key: 'manager-approval',\n * checkApproval: checkApproval('manager-approval'),\n * pendingReason: 'Waiting for manager approval',\n * });\n * ```\n */\nexport function createApprovalChecker<T>(store: ApprovalStore) {\n return (key: string) => async (): Promise<\n | { status: \"pending\" }\n | { status: \"approved\"; value: T }\n | { status: \"rejected\"; reason: string }\n > => {\n const status = await store.getApproval(key);\n\n switch (status.status) {\n case \"pending\":\n return { status: \"pending\" };\n case \"approved\":\n return { status: \"approved\", value: status.value as T };\n case \"rejected\":\n return { status: \"rejected\", reason: status.reason };\n case \"expired\":\n return { status: \"rejected\", reason: \"Approval request expired\" };\n default:\n return { status: \"pending\" };\n }\n };\n}\n","/**\n * @jagreehal/workflow/testing\n *\n * Deterministic Workflow Testing Harness.\n * Provides tools for scripting step outcomes and asserting workflow behavior.\n */\n\nimport type { Result, AsyncResult, StepOptions, WorkflowEvent } from \"./core\";\nimport { ok, err } from \"./core\";\nimport type { AnyResultFn, ErrorsOfDeps } from \"./workflow\";\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\n/**\n * Internal early exit marker used by the testing harness.\n * @internal\n */\ninterface TestEarlyExit<E = unknown> {\n __earlyExit: true;\n error: E;\n}\n\n/**\n * Type guard for test early exit objects.\n * @internal\n */\nfunction isTestEarlyExit(e: unknown): e is TestEarlyExit {\n return (\n typeof e === \"object\" &&\n e !== null &&\n \"__earlyExit\" in e &&\n (e as TestEarlyExit).__earlyExit === true\n );\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * A scripted outcome for a step.\n */\nexport type ScriptedOutcome<T = unknown, E = unknown> =\n | { type: \"ok\"; value: T }\n | { type: \"err\"; error: E }\n | { type: \"throw\"; error: unknown };\n\n/**\n * Step invocation record.\n */\nexport interface StepInvocation {\n /** Step name */\n name?: string;\n /** Step key */\n key?: string;\n /** Invocation order (0-indexed) */\n order: number;\n /** Timestamp when step was invoked */\n timestamp: number;\n /** Duration in milliseconds */\n durationMs?: number;\n /** Result of the step */\n result?: Result<unknown, unknown>;\n /** Whether the step was from cache */\n cached?: boolean;\n}\n\n/**\n * Assertion result.\n */\nexport interface AssertionResult {\n passed: boolean;\n message: string;\n expected?: unknown;\n actual?: unknown;\n}\n\n/**\n * Test harness options.\n */\nexport interface TestHarnessOptions {\n /** Whether to record step invocations */\n recordInvocations?: boolean;\n /** Custom clock for deterministic timing */\n clock?: () => number;\n}\n\n/**\n * Mock step function that returns scripted outcomes.\n */\nexport type MockStep<E> = {\n /** Execute with a Result-returning operation */\n <T, StepE extends E>(\n operation: () => Result<T, StepE> | AsyncResult<T, StepE>,\n options?: StepOptions | string\n ): Promise<T>;\n\n /** Execute with a direct Result */\n <T, StepE extends E>(\n result: Result<T, StepE> | AsyncResult<T, StepE>,\n options?: StepOptions | string\n ): Promise<T>;\n\n /** step.try for catching throws */\n try: <T, Err extends E>(\n operation: () => T | Promise<T>,\n options: { error: Err; name?: string; key?: string } | { onError: (cause: unknown) => Err; name?: string; key?: string }\n ) => Promise<T>;\n};\n\n// =============================================================================\n// Test Harness\n// =============================================================================\n\n/**\n * Workflow test harness interface.\n */\nexport interface WorkflowHarness<E, Deps> {\n /**\n * Script step outcomes in order.\n * Each outcome will be returned for the corresponding step invocation.\n */\n script(outcomes: ScriptedOutcome[]): void;\n\n /**\n * Script a specific step outcome by name or key.\n */\n scriptStep(nameOrKey: string, outcome: ScriptedOutcome): void;\n\n /**\n * Run the workflow with scripted outcomes.\n */\n run<T>(\n fn: (step: MockStep<E>, deps: Deps) => Promise<T>\n ): Promise<Result<T, E | unknown>>;\n\n /**\n * Run the workflow with input.\n */\n runWithInput<T, TInput>(\n input: TInput,\n fn: (step: MockStep<E>, deps: Deps, input: TInput) => Promise<T>\n ): Promise<Result<T, E | unknown>>;\n\n /**\n * Get recorded step invocations.\n */\n getInvocations(): StepInvocation[];\n\n /**\n * Assert that steps were invoked in order.\n */\n assertSteps(expectedNames: string[]): AssertionResult;\n\n /**\n * Assert that a step was invoked with specific options.\n */\n assertStepCalled(nameOrKey: string): AssertionResult;\n\n /**\n * Assert that a step was NOT invoked.\n */\n assertStepNotCalled(nameOrKey: string): AssertionResult;\n\n /**\n * Assert the workflow result.\n */\n assertResult<T>(result: Result<T, unknown>, expected: Result<T, unknown>): AssertionResult;\n\n /**\n * Clear all state for a new test.\n */\n reset(): void;\n}\n\n/**\n * Create a test harness for a workflow.\n *\n * @example\n * ```typescript\n * const harness = createWorkflowHarness({ fetchUser, chargeCard });\n *\n * // Script step outcomes\n * harness.script([\n * { type: 'ok', value: { id: '1', name: 'Alice' } },\n * { type: 'ok', value: { transactionId: 'tx_123' } },\n * ]);\n *\n * // Run the workflow\n * const result = await harness.run(async (step, { fetchUser, chargeCard }) => {\n * const user = await step(() => fetchUser('1'), 'fetch-user');\n * const charge = await step(() => chargeCard(100), 'charge-card');\n * return { user, charge };\n * });\n *\n * // Assert\n * expect(result.ok).toBe(true);\n * harness.assertSteps(['fetch-user', 'charge-card']);\n * ```\n */\nexport function createWorkflowHarness<\n Deps extends Record<string, AnyResultFn>\n>(\n deps: Deps,\n options: TestHarnessOptions = {}\n): WorkflowHarness<ErrorsOfDeps<Deps>, Deps> {\n type E = ErrorsOfDeps<Deps>;\n\n const { recordInvocations = true, clock = Date.now } = options;\n\n let scriptedOutcomes: ScriptedOutcome[] = [];\n const namedOutcomes = new Map<string, ScriptedOutcome>();\n let invocationIndex = 0;\n let invocations: StepInvocation[] = [];\n\n function script(outcomes: ScriptedOutcome[]): void {\n scriptedOutcomes = [...outcomes];\n invocationIndex = 0; // Reset index when script is called\n namedOutcomes.clear(); // Clear named overrides for deterministic behavior\n }\n\n function scriptStep(nameOrKey: string, outcome: ScriptedOutcome): void {\n namedOutcomes.set(nameOrKey, outcome);\n }\n\n function getNextOutcome(nameOrKey?: string): ScriptedOutcome | undefined {\n // Check named outcomes first\n if (nameOrKey && namedOutcomes.has(nameOrKey)) {\n return namedOutcomes.get(nameOrKey);\n }\n\n // Fall back to sequential outcomes\n if (invocationIndex < scriptedOutcomes.length) {\n return scriptedOutcomes[invocationIndex++];\n }\n\n return undefined;\n }\n\n function createMockStep(): MockStep<E> {\n const mockStep = async <T, StepE extends E>(\n operationOrResult:\n | (() => Result<T, StepE> | AsyncResult<T, StepE>)\n | Result<T, StepE>\n | AsyncResult<T, StepE>,\n stepOptions?: StepOptions | string\n ): Promise<T> => {\n const opts = typeof stepOptions === \"string\" ? { name: stepOptions } : (stepOptions ?? {});\n const nameOrKey = opts.name ?? opts.key;\n const startTime = clock();\n\n // Record invocation\n const invocation: StepInvocation = {\n name: opts.name,\n key: opts.key,\n order: invocations.length,\n timestamp: startTime,\n };\n\n if (recordInvocations) {\n invocations.push(invocation);\n }\n\n // Get scripted outcome\n const outcome = getNextOutcome(nameOrKey);\n\n if (outcome) {\n invocation.durationMs = clock() - startTime;\n\n switch (outcome.type) {\n case \"ok\":\n invocation.result = ok(outcome.value);\n return outcome.value as T;\n\n case \"err\":\n invocation.result = err(outcome.error);\n throw { __earlyExit: true, error: outcome.error };\n\n case \"throw\":\n throw outcome.error;\n }\n }\n\n // No scripted outcome - execute the real operation\n const result =\n typeof operationOrResult === \"function\"\n ? await operationOrResult()\n : await operationOrResult;\n\n invocation.durationMs = clock() - startTime;\n invocation.result = result;\n\n if (!result.ok) {\n throw { __earlyExit: true, error: result.error };\n }\n\n return result.value;\n };\n\n mockStep.try = async <T, Err extends E>(\n operation: () => T | Promise<T>,\n opts:\n | { error: Err; name?: string; key?: string }\n | { onError: (cause: unknown) => Err; name?: string; key?: string }\n ): Promise<T> => {\n const nameOrKey = opts.name ?? opts.key;\n const startTime = clock();\n\n const invocation: StepInvocation = {\n name: opts.name,\n key: opts.key,\n order: invocations.length,\n timestamp: startTime,\n };\n\n if (recordInvocations) {\n invocations.push(invocation);\n }\n\n // Get scripted outcome\n const outcome = getNextOutcome(nameOrKey);\n\n if (outcome) {\n invocation.durationMs = clock() - startTime;\n\n switch (outcome.type) {\n case \"ok\":\n invocation.result = ok(outcome.value);\n return outcome.value as T;\n\n case \"err\":\n invocation.result = err(outcome.error);\n throw { __earlyExit: true, error: outcome.error };\n\n case \"throw\":\n throw outcome.error;\n }\n }\n\n // No scripted outcome - execute the real operation\n try {\n const value = await operation();\n invocation.durationMs = clock() - startTime;\n invocation.result = ok(value);\n return value;\n } catch (error) {\n invocation.durationMs = clock() - startTime;\n const mappedError = \"error\" in opts ? opts.error : opts.onError(error);\n invocation.result = err(mappedError);\n throw { __earlyExit: true, error: mappedError };\n }\n };\n\n return mockStep as MockStep<E>;\n }\n\n async function run<T>(\n fn: (step: MockStep<E>, deps: Deps) => Promise<T>\n ): Promise<Result<T, E | unknown>> {\n const mockStep = createMockStep();\n\n try {\n const value = await fn(mockStep, deps);\n return ok(value);\n } catch (error) {\n if (isTestEarlyExit(error)) {\n return err(error.error);\n }\n return err({ type: \"UNEXPECTED_ERROR\", cause: error });\n }\n }\n\n async function runWithInput<T, TInput>(\n input: TInput,\n fn: (step: MockStep<E>, deps: Deps, input: TInput) => Promise<T>\n ): Promise<Result<T, E | unknown>> {\n const mockStep = createMockStep();\n\n try {\n const value = await fn(mockStep, deps, input);\n return ok(value);\n } catch (error) {\n if (isTestEarlyExit(error)) {\n return err(error.error);\n }\n return err({ type: \"UNEXPECTED_ERROR\", cause: error });\n }\n }\n\n function getInvocations(): StepInvocation[] {\n return [...invocations];\n }\n\n function assertSteps(expectedNames: string[]): AssertionResult {\n const actualNames = invocations\n .map((inv) => inv.name ?? inv.key ?? \"unnamed\")\n .filter((n) => n !== \"unnamed\");\n\n const passed = JSON.stringify(actualNames) === JSON.stringify(expectedNames);\n\n return {\n passed,\n message: passed\n ? `Steps invoked in order: ${expectedNames.join(\", \")}`\n : `Expected steps [${expectedNames.join(\", \")}] but got [${actualNames.join(\", \")}]`,\n expected: expectedNames,\n actual: actualNames,\n };\n }\n\n function assertStepCalled(nameOrKey: string): AssertionResult {\n const found = invocations.some(\n (inv) => inv.name === nameOrKey || inv.key === nameOrKey\n );\n\n return {\n passed: found,\n message: found\n ? `Step \"${nameOrKey}\" was invoked`\n : `Step \"${nameOrKey}\" was NOT invoked`,\n expected: nameOrKey,\n actual: found,\n };\n }\n\n function assertStepNotCalled(nameOrKey: string): AssertionResult {\n const found = invocations.some(\n (inv) => inv.name === nameOrKey || inv.key === nameOrKey\n );\n\n return {\n passed: !found,\n message: !found\n ? `Step \"${nameOrKey}\" was correctly NOT invoked`\n : `Step \"${nameOrKey}\" was invoked but should not have been`,\n expected: \"not called\",\n actual: found ? \"called\" : \"not called\",\n };\n }\n\n function assertResult<T>(\n result: Result<T, unknown>,\n expected: Result<T, unknown>\n ): AssertionResult {\n const passed =\n result.ok === expected.ok &&\n (result.ok\n ? JSON.stringify(result.value) === JSON.stringify((expected as { ok: true; value: T }).value)\n : JSON.stringify(result.error) === JSON.stringify((expected as { ok: false; error: unknown }).error));\n\n return {\n passed,\n message: passed\n ? `Result matches expected`\n : `Result does not match expected`,\n expected,\n actual: result,\n };\n }\n\n function reset(): void {\n scriptedOutcomes = [];\n namedOutcomes.clear();\n invocationIndex = 0;\n invocations = [];\n }\n\n return {\n script,\n scriptStep,\n run,\n runWithInput,\n getInvocations,\n assertSteps,\n assertStepCalled,\n assertStepNotCalled,\n assertResult,\n reset,\n };\n}\n\n// =============================================================================\n// Mock Factories\n// =============================================================================\n\n/**\n * Create a mock Result-returning function.\n *\n * @example\n * ```typescript\n * const fetchUser = createMockFn<User, 'NOT_FOUND'>();\n *\n * fetchUser.returns(ok({ id: '1', name: 'Alice' }));\n * // or\n * fetchUser.returnsOnce(ok({ id: '1', name: 'Alice' }));\n * fetchUser.returnsOnce(err('NOT_FOUND'));\n * ```\n */\nexport function createMockFn<T, E>(): MockFunction<T, E> {\n let defaultReturn: Result<T, E> | undefined;\n const returnQueue: Result<T, E>[] = [];\n const calls: unknown[][] = [];\n\n const fn = ((...args: unknown[]) => {\n calls.push(args);\n\n if (returnQueue.length > 0) {\n return Promise.resolve(returnQueue.shift()!);\n }\n\n if (defaultReturn) {\n return Promise.resolve(defaultReturn);\n }\n\n throw new Error(\"Mock function called without configured return value\");\n }) as MockFunction<T, E>;\n\n fn.returns = (result: Result<T, E>) => {\n defaultReturn = result;\n return fn;\n };\n\n fn.returnsOnce = (result: Result<T, E>) => {\n returnQueue.push(result);\n return fn;\n };\n\n fn.getCalls = () => [...calls];\n\n fn.getCallCount = () => calls.length;\n\n fn.reset = () => {\n defaultReturn = undefined;\n returnQueue.length = 0;\n calls.length = 0;\n };\n\n return fn;\n}\n\n/**\n * Mock function interface.\n */\nexport interface MockFunction<T, E> {\n (...args: unknown[]): AsyncResult<T, E>;\n\n /** Set the default return value */\n returns(result: Result<T, E>): MockFunction<T, E>;\n\n /** Queue a return value for the next call */\n returnsOnce(result: Result<T, E>): MockFunction<T, E>;\n\n /** Get all call arguments */\n getCalls(): unknown[][];\n\n /** Get the number of times the function was called */\n getCallCount(): number;\n\n /** Reset the mock */\n reset(): void;\n}\n\n// =============================================================================\n// Snapshot Testing\n// =============================================================================\n\n/**\n * Workflow snapshot for comparison.\n */\nexport interface WorkflowSnapshot {\n /** Step invocations */\n invocations: StepInvocation[];\n /** Final result */\n result: Result<unknown, unknown>;\n /** Events emitted */\n events?: WorkflowEvent<unknown>[];\n /** Total duration */\n durationMs?: number;\n}\n\n/**\n * Create a snapshot of a workflow execution.\n */\nexport function createSnapshot(\n invocations: StepInvocation[],\n result: Result<unknown, unknown>,\n events?: WorkflowEvent<unknown>[]\n): WorkflowSnapshot {\n const totalDuration = invocations.reduce(\n (sum, inv) => sum + (inv.durationMs ?? 0),\n 0\n );\n\n return {\n invocations: invocations.map((inv) => ({\n ...inv,\n // Normalize timestamps for comparison\n timestamp: 0,\n })),\n result,\n events: events?.map((e) => ({\n ...e,\n ts: 0, // Normalize timestamps\n })),\n durationMs: totalDuration,\n };\n}\n\n/**\n * Compare two workflow snapshots.\n */\nexport function compareSnapshots(\n snapshot1: WorkflowSnapshot,\n snapshot2: WorkflowSnapshot\n): {\n equal: boolean;\n differences: string[];\n} {\n const differences: string[] = [];\n\n // Compare invocations count\n if (snapshot1.invocations.length !== snapshot2.invocations.length) {\n differences.push(\n `Invocation count: ${snapshot1.invocations.length} vs ${snapshot2.invocations.length}`\n );\n }\n\n // Compare each invocation\n const maxLen = Math.max(\n snapshot1.invocations.length,\n snapshot2.invocations.length\n );\n\n for (let i = 0; i < maxLen; i++) {\n const inv1 = snapshot1.invocations[i];\n const inv2 = snapshot2.invocations[i];\n\n if (!inv1) {\n differences.push(`Step ${i}: missing in first snapshot`);\n continue;\n }\n\n if (!inv2) {\n differences.push(`Step ${i}: missing in second snapshot`);\n continue;\n }\n\n if (inv1.name !== inv2.name) {\n differences.push(`Step ${i} name: \"${inv1.name}\" vs \"${inv2.name}\"`);\n }\n\n if (inv1.key !== inv2.key) {\n differences.push(`Step ${i} key: \"${inv1.key}\" vs \"${inv2.key}\"`);\n }\n\n // Compare results\n if (inv1.result?.ok !== inv2.result?.ok) {\n differences.push(\n `Step ${i} result: ${inv1.result?.ok ? \"ok\" : \"err\"} vs ${inv2.result?.ok ? \"ok\" : \"err\"}`\n );\n }\n }\n\n // Compare final result\n if (snapshot1.result.ok !== snapshot2.result.ok) {\n differences.push(\n `Final result: ${snapshot1.result.ok ? \"ok\" : \"err\"} vs ${snapshot2.result.ok ? \"ok\" : \"err\"}`\n );\n }\n\n return {\n equal: differences.length === 0,\n differences,\n };\n}\n\n// =============================================================================\n// Test Utilities\n// =============================================================================\n\n/**\n * Create a deterministic clock for testing.\n */\nexport function createTestClock(startTime = 0): {\n now: () => number;\n advance: (ms: number) => void;\n set: (time: number) => void;\n reset: () => void;\n} {\n let currentTime = startTime;\n\n return {\n now: () => currentTime,\n advance: (ms: number) => {\n currentTime += ms;\n },\n set: (time: number) => {\n currentTime = time;\n },\n reset: () => {\n currentTime = startTime;\n },\n };\n}\n\n/**\n * Helper to create ok outcomes.\n */\nexport function okOutcome<T>(value: T): ScriptedOutcome<T, never> {\n return { type: \"ok\", value };\n}\n\n/**\n * Helper to create err outcomes.\n */\nexport function errOutcome<E>(error: E): ScriptedOutcome<never, E> {\n return { type: \"err\", error };\n}\n\n/**\n * Helper to create throw outcomes.\n */\nexport function throwOutcome(error: unknown): ScriptedOutcome<never, never> {\n return { type: \"throw\", error };\n}\n"],"mappings":"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","names":["ok","value","err","error","options","isOk","r","isErr","isUnexpectedError","STEP_TIMEOUT_MARKER","isStepTimeoutError","getStepTimeoutMeta","EARLY_EXIT_SYMBOL","createEarlyExit","meta","isEarlyExit","MAPPER_EXCEPTION_SYMBOL","createMapperException","thrown","isMapperException","parseStepOptions","calculateRetryDelay","attempt","backoff","initialDelay","maxDelay","jitter","delay","jitterAmount","sleep","ms","resolve","TIMEOUT_SYMBOL","executeWithTimeout","operation","stepInfo","controller","timeoutError","timeoutId","timeoutPromise","_","reject","operationPromise","errorToThrow","DEFAULT_RETRY_CONFIG","run","fn","onError","onEvent","catchUnexpected","providedWorkflowId","context","workflowId","wrapMode","activeScopeStack","stepIdCounter","generateStepId","stepKey","emitEvent","event","stepId","i","scope","earlyExit","isEarlyExitE","e","wrapForStep","causeFromMeta","unexpectedFromFailure","failure","stepFn","operationOrResult","stepOptions","parsedOptions","stepName","retryConfig","timeoutConfig","hasEventListeners","overallStartTime","effectiveRetry","lastResult","attemptStartTime","result","durationMs","timeoutMeta","timeoutMs","totalDurationMs","mappedError","mapperError","unexpectedError","errorResult","wrappedError","opts","mapToError","startTime","mapped","name","scopeId","scopeEnded","emitScopeEnd","idx","s","scopeEntry","failureCause","UnwrapError","cause","unwrap","unwrapOr","defaultValue","unwrapOrElse","from","fromPromise","promise","tryAsync","fromNullable","onNull","map","mapError","match","handlers","andThen","tap","tapError","mapTry","transform","mapErrorTry","all","results","values","allAsync","settled","pendingCount","index","reason","allSettled","errors","partition","any","firstError","anyAsync","item","allSettledAsync","createStepCollector","steps","event","isStepComplete","isCachedErrorCause","cause","encodeCachedError","error","meta","originalCause","err","decodeCachedMeta","createWorkflow","deps","options","workflowExecutor","fnOrArgs","maybeFn","hasArgs","args","userFn","workflowId","context","emitEvent","startTs","startTime","resumeStateOption","cache","resumeState","key","entry","result","effectiveMeta","parseStepOptions","opts","createCachedStep","realStep","cachedStepFn","operationOrResult","stepOptions","name","cached","createEarlyExit","wrappedOp","value","ok","thrown","isEarlyExit","exit","operation","wrappedFn","step","strictOptions","run","normalOptions","durationMs","isPendingApproval","isApprovalRejected","pendingApproval","stepKey","createApprovalStep","injectApproval","state","newSteps","clearStep","hasPendingApproval","getPendingApprovals","pending","createHITLCollector","generateDecisionId","emitSkipped","ctx","options","decisionId","when","condition","operation","unless","whenOr","defaultValue","unlessOr","createConditionalHelpers","CircuitOpenError","options","isCircuitOpenError","error","DEFAULT_CONFIG","createCircuitBreaker","name","config","effectiveConfig","state","failures","lastFailureTime","lastSuccessTime","successCount","halfOpenSuccesses","cleanupFailures","now","f","transitionTo","newState","oldState","checkOpenToHalfOpen","handleSuccess","handleFailure","canExecute","elapsed","operation","_options","waitTime","result","err","circuitBreakerPresets","isSagaCompensationError","error","createSagaWorkflow","deps","options","fn","sagaId","startTime","compensations","emitEvent","event","runCompensations","_originalError","errors","compensationStartTime","i","comp","sagaContext","operation","stepOptions","result","createEarlyExit","opts","mapToError","value","thrown","mapped","durationMs","ok","originalError","isEarlyExit","compensationErrors","sagaError","err","runSaga","c","isRateLimitExceededError","error","isQueueFullError","createRateLimiter","name","config","maxPerSecond","strategy","maxTokens","tokens","lastRefill","refillRate","waitQueue","refill","now","tokensToAdd","tryConsume","tokensNeeded","waitForToken","resolve","check","waitTime","idx","operation","err","createConcurrencyLimiter","maxConcurrent","maxQueueSize","activeCount","queue","acquire","reject","release","operations","results","executing","i","index","promise","result","createCombinedLimiter","rate","concurrency","op","originalOp","rateLimiterPresets","isMigrationError","error","isVersionIncompatibleError","migrateState","state","targetVersion","migrations","currentState","currentVersion","err","ok","migration","cause","createVersionedStateLoader","config","version","strictVersioning","versionedState","result","createVersionedState","parseVersionedState","json","parsed","typedParsed","steps","stringifyVersionedState","createKeyRenameMigration","renames","newSteps","key","entry","newKey","createKeyRemoveMigration","keysToRemove","keysSet","createValueTransformMigration","transforms","transform","composeMigrations","createAutotelAdapter","config","serviceName","createStepSpans","recordMetrics","defaultAttributes","recordRetryEvents","markErrorsOnSpan","activeSpans","metrics","getSpanName","name","stepKey","handleEvent","event","stepId","span","createAutotelEventHandler","options","includeStepDetails","prefix","withAutotelTracing","traceFn","fn","attributes","spanName","ctx","key","value","isValidationError","defaultValidationErrorMapper","error","defaultUnexpectedErrorMapper","_error","createWebhookHandler","workflow","workflowFn","config","validateInput","mapResult","mapValidationError","mapUnexpectedError","beforeValidation","afterResponse","req","processedReq","validationResult","isOk","response","workflowResult","step","deps","createSimpleHandler","handler","result","createResultMapper","mappings","options","defaultStatus","successStatus","errorMap","mapping","toWebhookRequest","sendWebhookResponse","res","createExpressHandler","webhookReq","validationError","message","field","details","requireFields","fields","body","err","ok","composeValidators","validators","input","validator","createEventHandler","validatePayload","event","mergePolicies","policies","result","policy","createPolicyApplier","basePolicies","basePolicy","stepOptions","createPolicyBundle","name","retryPolicy","options","retryPolicies","attempts","delayMs","initialDelay","timeoutPolicy","timeoutPolicies","ms","seconds","error","servicePolicies","withPolicy","withPolicies","opts","conditionalPolicy","condition","elsePolicy","envPolicy","envPolicies","currentEnv","defaultPolicy","createPolicyRegistry","policyName","builder","key","serializeCause","cause","deserializeCause","serialized","error","serializeResult","result","deserializeResult","ok","err","serializeMeta","meta","deserializeMeta","serializeEntry","entry","deserializeEntry","serializeState","state","metadata","entries","key","deserializeState","steps","stringifyState","parseState","json","createMemoryCache","options","maxSize","ttl","cache","isExpired","timestamp","evictExpired","evictOldest","oldestKey","oldestTime","createFileCache","directory","extension","fs","keyToPath","safeKey","memoryCache","path","data","files","file","createKVCache","store","prefix","prefixKey","keys","createStatePersistence","runId","createHydratingCache","persistence","hydrated","formatDuration","ms","minutes","seconds","generateId","hasRealScopeNodes","nodes","node","detectParallelGroups","options","maxGapMs","stepsWithTiming","nonStepNodes","i","a","b","groups","currentGroup","step","groupEnd","s","groupedNodes","group","position","children","startTs","endTs","parallelNode","deriveGroupState","originalIndex","g","c","createIRBuilder","options","detectParallel","parallelDetection","workflowId","workflowStartTs","workflowState","workflowError","workflowDurationMs","activeSteps","scopeStack","decisionStack","currentNodes","createdAt","lastUpdatedAt","getStepId","event","generateId","addNode","node","decision","branch","firstBranch","handleEvent","id","active","handleScopeEvent","scope","deriveState","handleDecisionEvent","branchKey","existing","branches","b","children","c","getCurrentNodes","nodes","getIR","detectParallelGroups","reset","isStepNode","node","isParallelNode","node","isRaceNode","isDecisionNode","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","padEnd","str","width","visibleLen","stripAnsi","padding","horizontalLine","title","titleText","remainingWidth","leftPad","rightPad","asciiRenderer","ir","options","colors","defaultColorScheme","innerWidth","lines","workflowName","headerTitle","bold","childLines","renderNodes","line","status","footer","colorByState","formatDuration","nodes","depth","node","isStepNode","renderStepNode","isParallelNode","renderParallelNode","isRaceNode","renderRaceNode","isDecisionNode","renderDecisionNode","symbol","getColoredSymbol","name","nameColored","dim","inputStr","outputStr","timeoutInfo","indent","mode","i","child","prefix","nestedLines","winnerSuffix","condition","decisionValue","branchTaken","branch","branchSymbol","branchColor","branchLabel","colorize","branchCondition","getStyleDefinitions","nodeCounter","generateNodeId","prefix","resetNodeCounter","escapeMermaidText","text","escapeSubgraphName","mermaidRenderer","ir","options","lines","startId","prevNodeId","child","result","renderNode","endId","endShape","endClass","getStyleDefinitions","node","isStepNode","renderStepNode","isParallelNode","renderParallelNode","isRaceNode","renderRaceNode","isDecisionNode","renderDecisionNode","id","label","timing","formatDuration","ioInfo","inputStr","outputStr","retryInfo","timeoutStr","escapedLabel","stateClass","shape","subgraphId","forkId","joinId","name","childExitIds","exitId","c","decisionId","condition","decisionValue","decisionLabel","branchExitIds","takenBranchExitId","branch","branchId","branchLabelText","branchLabel","branchClass","edgeLabel","prevId","createVisualizer","options","workflowName","detectParallel","showTimings","showKeys","customColors","builder","createIRBuilder","updateCallbacks","ascii","asciiRenderer","mermaid","mermaidRenderer","renderOptions","defaultColorScheme","notifyUpdate","ir","callback","handleEvent","event","handleScopeEvent","handleDecisionEvent","getIR","render","renderAs","format","reset","onUpdate","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","createMemoryApprovalStore","approvals","key","approval","options","value","reason","pending","createMemoryWorkflowStateStore","states","state","runId","results","hasPending","approvalKey","createHITLOrchestrator","approvalStore","workflowStateStore","defaultExpirationMs","logger","execute","workflowName","workflowFactory","workflowFn","input","opts","collector","createHITLCollector","result","pendingApprovals","isPendingApproval","resume","savedState","status","resumeState","injectApproval","newPendingApprovals","p","updatedState","grantApprovalFn","grantedAt","resumedWorkflows","waitingWorkflows","rejectApprovalFn","pollApproval","intervalMs","maxPolls","timeoutMs","onPollStart","onPollComplete","startTime","pollCount","resolve","getWorkflowStatus","listPendingWorkflows","cleanup","maxAgeMs","allWorkflows","cleaned","cutoff","createApprovalWebhookHandler","store","request","action","actorId","error","createApprovalChecker","isTestEarlyExit","createWorkflowHarness","deps","options","recordInvocations","clock","scriptedOutcomes","namedOutcomes","invocationIndex","invocations","script","outcomes","scriptStep","nameOrKey","outcome","getNextOutcome","createMockStep","mockStep","operationOrResult","stepOptions","opts","startTime","invocation","ok","err","result","operation","value","error","mappedError","run","fn","runWithInput","input","getInvocations","assertSteps","expectedNames","actualNames","inv","n","passed","assertStepCalled","found","assertStepNotCalled","assertResult","expected","reset","createMockFn","defaultReturn","returnQueue","calls","args","createSnapshot","events","totalDuration","sum","e","compareSnapshots","snapshot1","snapshot2","differences","maxLen","i","inv1","inv2","createTestClock","currentTime","ms","time","okOutcome","errOutcome","throwOutcome"]}
1
+ {"version":3,"sources":["../src/core.ts","../src/workflow.ts","../src/conditional.ts","../src/circuit-breaker.ts","../src/saga.ts","../src/rate-limiter.ts","../src/versioning.ts","../src/autotel.ts","../src/webhook.ts","../src/policies.ts","../src/persistence.ts","../src/visualize/utils/timing.ts","../src/visualize/parallel-detector.ts","../src/visualize/ir-builder.ts","../src/visualize/types.ts","../src/visualize/renderers/colors.ts","../src/visualize/renderers/ascii.ts","../src/visualize/renderers/mermaid.ts","../src/visualize/index.ts","../src/devtools.ts","../src/hitl.ts","../src/testing.ts"],"sourcesContent":["/**\n * @jagreehal/workflow/core\n *\n * Core Result primitives and run() function.\n * Use this module for minimal bundle size when you don't need the full workflow capabilities\n * (like retries, timeout, or state persistence) provided by `createWorkflow`.\n *\n * This module provides:\n * 1. `Result` types for error handling without try/catch\n * 2. `run()` function for executing steps with standardized error management\n * 3. Utilities for transforming and combining Results\n */\n\n// =============================================================================\n// Core Result Types\n// =============================================================================\n\n/**\n * Represents a successful computation or a failed one.\n * Use this type to represent the outcome of an operation that might fail,\n * instead of throwing exceptions.\n *\n * @template T - The type of the success value\n * @template E - The type of the error value (defaults to unknown)\n * @template C - The type of the cause (defaults to unknown)\n */\nexport type Result<T, E = unknown, C = unknown> =\n | { ok: true; value: T }\n | { ok: false; error: E; cause?: C };\n\n/**\n * A Promise that resolves to a Result.\n * Use this for asynchronous operations that might fail.\n */\nexport type AsyncResult<T, E = unknown, C = unknown> = Promise<Result<T, E, C>>;\n\nexport type UnexpectedStepFailureCause =\n | {\n type: \"STEP_FAILURE\";\n origin: \"result\";\n error: unknown;\n cause?: unknown;\n }\n | {\n type: \"STEP_FAILURE\";\n origin: \"throw\";\n error: unknown;\n thrown: unknown;\n };\n\nexport type UnexpectedCause =\n | { type: \"UNCAUGHT_EXCEPTION\"; thrown: unknown }\n | UnexpectedStepFailureCause;\n\nexport type UnexpectedError = {\n type: \"UNEXPECTED_ERROR\";\n cause: UnexpectedCause;\n};\nexport type PromiseRejectedError = { type: \"PROMISE_REJECTED\"; cause: unknown };\n/** Cause type for promise rejections in async batch helpers */\nexport type PromiseRejectionCause = { type: \"PROMISE_REJECTION\"; reason: unknown };\nexport type EmptyInputError = { type: \"EMPTY_INPUT\"; message: string };\nexport type MaybeAsyncResult<T, E, C = unknown> = Result<T, E, C> | Promise<Result<T, E, C>>;\n\n// =============================================================================\n// Result Constructors\n// =============================================================================\n\n/**\n * Creates a successful Result.\n * Use this when an operation completes successfully.\n *\n * @param value - The success value to wrap\n * @returns A Result object with `{ ok: true, value }`\n *\n * @example\n * ```typescript\n * function divide(a: number, b: number): Result<number, string> {\n * if (b === 0) return err(\"Division by zero\");\n * return ok(a / b);\n * }\n * ```\n */\nexport const ok = <T>(value: T): Result<T, never, never> => ({ ok: true, value });\n\n/**\n * Creates a failed Result.\n * Use this when an operation fails.\n *\n * @param error - The error value describing what went wrong (e.g., error code, object)\n * @param options - Optional context about the failure\n * @param options.cause - The underlying cause of the error (e.g., a caught exception)\n * @returns A Result object with `{ ok: false, error }` (and optional cause)\n *\n * @example\n * ```typescript\n * // Simple error\n * const r1 = err(\"NOT_FOUND\");\n *\n * // Error with cause (useful for wrapping exceptions)\n * try {\n * // ... unsafe code\n * } catch (e) {\n * return err(\"PROCESSING_FAILED\", { cause: e });\n * }\n * ```\n */\nexport const err = <E, C = unknown>(\n error: E,\n options?: { cause?: C }\n): Result<never, E, C> => ({\n ok: false,\n error,\n ...(options?.cause !== undefined ? { cause: options.cause } : {}),\n});\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Checks if a Result is successful.\n * Use this to narrow the type of a Result to the success case.\n *\n * @param r - The Result to check\n * @returns `true` if successful, allowing access to `r.value`\n *\n * @example\n * ```typescript\n * const r = someOperation();\n * if (isOk(r)) {\n * // Use r.value (Type is T)\n * processValue(r.value);\n * } else {\n * // Handle r.error (Type is E)\n * handleError(r.error);\n * }\n * ```\n */\nexport const isOk = <T, E, C>(r: Result<T, E, C>): r is { ok: true; value: T } =>\n r.ok;\n\n/**\n * Checks if a Result is a failure.\n * Use this to narrow the type of a Result to the error case.\n *\n * @param r - The Result to check\n * @returns `true` if failed, allowing access to `r.error` and `r.cause`\n *\n * @example\n * ```typescript\n * if (isErr(r)) {\n * // Handle error case early\n * return;\n * }\n * // Proceed with success case\n * ```\n */\nexport const isErr = <T, E, C>(\n r: Result<T, E, C>\n): r is { ok: false; error: E; cause?: C } => !r.ok;\n\n/**\n * Checks if an error is an UnexpectedError.\n * Used internally by the framework but exported for advanced custom handling.\n * Indicates an error that wasn't typed/expected in the `run` signature.\n */\nexport const isUnexpectedError = (e: unknown): e is UnexpectedError =>\n typeof e === \"object\" &&\n e !== null &&\n (e as UnexpectedError).type === \"UNEXPECTED_ERROR\";\n\n// =============================================================================\n// Type Utilities\n// =============================================================================\n\ntype AnyFunction = (...args: never[]) => unknown;\n\n/**\n * Helper to extract the error type from Result or AsyncResult return values.\n * Works even when a function is declared to return a union of both forms.\n */\ntype ErrorOfReturn<R> = Extract<Awaited<R>, { ok: false }> extends { error: infer E }\n ? E\n : never;\n\n/**\n * Extract error type from a single function's return type\n */\nexport type ErrorOf<T extends AnyFunction> = ErrorOfReturn<ReturnType<T>>;\n\n/**\n * Extract union of error types from multiple functions\n */\nexport type Errors<T extends AnyFunction[]> = {\n [K in keyof T]: ErrorOf<T[K]>;\n}[number];\n\n/**\n * Extract value type from Result\n */\nexport type ExtractValue<T> = T extends { ok: true; value: infer U }\n ? U\n : never;\n\n/**\n * Extract error type from Result\n */\nexport type ExtractError<T> = T extends { ok: false; error: infer E }\n ? E\n : never;\n\n/**\n * Extract cause type from Result\n */\nexport type ExtractCause<T> = T extends { ok: false; cause?: infer C }\n ? C\n : never;\n\n/**\n * Helper to extract the cause type from Result or AsyncResult return values.\n * Works even when a function is declared to return a union of both forms.\n */\ntype CauseOfReturn<R> = Extract<Awaited<R>, { ok: false }> extends { cause?: infer C }\n ? C\n : never;\n\n/**\n * Extract cause type from a function's return type\n */\nexport type CauseOf<T extends AnyFunction> = CauseOfReturn<ReturnType<T>>;\n\n// =============================================================================\n// Step Options\n// =============================================================================\n\n/**\n * Options for configuring a step within a workflow.\n * Use these to enable tracing, caching, and state persistence.\n */\nexport type StepOptions = {\n /**\n * Human-readable label for the step.\n * Used in logs, traces, and error messages.\n * Highly recommended for debugging complex workflows.\n */\n name?: string;\n\n /**\n * Stable identity key for the step.\n * REQUIRED for:\n * 1. Caching: Used as the cache key.\n * 2. Resuming: Used to identify which steps have already completed.\n *\n * Must be unique within the workflow.\n */\n key?: string;\n\n /**\n * Retry configuration for transient failures.\n * When specified, the step will retry on errors according to this config.\n */\n retry?: RetryOptions;\n\n /**\n * Timeout configuration for the operation.\n * When specified, each attempt will be aborted after the timeout duration.\n */\n timeout?: TimeoutOptions;\n};\n\n// =============================================================================\n// Retry and Timeout Types\n// =============================================================================\n\n/**\n * Backoff strategy for retry operations.\n */\nexport type BackoffStrategy = \"fixed\" | \"linear\" | \"exponential\";\n\n/**\n * Configuration for step retry behavior.\n */\nexport type RetryOptions = {\n /**\n * Total number of attempts (1 = no retry, 3 = initial + 2 retries).\n * Must be >= 1.\n */\n attempts: number;\n\n /**\n * Backoff strategy between retries.\n * - 'fixed': Same delay each time (initialDelay)\n * - 'linear': Delay increases linearly (initialDelay * attempt)\n * - 'exponential': Delay doubles each time (initialDelay * 2^(attempt-1))\n * @default 'exponential'\n */\n backoff?: BackoffStrategy;\n\n /**\n * Initial delay in milliseconds before first retry.\n * @default 100\n */\n initialDelay?: number;\n\n /**\n * Maximum delay cap in milliseconds.\n * Prevents exponential backoff from growing too large.\n * @default 30000 (30 seconds)\n */\n maxDelay?: number;\n\n /**\n * Whether to add random jitter (0-25% of delay).\n * Helps prevent thundering herd when multiple workflows retry simultaneously.\n * @default true\n */\n jitter?: boolean;\n\n /**\n * Predicate to determine if a retry should occur.\n * Receives the error and current attempt number (1-indexed).\n * Return true to retry, false to fail immediately.\n * @default Always retry on any error\n */\n retryOn?: (error: unknown, attempt: number) => boolean;\n\n /**\n * Callback invoked before each retry attempt.\n * Useful for logging, metrics, or side effects.\n */\n onRetry?: (error: unknown, attempt: number, delayMs: number) => void;\n};\n\n/**\n * Configuration for step timeout behavior.\n */\nexport type TimeoutOptions = {\n /**\n * Timeout duration in milliseconds per attempt.\n * When combined with retry, each attempt gets its own timeout.\n */\n ms: number;\n\n /**\n * Custom error to use when timeout occurs.\n * @default StepTimeoutError with step details\n */\n error?: unknown;\n\n /**\n * Whether to pass an AbortSignal to the operation.\n * When true, the operation function receives (signal: AbortSignal) as argument.\n * Useful for fetch() and other APIs that support cancellation.\n * @default false\n */\n signal?: boolean;\n};\n\n/**\n * Standard timeout error type.\n */\nexport type StepTimeoutError = {\n type: \"STEP_TIMEOUT\";\n stepName?: string;\n stepKey?: string;\n timeoutMs: number;\n attempt?: number;\n};\n\n/**\n * Symbol used to mark any error (including custom errors) as a timeout error.\n * This allows detection of timeout errors even when users provide custom error payloads.\n */\nexport const STEP_TIMEOUT_MARKER: unique symbol = Symbol.for(\"step_timeout_marker\");\n\n/**\n * Metadata attached to timeout-marked errors.\n */\nexport type StepTimeoutMarkerMeta = {\n timeoutMs: number;\n stepName?: string;\n stepKey?: string;\n attempt?: number;\n};\n\n/**\n * Type guard to check if an error is a StepTimeoutError.\n * This checks both the standard type field AND the timeout marker symbol,\n * so custom errors provided via timeout.error are also detected.\n */\nexport function isStepTimeoutError(e: unknown): e is StepTimeoutError {\n if (typeof e !== \"object\" || e === null) {\n return false;\n }\n // Check for standard type field\n if ((e as StepTimeoutError).type === \"STEP_TIMEOUT\") {\n return true;\n }\n // Check for timeout marker (custom errors)\n return STEP_TIMEOUT_MARKER in e;\n}\n\n/**\n * Get timeout metadata from a timeout error (works with both standard and custom errors).\n * Returns undefined if the error is not a timeout error.\n */\nexport function getStepTimeoutMeta(e: unknown): StepTimeoutMarkerMeta | undefined {\n if (typeof e !== \"object\" || e === null) {\n return undefined;\n }\n // Check for standard type field first\n if ((e as StepTimeoutError).type === \"STEP_TIMEOUT\") {\n const err = e as StepTimeoutError;\n return {\n timeoutMs: err.timeoutMs,\n stepName: err.stepName,\n stepKey: err.stepKey,\n attempt: err.attempt,\n };\n }\n // Check for timeout marker (custom errors)\n if (STEP_TIMEOUT_MARKER in e) {\n return (e as Record<symbol, StepTimeoutMarkerMeta>)[STEP_TIMEOUT_MARKER];\n }\n return undefined;\n}\n\n// =============================================================================\n// RunStep Interface\n// =============================================================================\n\n/**\n * The `step` object passed to the function in `run(async (step) => { ... })`.\n * acts as the bridge between your business logic and the workflow engine.\n *\n * It provides methods to:\n * 1. Execute operations that return `Result` types.\n * 2. safely wrap operations that might throw exceptions (using `step.try`).\n * 3. Assign names and keys to operations for tracing and caching.\n *\n * @template E - The union of all known error types expected in this workflow.\n */\nexport interface RunStep<E = unknown> {\n /**\n * Execute a Result-returning operation (lazy function form).\n *\n * Use this form when the operation has side effects or is expensive,\n * so it's only executed if the step hasn't been cached/completed yet.\n *\n * @param operation - A function that returns a Result or AsyncResult\n * @param options - Step name or options object\n * @returns The success value (unwrapped)\n * @throws {EarlyExit} If the result is an error (stops execution safely)\n *\n * @example\n * ```typescript\n * const user = await step(() => fetchUser(id), \"fetch-user\");\n * ```\n */\n <T, StepE extends E, StepC = unknown>(\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n options?: StepOptions | string\n ): Promise<T>;\n\n /**\n * Execute a Result-returning operation (direct value form).\n *\n * Use this form for simple operations or when you already have a Result/Promise.\n * Note: The operation has already started/completed by the time `step` is called.\n *\n * @param result - A Result object or Promise resolving to a Result\n * @param options - Step name or options object\n * @returns The success value (unwrapped)\n * @throws {EarlyExit} If the result is an error (stops execution safely)\n *\n * @example\n * ```typescript\n * const user = await step(existingResult, \"check-result\");\n * ```\n */\n <T, StepE extends E, StepC = unknown>(\n result: Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n options?: StepOptions | string\n ): Promise<T>;\n\n /**\n * Execute a standard throwing operation safely.\n * Catches exceptions and maps them to a typed error, or wraps them if no mapper is provided.\n *\n * Use this when integrating with libraries that throw exceptions.\n *\n * @param operation - A function that returns a value or Promise (may throw)\n * @param options - Configuration including error mapping\n * @returns The success value\n * @throws {EarlyExit} If the operation throws (stops execution safely)\n *\n * @example\n * ```typescript\n * const data = await step.try(\n * () => db.query(),\n * {\n * name: \"db-query\",\n * onError: (e) => ({ type: \"DB_ERROR\", cause: e })\n * }\n * );\n * ```\n */\n try: <T, const Err extends E>(\n operation: () => T | Promise<T>,\n options:\n | { error: Err; name?: string; key?: string }\n | { onError: (cause: unknown) => Err; name?: string; key?: string }\n ) => Promise<T>;\n\n /**\n * Execute a Result-returning function and map its error to a typed error.\n *\n * Use this when calling functions that return Result<T, E> and you want to\n * map their typed errors to your workflow's error type. Unlike step.try(),\n * the error passed to onError is typed (not unknown).\n *\n * @param operation - A function that returns a Result or AsyncResult\n * @param options - Configuration including error mapping\n * @returns The success value (unwrapped)\n * @throws {EarlyExit} If the result is an error (stops execution safely)\n *\n * @example\n * ```typescript\n * const response = await step.fromResult(\n * () => callProvider(input),\n * {\n * name: \"call-provider\",\n * onError: (providerError) => ({\n * type: \"PROVIDER_FAILED\",\n * provider: providerError.provider,\n * cause: providerError\n * })\n * }\n * );\n * ```\n */\n fromResult: <T, ResultE, const Err extends E>(\n operation: () => Result<T, ResultE, unknown> | AsyncResult<T, ResultE, unknown>,\n options:\n | { error: Err; name?: string; key?: string }\n | { onError: (resultError: ResultE) => Err; name?: string; key?: string }\n ) => Promise<T>;\n\n /**\n * Execute a parallel operation (allAsync) with scope events for visualization.\n *\n * This wraps the operation with scope_start and scope_end events, enabling\n * visualization of parallel execution branches.\n *\n * @param name - Name for this parallel block (used in visualization)\n * @param operation - A function that returns a Result from allAsync or allSettledAsync\n * @returns The success value (unwrapped array)\n *\n * @example\n * ```typescript\n * const [user, posts] = await step.parallel('Fetch all data', () =>\n * allAsync([fetchUser(id), fetchPosts(id)])\n * );\n * ```\n */\n parallel: <T, StepE extends E, StepC = unknown>(\n name: string,\n operation: () => Result<T[], StepE, StepC> | AsyncResult<T[], StepE, StepC>\n ) => Promise<T[]>;\n\n /**\n * Execute a race operation (anyAsync) with scope events for visualization.\n *\n * This wraps the operation with scope_start and scope_end events, enabling\n * visualization of racing execution branches.\n *\n * @param name - Name for this race block (used in visualization)\n * @param operation - A function that returns a Result from anyAsync\n * @returns The success value (first to succeed)\n *\n * @example\n * ```typescript\n * const data = await step.race('Fastest API', () =>\n * anyAsync([fetchFromPrimary(id), fetchFromFallback(id)])\n * );\n * ```\n */\n race: <T, StepE extends E, StepC = unknown>(\n name: string,\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>\n ) => Promise<T>;\n\n /**\n * Execute an allSettled operation with scope events for visualization.\n *\n * This wraps the operation with scope_start and scope_end events, enabling\n * visualization of allSettled execution branches. Unlike step.parallel,\n * allSettled collects all results even if some fail.\n *\n * @param name - Name for this allSettled block (used in visualization)\n * @param operation - A function that returns a Result from allSettledAsync\n * @returns The success value (unwrapped array)\n *\n * @example\n * ```typescript\n * const [user, posts] = await step.allSettled('Fetch all data', () =>\n * allSettledAsync([fetchUser(id), fetchPosts(id)])\n * );\n * ```\n */\n allSettled: <T, StepE extends E, StepC = unknown>(\n name: string,\n operation: () => Result<T[], StepE, StepC> | AsyncResult<T[], StepE, StepC>\n ) => Promise<T[]>;\n\n /**\n * Execute an operation with retry and optional timeout.\n *\n * Use this for operations that may fail transiently (network issues, rate limits)\n * and benefit from automatic retry with backoff.\n *\n * @param operation - A function that returns a Result or AsyncResult\n * @param options - Retry configuration and optional timeout\n * @returns The success value (unwrapped)\n * @throws {EarlyExit} If all retries are exhausted (stops execution safely)\n *\n * @example\n * ```typescript\n * const data = await step.retry(\n * () => fetchFromExternalApi(id),\n * {\n * name: 'fetch-external',\n * attempts: 3,\n * backoff: 'exponential',\n * initialDelay: 200,\n * retryOn: (error) => error === 'RATE_LIMITED' || error === 'TRANSIENT',\n * onRetry: (error, attempt, delay) => {\n * console.log(`Retry ${attempt} after ${delay}ms`);\n * },\n * }\n * );\n * ```\n */\n retry: <T, StepE extends E, StepC = unknown>(\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n options: RetryOptions & { name?: string; key?: string; timeout?: TimeoutOptions }\n ) => Promise<T>;\n\n /**\n * Execute an operation with a timeout.\n *\n * Use this for operations that may hang indefinitely (external APIs, connections)\n * and need to be aborted after a certain duration.\n *\n * When `signal: true` is set, an AbortSignal is passed to your operation,\n * which you can use with APIs like fetch() for proper cancellation.\n *\n * @param operation - A function that returns a Result (may receive AbortSignal)\n * @param options - Timeout configuration\n * @returns The success value (unwrapped)\n * @throws {EarlyExit} If the operation times out (stops execution safely)\n *\n * @example\n * ```typescript\n * // Without AbortSignal\n * const data = await step.withTimeout(\n * () => fetchData(id),\n * { ms: 5000, name: 'fetch-data' }\n * );\n *\n * // With AbortSignal for fetch()\n * const data = await step.withTimeout(\n * (signal) => fetch(url, { signal }).then(r => ok(r.json())),\n * { ms: 5000, signal: true, name: 'fetch-url' }\n * );\n * ```\n */\n withTimeout: <T, StepE extends E, StepC = unknown>(\n operation:\n | (() => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>)\n | ((signal: AbortSignal) => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>),\n options: TimeoutOptions & { name?: string; key?: string }\n ) => Promise<T>;\n}\n\n// =============================================================================\n// Event Types (for run() optional event support)\n// =============================================================================\n\n/**\n * Unified event stream for workflow execution.\n *\n * Note: step_complete.result uses Result<unknown, unknown, unknown> because events\n * aggregate results from heterogeneous steps. At runtime, the actual Result object\n * preserves its original types, but the event type cannot statically represent them.\n * Use runtime checks or the meta field to interpret cause values.\n */\n/**\n * Scope types for parallel and race operations.\n */\nexport type ScopeType = \"parallel\" | \"race\" | \"allSettled\";\n\nexport type WorkflowEvent<E> =\n | { type: \"workflow_start\"; workflowId: string; ts: number }\n | { type: \"workflow_success\"; workflowId: string; ts: number; durationMs: number }\n | { type: \"workflow_error\"; workflowId: string; ts: number; durationMs: number; error: E }\n | { type: \"step_start\"; workflowId: string; stepId: string; stepKey?: string; name?: string; ts: number }\n | { type: \"step_success\"; workflowId: string; stepId: string; stepKey?: string; name?: string; ts: number; durationMs: number }\n | { type: \"step_error\"; workflowId: string; stepId: string; stepKey?: string; name?: string; ts: number; durationMs: number; error: E }\n | { type: \"step_aborted\"; workflowId: string; stepId: string; stepKey?: string; name?: string; ts: number; durationMs: number }\n | { type: \"step_complete\"; workflowId: string; stepKey: string; name?: string; ts: number; durationMs: number; result: Result<unknown, unknown, unknown>; meta?: StepFailureMeta }\n | { type: \"step_cache_hit\"; workflowId: string; stepKey: string; name?: string; ts: number }\n | { type: \"step_cache_miss\"; workflowId: string; stepKey: string; name?: string; ts: number }\n | { type: \"step_skipped\"; workflowId: string; stepKey?: string; name?: string; reason?: string; decisionId?: string; ts: number }\n | { type: \"scope_start\"; workflowId: string; scopeId: string; scopeType: ScopeType; name?: string; ts: number }\n | { type: \"scope_end\"; workflowId: string; scopeId: string; ts: number; durationMs: number; winnerId?: string }\n // Retry events\n | {\n type: \"step_retry\";\n workflowId: string;\n stepId: string;\n stepKey?: string;\n name?: string;\n ts: number;\n attempt: number;\n maxAttempts: number;\n delayMs: number;\n error: E;\n }\n | {\n type: \"step_retries_exhausted\";\n workflowId: string;\n stepId: string;\n stepKey?: string;\n name?: string;\n ts: number;\n durationMs: number;\n attempts: number;\n lastError: E;\n }\n // Timeout event\n | {\n type: \"step_timeout\";\n workflowId: string;\n stepId: string;\n stepKey?: string;\n name?: string;\n ts: number;\n timeoutMs: number;\n attempt?: number;\n };\n\n// =============================================================================\n// Run Options\n// =============================================================================\n\nexport type RunOptionsWithCatch<E, C = void> = {\n /**\n * Handler for expected errors.\n * Called when a step fails with a known error type.\n */\n onError?: (error: E, stepName?: string) => void;\n /**\n * Listener for workflow events (start, success, error, step events).\n * Use this for logging, telemetry, or debugging.\n */\n onEvent?: (event: WorkflowEvent<E | UnexpectedError>, ctx: C) => void;\n /**\n * Catch-all mapper for unexpected exceptions.\n * Required for \"Strict Mode\".\n * Converts unknown exceptions (like network crashes or bugs) into your typed error union E.\n */\n catchUnexpected: (cause: unknown) => E;\n /**\n * Unique ID for this workflow execution.\n * Defaults to a random UUID.\n * Useful for correlating logs across distributed systems.\n */\n workflowId?: string;\n /**\n * Arbitrary context object passed to onEvent.\n * Useful for passing request IDs, user IDs, or loggers.\n */\n context?: C;\n};\n\nexport type RunOptionsWithoutCatch<E, C = void> = {\n /**\n * Handler for expected errors AND unexpected errors.\n * Unexpected errors will be wrapped in `UnexpectedError`.\n */\n onError?: (error: E | UnexpectedError, stepName?: string) => void;\n onEvent?: (event: WorkflowEvent<E | UnexpectedError>, ctx: C) => void;\n catchUnexpected?: undefined;\n workflowId?: string;\n context?: C;\n};\n\nexport type RunOptions<E, C = void> = RunOptionsWithCatch<E, C> | RunOptionsWithoutCatch<E, C>;\n\n// =============================================================================\n// Early Exit Mechanism (exported for caching layer)\n// =============================================================================\n\n/**\n * Symbol used to identify early exit throws.\n * Exported for the caching layer in workflow.ts.\n * @internal\n */\nexport const EARLY_EXIT_SYMBOL: unique symbol = Symbol(\"early-exit\");\n\n/**\n * Metadata about how a step failed.\n * @internal\n */\nexport type StepFailureMeta =\n | { origin: \"result\"; resultCause?: unknown }\n | { origin: \"throw\"; thrown: unknown };\n\n/**\n * Early exit object thrown to short-circuit workflow execution.\n * @internal\n */\nexport type EarlyExit<E> = {\n [EARLY_EXIT_SYMBOL]: true;\n error: E;\n meta: StepFailureMeta;\n};\n\n/**\n * Create an early exit throw object.\n * Used by the caching layer to synthesize early exits for cached errors.\n * @internal\n */\nexport function createEarlyExit<E>(error: E, meta: StepFailureMeta): EarlyExit<E> {\n return {\n [EARLY_EXIT_SYMBOL]: true,\n error,\n meta,\n };\n}\n\n/**\n * Type guard for early exit objects.\n * @internal\n */\nexport function isEarlyExit<E>(e: unknown): e is EarlyExit<E> {\n return (\n typeof e === \"object\" &&\n e !== null &&\n (e as Record<PropertyKey, unknown>)[EARLY_EXIT_SYMBOL] === true\n );\n}\n\n/**\n * Symbol to mark exceptions thrown by catchUnexpected mappers.\n * These should propagate without being re-processed.\n * @internal\n */\nconst MAPPER_EXCEPTION_SYMBOL: unique symbol = Symbol(\"mapper-exception\");\n\ntype MapperException = {\n [MAPPER_EXCEPTION_SYMBOL]: true;\n thrown: unknown;\n};\n\nfunction createMapperException(thrown: unknown): MapperException {\n return { [MAPPER_EXCEPTION_SYMBOL]: true, thrown };\n}\n\nfunction isMapperException(e: unknown): e is MapperException {\n return (\n typeof e === \"object\" &&\n e !== null &&\n (e as Record<PropertyKey, unknown>)[MAPPER_EXCEPTION_SYMBOL] === true\n );\n}\n\n/** Helper to parse step options - accepts string or object form */\nfunction parseStepOptions(\n options?: StepOptions | string\n): StepOptions & { name?: string; key?: string } {\n if (typeof options === \"string\") {\n return { name: options };\n }\n return options ?? {};\n}\n\n// =============================================================================\n// Retry and Timeout Utilities\n// =============================================================================\n\n/**\n * Calculate the delay for a retry attempt based on the backoff strategy.\n * @internal\n */\nfunction calculateRetryDelay(\n attempt: number,\n options: {\n backoff: BackoffStrategy;\n initialDelay: number;\n maxDelay: number;\n jitter: boolean;\n }\n): number {\n const { backoff, initialDelay, maxDelay, jitter } = options;\n\n let delay: number;\n\n switch (backoff) {\n case \"fixed\":\n delay = initialDelay;\n break;\n case \"linear\":\n delay = initialDelay * attempt;\n break;\n case \"exponential\":\n delay = initialDelay * Math.pow(2, attempt - 1);\n break;\n }\n\n // Apply max cap\n delay = Math.min(delay, maxDelay);\n\n // Apply jitter (0-25% of delay)\n if (jitter) {\n const jitterAmount = delay * 0.25 * Math.random();\n delay = delay + jitterAmount;\n }\n\n return Math.floor(delay);\n}\n\n/**\n * Sleep for a specified number of milliseconds.\n * @internal\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Symbol used internally to identify timeout rejection.\n */\nconst TIMEOUT_SYMBOL: unique symbol = Symbol(\"timeout\");\n\n/**\n * Execute an operation with a timeout using Promise.race.\n * @internal\n */\nasync function executeWithTimeout<T>(\n operation: (() => Promise<T>) | ((signal: AbortSignal) => Promise<T>),\n options: TimeoutOptions,\n stepInfo: { name?: string; key?: string; attempt?: number }\n): Promise<T> {\n const controller = new AbortController();\n\n // Create the timeout error once\n const timeoutError: StepTimeoutError =\n (options.error as StepTimeoutError) ?? {\n type: \"STEP_TIMEOUT\",\n stepName: stepInfo.name,\n stepKey: stepInfo.key,\n timeoutMs: options.ms,\n attempt: stepInfo.attempt,\n };\n\n // Track the timeout ID for cleanup\n let timeoutId: ReturnType<typeof setTimeout>;\n\n // Create a timeout promise that rejects after the specified duration\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n controller.abort(); // Signal abort for operations that support it\n reject({ [TIMEOUT_SYMBOL]: true, error: timeoutError });\n }, options.ms);\n });\n\n // Execute the operation\n let operationPromise: Promise<T>;\n if (options.signal) {\n // Operation expects an AbortSignal\n operationPromise = Promise.resolve(\n (operation as (signal: AbortSignal) => Promise<T>)(controller.signal)\n );\n } else {\n // Standard operation\n operationPromise = Promise.resolve((operation as () => Promise<T>)());\n }\n\n try {\n // Race between operation and timeout\n const result = await Promise.race([operationPromise, timeoutPromise]);\n return result;\n } catch (error) {\n // Check if this was our timeout\n if (\n typeof error === \"object\" &&\n error !== null &&\n (error as Record<symbol, unknown>)[TIMEOUT_SYMBOL] === true\n ) {\n const errorToThrow = (error as { error: unknown }).error;\n\n // Mark the error with STEP_TIMEOUT_MARKER if it's a custom error (not already a StepTimeoutError)\n // This allows isStepTimeoutError() and getStepTimeoutMeta() to work with custom errors\n // Note: Always update metadata to reflect the current attempt (same error may be reused across retries)\n if (\n typeof errorToThrow === \"object\" &&\n errorToThrow !== null &&\n (errorToThrow as StepTimeoutError).type !== \"STEP_TIMEOUT\"\n ) {\n const meta: StepTimeoutMarkerMeta = {\n timeoutMs: options.ms,\n stepName: stepInfo.name,\n stepKey: stepInfo.key,\n attempt: stepInfo.attempt,\n };\n\n if (STEP_TIMEOUT_MARKER in errorToThrow) {\n // Update existing marker with current attempt's metadata\n (errorToThrow as Record<symbol, StepTimeoutMarkerMeta>)[STEP_TIMEOUT_MARKER] = meta;\n } else {\n // Define new marker (writable so it can be updated on retry)\n Object.defineProperty(errorToThrow, STEP_TIMEOUT_MARKER, {\n value: meta,\n enumerable: false,\n writable: true,\n configurable: false,\n });\n }\n }\n\n throw errorToThrow;\n }\n // Re-throw other errors\n throw error;\n } finally {\n // Always clear the timeout to prevent leaks\n clearTimeout(timeoutId!);\n }\n}\n\n/**\n * Default retry configuration values.\n * @internal\n */\nconst DEFAULT_RETRY_CONFIG = {\n backoff: \"exponential\" as BackoffStrategy,\n initialDelay: 100,\n maxDelay: 30000,\n jitter: true,\n retryOn: () => true,\n onRetry: () => {},\n} as const;\n\n// =============================================================================\n// run() Function\n// =============================================================================\n\n/**\n * Execute a workflow with step-based error handling.\n *\n * ## When to Use run()\n *\n * Use `run()` when:\n * - Dependencies are dynamic (passed at runtime, not known at compile time)\n * - You don't need step caching or resume state\n * - Error types are known upfront and can be specified manually\n * - Building lightweight, one-off workflows\n *\n * For automatic error type inference from static dependencies, use `createWorkflow()`.\n *\n * ## Modes\n *\n * `run()` has three modes based on options:\n * - **Strict Mode** (`catchUnexpected`): Returns `Result<T, E>` (closed union)\n * - **Typed Mode** (`onError`): Returns `Result<T, E | UnexpectedError>`\n * - **Safe Default** (no options): Returns `Result<T, UnexpectedError>`\n *\n * @example\n * ```typescript\n * // Typed mode with explicit error union\n * const result = await run<Output, 'NOT_FOUND' | 'FETCH_ERROR'>(\n * async (step) => {\n * const user = await step(fetchUser(userId));\n * return user;\n * },\n * { onError: (e) => console.log('Failed:', e) }\n * );\n * ```\n *\n * @see createWorkflow - For static dependencies with auto error inference\n */\n\n/**\n * Execute a workflow with \"Strict Mode\" error handling.\n *\n * In this mode, you MUST provide `catchUnexpected` to map unknown exceptions\n * to your typed error union `E`. This guarantees that the returned Result\n * will only ever contain errors of type `E`.\n *\n * @param fn - The workflow function containing steps\n * @param options - Configuration options, including `catchUnexpected`\n * @returns A Promise resolving to `Result<T, E>`\n *\n * @example\n * ```typescript\n * const result = await run(async (step) => {\n * // ... steps ...\n * }, {\n * catchUnexpected: (e) => ({ type: 'UNKNOWN_ERROR', cause: e })\n * });\n * ```\n */\nexport function run<T, E, C = void>(\n fn: (step: RunStep<E>) => Promise<T> | T,\n options: RunOptionsWithCatch<E, C>\n): AsyncResult<T, E, unknown>;\n\n/**\n * Execute a workflow with \"Typed Mode\" error handling.\n *\n * In this mode, you provide an `onError` callback. The returned Result\n * may contain your typed errors `E` OR `UnexpectedError` if an uncaught\n * exception occurs.\n *\n * @param fn - The workflow function containing steps\n * @param options - Configuration options, including `onError`\n * @returns A Promise resolving to `Result<T, E | UnexpectedError>`\n */\nexport function run<T, E, C = void>(\n fn: (step: RunStep<E | UnexpectedError>) => Promise<T> | T,\n options: {\n onError: (error: E | UnexpectedError, stepName?: string) => void;\n onEvent?: (event: WorkflowEvent<E | UnexpectedError>, ctx: C) => void;\n workflowId?: string;\n context?: C;\n }\n): AsyncResult<T, E | UnexpectedError, unknown>;\n\n/**\n * Execute a workflow with \"Safe Default\" error handling.\n *\n * In this mode, you don't need to specify any error types.\n * Any error (Result error or thrown exception) will be returned as\n * an `UnexpectedError`.\n *\n * @param fn - The workflow function containing steps\n * @param options - Optional configuration\n * @returns A Promise resolving to `Result<T, UnexpectedError>`\n *\n * @example\n * ```typescript\n * const result = await run(async (step) => {\n * return await step(someOp());\n * });\n * ```\n */\nexport function run<T, C = void>(\n fn: (step: RunStep) => Promise<T> | T,\n options?: {\n onEvent?: (event: WorkflowEvent<UnexpectedError>, ctx: C) => void;\n workflowId?: string;\n context?: C;\n }\n): AsyncResult<T, UnexpectedError, unknown>;\n\n// Implementation\nexport async function run<T, E, C = void>(\n fn: (step: RunStep<E | UnexpectedError>) => Promise<T> | T,\n options?: RunOptions<E, C>\n): AsyncResult<T, E | UnexpectedError> {\n const {\n onError,\n onEvent,\n catchUnexpected,\n workflowId: providedWorkflowId,\n context,\n } = options && typeof options === \"object\"\n ? (options as RunOptions<E, C>)\n : ({} as RunOptions<E, C>);\n\n const workflowId = providedWorkflowId ?? crypto.randomUUID();\n const wrapMode = !onError && !catchUnexpected;\n\n // Track active scopes as a stack for proper nesting\n // When a step succeeds, only the innermost race scope gets the winner\n const activeScopeStack: Array<{ scopeId: string; type: \"race\" | \"parallel\" | \"allSettled\"; winnerId?: string }> = [];\n\n // Counter for generating unique step IDs\n let stepIdCounter = 0;\n\n // Generate a unique step ID\n // Uses stepKey when provided (for cache stability), otherwise generates a unique ID.\n // Note: name is NOT used for stepId because multiple concurrent steps may share a name,\n // which would cause them to collide in activeSteps tracking and race winner detection.\n const generateStepId = (stepKey?: string): string => {\n return stepKey ?? `step_${++stepIdCounter}`;\n };\n\n const emitEvent = (event: WorkflowEvent<E | UnexpectedError>) => {\n // Track first successful step in the innermost race scope for winnerId\n if (event.type === \"step_success\") {\n // Use the stepId from the event (already generated at step start)\n const stepId = event.stepId;\n\n // Find innermost race scope (search from end of stack)\n for (let i = activeScopeStack.length - 1; i >= 0; i--) {\n const scope = activeScopeStack[i];\n if (scope.type === \"race\" && !scope.winnerId) {\n scope.winnerId = stepId;\n break; // Only update innermost race scope\n }\n }\n }\n onEvent?.(event, context as C);\n };\n\n // Use the exported early exit function with proper type parameter\n const earlyExit = createEarlyExit<E>;\n\n // Local type guard that narrows to EarlyExit<E> specifically\n const isEarlyExitE = (e: unknown): e is EarlyExit<E> => isEarlyExit(e);\n\n const wrapForStep = (\n error: unknown,\n meta?: StepFailureMeta\n ): E | UnexpectedError => {\n if (!wrapMode) {\n return error as E;\n }\n\n if (meta?.origin === \"result\") {\n return {\n type: \"UNEXPECTED_ERROR\",\n cause: {\n type: \"STEP_FAILURE\",\n origin: \"result\",\n error,\n ...(meta.resultCause !== undefined\n ? { cause: meta.resultCause }\n : {}),\n },\n };\n }\n\n if (meta?.origin === \"throw\") {\n return {\n type: \"UNEXPECTED_ERROR\",\n cause: {\n type: \"STEP_FAILURE\",\n origin: \"throw\",\n error,\n thrown: meta.thrown,\n },\n };\n }\n\n return {\n type: \"UNEXPECTED_ERROR\",\n cause: {\n type: \"STEP_FAILURE\",\n origin: \"result\",\n error,\n },\n };\n };\n\n const causeFromMeta = (meta: StepFailureMeta): unknown => {\n if (meta.origin === \"result\") {\n return meta.resultCause;\n }\n return meta.thrown;\n };\n\n const unexpectedFromFailure = (failure: EarlyExit<E>): UnexpectedError => ({\n type: \"UNEXPECTED_ERROR\",\n cause:\n failure.meta.origin === \"result\"\n ? {\n type: \"STEP_FAILURE\" as const,\n origin: \"result\" as const,\n error: failure.error,\n ...(failure.meta.resultCause !== undefined\n ? { cause: failure.meta.resultCause }\n : {}),\n }\n : {\n type: \"STEP_FAILURE\" as const,\n origin: \"throw\" as const,\n error: failure.error,\n thrown: failure.meta.thrown,\n },\n });\n\n try {\n const stepFn = <T, StepE, StepC = unknown>(\n operationOrResult:\n | (() => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>)\n | Result<T, StepE, StepC>\n | AsyncResult<T, StepE, StepC>,\n stepOptions?: StepOptions | string\n ): Promise<T> => {\n return (async () => {\n const parsedOptions = parseStepOptions(stepOptions);\n const { name: stepName, key: stepKey, retry: retryConfig, timeout: timeoutConfig } = parsedOptions;\n const stepId = generateStepId(stepKey);\n const hasEventListeners = onEvent;\n const overallStartTime = hasEventListeners ? performance.now() : 0;\n\n // Validate that retry/timeout are only used with function operations\n // Direct Promise/Result values cannot be re-executed or wrapped with timeout\n const isFunction = typeof operationOrResult === \"function\";\n if (!isFunction) {\n if (retryConfig && retryConfig.attempts > 1) {\n throw new Error(\n `step: retry options require a function operation. ` +\n `Direct Promise/Result values cannot be re-executed on retry. ` +\n `Wrap your operation in a function: step(() => yourOperation, { retry: {...} })`\n );\n }\n if (timeoutConfig) {\n throw new Error(\n `step: timeout options require a function operation. ` +\n `Direct Promise/Result values cannot be wrapped with timeout after they've started. ` +\n `Wrap your operation in a function: step(() => yourOperation, { timeout: {...} })`\n );\n }\n }\n\n // Build effective retry config with defaults\n // Ensure at least 1 attempt (0 would skip the loop entirely and crash)\n const maxAttempts = Math.max(1, retryConfig?.attempts ?? 1);\n const effectiveRetry = {\n attempts: maxAttempts,\n backoff: retryConfig?.backoff ?? DEFAULT_RETRY_CONFIG.backoff,\n initialDelay: retryConfig?.initialDelay ?? DEFAULT_RETRY_CONFIG.initialDelay,\n maxDelay: retryConfig?.maxDelay ?? DEFAULT_RETRY_CONFIG.maxDelay,\n jitter: retryConfig?.jitter ?? DEFAULT_RETRY_CONFIG.jitter,\n retryOn: retryConfig?.retryOn ?? DEFAULT_RETRY_CONFIG.retryOn,\n onRetry: retryConfig?.onRetry ?? DEFAULT_RETRY_CONFIG.onRetry,\n };\n\n // Emit step_start only once (before first attempt)\n if (onEvent) {\n emitEvent({\n type: \"step_start\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n });\n }\n\n let lastResult: Result<T, StepE, StepC> | undefined;\n\n for (let attempt = 1; attempt <= effectiveRetry.attempts; attempt++) {\n const attemptStartTime = hasEventListeners ? performance.now() : 0;\n\n try {\n // Execute operation with optional timeout\n let result: Result<T, StepE, StepC>;\n\n if (typeof operationOrResult === \"function\") {\n if (timeoutConfig) {\n // Wrap with timeout\n result = await executeWithTimeout(\n operationOrResult as () => Promise<Result<T, StepE, StepC>>,\n timeoutConfig,\n { name: stepName, key: stepKey, attempt }\n );\n } else {\n result = await operationOrResult();\n }\n } else {\n // Direct value - timeout doesn't apply\n result = await operationOrResult;\n }\n\n // Success case\n if (result.ok) {\n const durationMs = performance.now() - overallStartTime;\n emitEvent({\n type: \"step_success\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n });\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n result,\n });\n }\n return result.value;\n }\n\n // Result error case - check if we should retry\n lastResult = result;\n\n if (attempt < effectiveRetry.attempts && effectiveRetry.retryOn(result.error, attempt)) {\n const delay = calculateRetryDelay(attempt, effectiveRetry);\n\n // Emit retry event\n emitEvent({\n type: \"step_retry\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n attempt: attempt + 1,\n maxAttempts: effectiveRetry.attempts,\n delayMs: delay,\n error: result.error as unknown as E,\n });\n\n effectiveRetry.onRetry(result.error, attempt, delay);\n await sleep(delay);\n continue;\n }\n\n // No more retries or retryOn returned false - emit exhausted event if we retried\n if (effectiveRetry.attempts > 1) {\n emitEvent({\n type: \"step_retries_exhausted\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: performance.now() - overallStartTime,\n attempts: attempt,\n lastError: result.error as unknown as E,\n });\n }\n\n // Fall through to final error handling below\n break;\n\n } catch (thrown) {\n const durationMs = performance.now() - attemptStartTime;\n\n // Handle early exit - propagate immediately\n if (isEarlyExitE(thrown)) {\n emitEvent({\n type: \"step_aborted\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n });\n throw thrown;\n }\n\n // Handle timeout error\n if (isStepTimeoutError(thrown)) {\n // Get timeout metadata from the error (works for both standard and custom errors)\n const timeoutMeta = getStepTimeoutMeta(thrown);\n const timeoutMs = timeoutConfig?.ms ?? timeoutMeta?.timeoutMs ?? 0;\n emitEvent({\n type: \"step_timeout\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n timeoutMs,\n attempt,\n });\n\n // Check if we should retry after timeout\n if (attempt < effectiveRetry.attempts && effectiveRetry.retryOn(thrown, attempt)) {\n const delay = calculateRetryDelay(attempt, effectiveRetry);\n\n emitEvent({\n type: \"step_retry\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n attempt: attempt + 1,\n maxAttempts: effectiveRetry.attempts,\n delayMs: delay,\n error: thrown as unknown as E,\n });\n\n effectiveRetry.onRetry(thrown, attempt, delay);\n await sleep(delay);\n continue;\n }\n\n // No more retries - emit exhausted if we retried\n if (effectiveRetry.attempts > 1) {\n emitEvent({\n type: \"step_retries_exhausted\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: performance.now() - overallStartTime,\n attempts: attempt,\n lastError: thrown as unknown as E,\n });\n }\n\n // Treat timeout as a thrown error for error handling\n }\n\n // Handle other thrown errors (continue to error handling below)\n\n // Check if we should retry thrown errors\n if (attempt < effectiveRetry.attempts && effectiveRetry.retryOn(thrown, attempt)) {\n const delay = calculateRetryDelay(attempt, effectiveRetry);\n\n emitEvent({\n type: \"step_retry\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n attempt: attempt + 1,\n maxAttempts: effectiveRetry.attempts,\n delayMs: delay,\n error: thrown as unknown as E,\n });\n\n effectiveRetry.onRetry(thrown, attempt, delay);\n await sleep(delay);\n continue;\n }\n\n // No more retries for thrown errors - emit exhausted if we retried\n if (effectiveRetry.attempts > 1 && !isStepTimeoutError(thrown)) {\n emitEvent({\n type: \"step_retries_exhausted\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: performance.now() - overallStartTime,\n attempts: attempt,\n lastError: thrown as unknown as E,\n });\n }\n\n // Handle the error based on mode\n const totalDurationMs = performance.now() - overallStartTime;\n\n if (catchUnexpected) {\n let mappedError: E;\n try {\n mappedError = catchUnexpected(thrown) as unknown as E;\n } catch (mapperError) {\n throw createMapperException(mapperError);\n }\n emitEvent({\n type: \"step_error\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: totalDurationMs,\n error: mappedError,\n });\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: totalDurationMs,\n result: err(mappedError, { cause: thrown }),\n meta: { origin: \"throw\", thrown },\n });\n }\n onError?.(mappedError as E, stepName);\n throw earlyExit(mappedError as E, { origin: \"throw\", thrown });\n } else {\n const unexpectedError: UnexpectedError = {\n type: \"UNEXPECTED_ERROR\",\n cause: { type: \"UNCAUGHT_EXCEPTION\", thrown },\n };\n emitEvent({\n type: \"step_error\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: totalDurationMs,\n error: unexpectedError,\n });\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: totalDurationMs,\n result: err(unexpectedError, { cause: thrown }),\n meta: { origin: \"throw\", thrown },\n });\n }\n throw thrown;\n }\n }\n }\n\n // All retries exhausted with Result error - handle final error\n // At this point lastResult must be an error result (we only reach here on error)\n const errorResult = lastResult as { ok: false; error: StepE; cause?: StepC };\n const totalDurationMs = performance.now() - overallStartTime;\n const wrappedError = wrapForStep(errorResult.error, {\n origin: \"result\",\n resultCause: errorResult.cause,\n });\n emitEvent({\n type: \"step_error\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: totalDurationMs,\n error: wrappedError,\n });\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs: totalDurationMs,\n result: errorResult,\n meta: { origin: \"result\", resultCause: errorResult.cause },\n });\n }\n onError?.(errorResult.error as unknown as E, stepName);\n throw earlyExit(errorResult.error as unknown as E, {\n origin: \"result\",\n resultCause: errorResult.cause,\n });\n })();\n };\n\n stepFn.try = <T, Err>(\n operation: () => T | Promise<T>,\n opts:\n | { error: Err; name?: string; key?: string }\n | { onError: (cause: unknown) => Err; name?: string; key?: string }\n ): Promise<T> => {\n const stepName = opts.name;\n const stepKey = opts.key;\n const stepId = generateStepId(stepKey);\n const mapToError = \"error\" in opts ? () => opts.error : opts.onError;\n const hasEventListeners = onEvent;\n\n return (async () => {\n const startTime = hasEventListeners ? performance.now() : 0;\n\n if (onEvent) {\n emitEvent({\n type: \"step_start\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n });\n }\n\n try {\n const value = await operation();\n const durationMs = performance.now() - startTime;\n emitEvent({\n type: \"step_success\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n });\n // Emit step_complete for keyed steps (for state persistence)\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n result: ok(value),\n });\n }\n return value;\n } catch (error) {\n const mapped = mapToError(error);\n const durationMs = performance.now() - startTime;\n const wrappedError = wrapForStep(mapped, { origin: \"throw\", thrown: error });\n emitEvent({\n type: \"step_error\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n error: wrappedError,\n });\n // Emit step_complete for keyed steps (for state persistence)\n // Note: For step.try errors, we encode the mapped error, not the original thrown\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n result: err(mapped, { cause: error }),\n meta: { origin: \"throw\", thrown: error },\n });\n }\n onError?.(mapped as unknown as E, stepName);\n throw earlyExit(mapped as unknown as E, { origin: \"throw\", thrown: error });\n }\n })();\n };\n\n // step.fromResult: Execute a Result-returning function and map its typed error\n stepFn.fromResult = <T, ResultE, Err>(\n operation: () => Result<T, ResultE, unknown> | AsyncResult<T, ResultE, unknown>,\n opts:\n | { error: Err; name?: string; key?: string }\n | { onError: (resultError: ResultE) => Err; name?: string; key?: string }\n ): Promise<T> => {\n const stepName = opts.name;\n const stepKey = opts.key;\n const stepId = generateStepId(stepKey);\n const mapToError = \"error\" in opts ? () => opts.error : opts.onError;\n const hasEventListeners = onEvent;\n\n return (async () => {\n const startTime = hasEventListeners ? performance.now() : 0;\n\n if (onEvent) {\n emitEvent({\n type: \"step_start\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n });\n }\n\n const result = await operation();\n\n if (result.ok) {\n const durationMs = performance.now() - startTime;\n emitEvent({\n type: \"step_success\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n });\n // Emit step_complete for keyed steps (for state persistence)\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n result: ok(result.value),\n });\n }\n return result.value;\n } else {\n const mapped = mapToError(result.error);\n const durationMs = performance.now() - startTime;\n // For fromResult, the cause is the original result.error (what got mapped)\n // This is analogous to step.try using thrown exception as cause\n const wrappedError = wrapForStep(mapped, {\n origin: \"result\",\n resultCause: result.error,\n });\n emitEvent({\n type: \"step_error\",\n workflowId,\n stepId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n error: wrappedError,\n });\n // Emit step_complete for keyed steps (for state persistence)\n if (stepKey) {\n emitEvent({\n type: \"step_complete\",\n workflowId,\n stepKey,\n name: stepName,\n ts: Date.now(),\n durationMs,\n result: err(mapped, { cause: result.error }),\n meta: { origin: \"result\", resultCause: result.error },\n });\n }\n onError?.(mapped as unknown as E, stepName);\n throw earlyExit(mapped as unknown as E, {\n origin: \"result\",\n resultCause: result.error,\n });\n }\n })();\n };\n\n // step.retry: Execute an operation with retry and optional timeout\n stepFn.retry = <T, StepE, StepC = unknown>(\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n options: RetryOptions & { name?: string; key?: string; timeout?: TimeoutOptions }\n ): Promise<T> => {\n // Delegate to stepFn with retry options merged into StepOptions\n return stepFn(operation, {\n name: options.name,\n key: options.key,\n retry: {\n attempts: options.attempts,\n backoff: options.backoff,\n initialDelay: options.initialDelay,\n maxDelay: options.maxDelay,\n jitter: options.jitter,\n retryOn: options.retryOn,\n onRetry: options.onRetry,\n },\n timeout: options.timeout,\n });\n };\n\n // step.withTimeout: Execute an operation with a timeout\n stepFn.withTimeout = <T, StepE, StepC = unknown>(\n operation:\n | (() => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>)\n | ((signal: AbortSignal) => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>),\n options: TimeoutOptions & { name?: string; key?: string }\n ): Promise<T> => {\n // Delegate to stepFn with timeout options\n // The signal handling happens in executeWithTimeout when timeout.signal is true\n return stepFn(\n operation as () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n {\n name: options.name,\n key: options.key,\n timeout: options,\n }\n );\n };\n\n // step.parallel: Execute a parallel operation with scope events\n stepFn.parallel = <T, StepE, StepC>(\n name: string,\n operation: () => Result<T[], StepE, StepC> | AsyncResult<T[], StepE, StepC>\n ): Promise<T[]> => {\n const scopeId = `scope_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n\n return (async () => {\n const startTime = performance.now();\n let scopeEnded = false;\n\n // Push this scope onto the stack for proper nesting tracking\n activeScopeStack.push({ scopeId, type: \"parallel\" });\n\n // Helper to emit scope_end exactly once\n const emitScopeEnd = () => {\n if (scopeEnded) return;\n scopeEnded = true;\n // Pop this scope from the stack\n const idx = activeScopeStack.findIndex(s => s.scopeId === scopeId);\n if (idx !== -1) activeScopeStack.splice(idx, 1);\n emitEvent({\n type: \"scope_end\",\n workflowId,\n scopeId,\n ts: Date.now(),\n durationMs: performance.now() - startTime,\n });\n };\n\n // Emit scope_start event\n emitEvent({\n type: \"scope_start\",\n workflowId,\n scopeId,\n scopeType: \"parallel\",\n name,\n ts: Date.now(),\n });\n\n try {\n const result = await operation();\n\n // Emit scope_end before processing result\n emitScopeEnd();\n\n if (!result.ok) {\n onError?.(result.error as unknown as E, name);\n throw earlyExit(result.error as unknown as E, {\n origin: \"result\",\n resultCause: result.cause,\n });\n }\n\n return result.value;\n } catch (error) {\n // Always emit scope_end in finally-like fashion\n emitScopeEnd();\n throw error;\n }\n })();\n };\n\n // step.race: Execute a race operation with scope events\n stepFn.race = <T, StepE, StepC>(\n name: string,\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>\n ): Promise<T> => {\n const scopeId = `scope_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n\n return (async () => {\n const startTime = performance.now();\n let scopeEnded = false;\n\n // Push this race scope onto the stack to track the first successful step as winner\n const scopeEntry = { scopeId, type: \"race\" as const, winnerId: undefined as string | undefined };\n activeScopeStack.push(scopeEntry);\n\n // Helper to emit scope_end exactly once, including winnerId\n const emitScopeEnd = () => {\n if (scopeEnded) return;\n scopeEnded = true;\n // Pop this scope from the stack\n const idx = activeScopeStack.findIndex(s => s.scopeId === scopeId);\n if (idx !== -1) activeScopeStack.splice(idx, 1);\n emitEvent({\n type: \"scope_end\",\n workflowId,\n scopeId,\n ts: Date.now(),\n durationMs: performance.now() - startTime,\n winnerId: scopeEntry.winnerId,\n });\n };\n\n // Emit scope_start event\n emitEvent({\n type: \"scope_start\",\n workflowId,\n scopeId,\n scopeType: \"race\",\n name,\n ts: Date.now(),\n });\n\n try {\n const result = await operation();\n\n // Emit scope_end before processing result\n emitScopeEnd();\n\n if (!result.ok) {\n onError?.(result.error as unknown as E, name);\n throw earlyExit(result.error as unknown as E, {\n origin: \"result\",\n resultCause: result.cause,\n });\n }\n\n return result.value;\n } catch (error) {\n // Always emit scope_end in finally-like fashion\n emitScopeEnd();\n throw error;\n }\n })();\n };\n\n // step.allSettled: Execute an allSettled operation with scope events\n stepFn.allSettled = <T, StepE, StepC>(\n name: string,\n operation: () => Result<T[], StepE, StepC> | AsyncResult<T[], StepE, StepC>\n ): Promise<T[]> => {\n const scopeId = `scope_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n\n return (async () => {\n const startTime = performance.now();\n let scopeEnded = false;\n\n // Push this scope onto the stack for proper nesting tracking\n activeScopeStack.push({ scopeId, type: \"allSettled\" });\n\n // Helper to emit scope_end exactly once\n const emitScopeEnd = () => {\n if (scopeEnded) return;\n scopeEnded = true;\n // Pop this scope from the stack\n const idx = activeScopeStack.findIndex(s => s.scopeId === scopeId);\n if (idx !== -1) activeScopeStack.splice(idx, 1);\n emitEvent({\n type: \"scope_end\",\n workflowId,\n scopeId,\n ts: Date.now(),\n durationMs: performance.now() - startTime,\n });\n };\n\n // Emit scope_start event\n emitEvent({\n type: \"scope_start\",\n workflowId,\n scopeId,\n scopeType: \"allSettled\",\n name,\n ts: Date.now(),\n });\n\n try {\n const result = await operation();\n\n // Emit scope_end before processing result\n emitScopeEnd();\n\n if (!result.ok) {\n onError?.(result.error as unknown as E, name);\n throw earlyExit(result.error as unknown as E, {\n origin: \"result\",\n resultCause: result.cause,\n });\n }\n\n return result.value;\n } catch (error) {\n // Always emit scope_end in finally-like fashion\n emitScopeEnd();\n throw error;\n }\n })();\n };\n\n const step = stepFn as RunStep<E | UnexpectedError>;\n const value = await fn(step);\n return ok(value);\n } catch (error) {\n // If a catchUnexpected mapper threw, propagate without re-processing\n if (isMapperException(error)) {\n throw error.thrown;\n }\n\n if (isEarlyExitE(error)) {\n const failureCause = causeFromMeta(error.meta);\n if (catchUnexpected || onError) {\n return err(error.error, { cause: failureCause });\n }\n // If the error is already an UnexpectedError (e.g., from resumed state),\n // return it directly without wrapping in another STEP_FAILURE\n if (isUnexpectedError(error.error)) {\n return err(error.error, { cause: failureCause });\n }\n const unexpectedError = unexpectedFromFailure(error);\n return err(unexpectedError, { cause: failureCause });\n }\n\n if (catchUnexpected) {\n const mapped = catchUnexpected(error);\n onError?.(mapped, \"unexpected\");\n return err(mapped, { cause: error });\n }\n\n const unexpectedError: UnexpectedError = {\n type: \"UNEXPECTED_ERROR\",\n cause: { type: \"UNCAUGHT_EXCEPTION\", thrown: error },\n };\n onError?.(unexpectedError as unknown as E, \"unexpected\");\n return err(unexpectedError, { cause: error });\n }\n}\n\n/**\n * Executes a workflow in \"Strict Mode\" with a closed error union.\n *\n * ## When to Use\n *\n * Use `run.strict()` when:\n * - You want a closed error union (no `UnexpectedError`)\n * - You need exhaustive error handling in production\n * - You want to guarantee all errors are explicitly typed\n * - You're building APIs where error types must be known\n *\n * ## Why Use This\n *\n * - **Closed union**: Error type is exactly `E`, no `UnexpectedError`\n * - **Exhaustive**: Forces you to handle all possible errors\n * - **Type-safe**: TypeScript ensures all errors are typed\n * - **Production-ready**: Better for APIs and libraries\n *\n * ## Important\n *\n * You MUST provide `catchUnexpected` to map any uncaught exceptions to your error type `E`.\n * This ensures the error union is truly closed.\n *\n * @param fn - The workflow function containing steps\n * @param options - Configuration options, MUST include `catchUnexpected`\n * @returns A Promise resolving to `Result<T, E>` (no UnexpectedError)\n *\n * @example\n * ```typescript\n * type AppError = 'NOT_FOUND' | 'UNAUTHORIZED' | 'UNEXPECTED';\n *\n * const result = await run.strict<User, AppError>(\n * async (step) => {\n * return await step(fetchUser(id));\n * },\n * {\n * catchUnexpected: () => 'UNEXPECTED' as const\n * }\n * );\n * // result.error: 'NOT_FOUND' | 'UNAUTHORIZED' | 'UNEXPECTED' (exactly)\n * ```\n */\nrun.strict = <T, E, C = void>(\n fn: (step: RunStep<E>) => Promise<T> | T,\n options: {\n onError?: (error: E, stepName?: string) => void;\n onEvent?: (event: WorkflowEvent<E | UnexpectedError>, ctx: C) => void;\n catchUnexpected: (cause: unknown) => E;\n workflowId?: string;\n context?: C;\n }\n): AsyncResult<T, E, unknown> => {\n return run<T, E, C>(fn, options);\n};\n\n// =============================================================================\n// Unwrap Utilities\n// =============================================================================\n\n/**\n * Error thrown when `unwrap()` is called on an error Result.\n *\n * This error is thrown to prevent silent failures when using `unwrap()`.\n * Prefer using `unwrapOr`, `unwrapOrElse`, or pattern matching with `match` or `isOk`/`isErr`.\n */\nexport class UnwrapError<E = unknown, C = unknown> extends Error {\n constructor(\n public readonly error: E,\n public readonly cause?: C\n ) {\n super(`Unwrap called on an error result: ${String(error)}`);\n this.name = \"UnwrapError\";\n }\n}\n\n/**\n * Unwraps a Result, throwing an error if it's a failure.\n *\n * ## When to Use\n *\n * Use `unwrap()` when:\n * - You're certain the Result is successful (e.g., after checking with `isOk`)\n * - You're in a context where errors should crash (e.g., tests, initialization)\n * - You need the value immediately and can't handle errors gracefully\n *\n * ## Why Avoid This\n *\n * **Prefer alternatives** in production code:\n * - `unwrapOr(defaultValue)` - Provide a fallback value\n * - `unwrapOrElse(fn)` - Compute fallback from error\n * - `match()` - Handle both cases explicitly\n * - `isOk()` / `isErr()` - Type-safe pattern matching\n *\n * Throwing errors makes error handling harder and can crash your application.\n *\n * @param r - The Result to unwrap\n * @returns The success value if the Result is successful\n * @throws {UnwrapError} If the Result is an error (includes the error and cause)\n *\n * @example\n * ```typescript\n * // Safe usage after checking\n * const result = someOperation();\n * if (isOk(result)) {\n * const value = unwrap(result); // Safe - we know it's ok\n * }\n *\n * // Unsafe usage (not recommended)\n * const value = unwrap(someOperation()); // May throw!\n * ```\n */\nexport const unwrap = <T, E, C>(r: Result<T, E, C>): T => {\n if (r.ok) return r.value;\n throw new UnwrapError<E, C>(r.error, r.cause);\n};\n\n/**\n * Unwraps a Result, returning a default value if it's a failure.\n *\n * ## When to Use\n *\n * Use `unwrapOr()` when:\n * - You have a sensible default value for errors\n * - You want to continue execution even on failure\n * - The default value is cheap to compute (use `unwrapOrElse` if expensive)\n *\n * ## Why Use This\n *\n * - **Safe**: Never throws, always returns a value\n * - **Simple**: One-liner for common error handling\n * - **Type-safe**: TypeScript knows you'll always get a `T`\n *\n * @param r - The Result to unwrap\n * @param defaultValue - The value to return if the Result is an error\n * @returns The success value if successful, otherwise the default value\n *\n * @example\n * ```typescript\n * // Provide default for missing data\n * const user = unwrapOr(fetchUser(id), { id: 'anonymous', name: 'Guest' });\n *\n * // Provide default for numeric operations\n * const count = unwrapOr(parseCount(input), 0);\n *\n * // Provide default for optional features\n * const config = unwrapOr(loadConfig(), getDefaultConfig());\n * ```\n */\nexport const unwrapOr = <T, E, C>(r: Result<T, E, C>, defaultValue: T): T =>\n r.ok ? r.value : defaultValue;\n\n/**\n * Unwraps a Result, computing a default value from the error if it's a failure.\n *\n * ## When to Use\n *\n * Use `unwrapOrElse()` when:\n * - The default value is expensive to compute (lazy evaluation)\n * - You need to log or handle the error before providing a default\n * - The default depends on the error type or cause\n * - You want to transform the error into a success value\n *\n * ## Why Use This Instead of `unwrapOr`\n *\n * - **Lazy**: Default is only computed if needed (better performance)\n * - **Error-aware**: You can inspect the error before providing default\n * - **Flexible**: Default can depend on error type or cause\n *\n * @param r - The Result to unwrap\n * @param fn - Function that receives the error and optional cause, returns the default value\n * @returns The success value if successful, otherwise the result of calling `fn(error, cause)`\n *\n * @example\n * ```typescript\n * // Compute default based on error type\n * const port = unwrapOrElse(parsePort(env.PORT), (error) => {\n * if (error === 'INVALID_FORMAT') return 3000;\n * if (error === 'OUT_OF_RANGE') return 8080;\n * return 4000; // default\n * });\n *\n * // Log error before providing default\n * const data = unwrapOrElse(fetchData(), (error, cause) => {\n * console.error('Failed to fetch:', error, cause);\n * return getCachedData();\n * });\n *\n * // Transform error into success value\n * const result = unwrapOrElse(operation(), (error) => {\n * return { success: false, reason: String(error) };\n * });\n * ```\n */\nexport const unwrapOrElse = <T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => T\n): T => (r.ok ? r.value : fn(r.error, r.cause));\n\n// =============================================================================\n// Wrapping Functions\n// =============================================================================\n\n/**\n * Wraps a synchronous throwing function in a Result.\n *\n * ## When to Use\n *\n * Use `from()` when:\n * - You have a synchronous function that throws exceptions\n * - You want to convert exceptions to typed errors\n * - You're integrating with libraries that throw (e.g., JSON.parse, fs.readFileSync)\n * - You need to handle errors without try/catch blocks\n *\n * ## Why Use This\n *\n * - **Type-safe errors**: Convert thrown exceptions to typed Result errors\n * - **No try/catch**: Cleaner code without nested try/catch blocks\n * - **Composable**: Results can be chained with `andThen`, `map`, etc.\n * - **Explicit errors**: Forces you to handle errors explicitly\n *\n * @param fn - The synchronous function to execute (may throw)\n * @returns A Result with the function's return value or the thrown error\n *\n * @example\n * ```typescript\n * // Wrap JSON.parse\n * const parsed = from(() => JSON.parse('{\"key\": \"value\"}'));\n * // parsed: { ok: true, value: { key: \"value\" } }\n *\n * const error = from(() => JSON.parse('invalid'));\n * // error: { ok: false, error: SyntaxError }\n * ```\n */\nexport function from<T>(fn: () => T): Result<T, unknown>;\n/**\n * Wraps a synchronous throwing function in a Result with custom error mapping.\n *\n * Use this overload when you want to map thrown exceptions to your typed error union.\n *\n * @param fn - The synchronous function to execute (may throw)\n * @param onError - Function to map the thrown exception to a typed error\n * @returns A Result with the function's return value or the mapped error\n *\n * @example\n * ```typescript\n * // Map exceptions to typed errors\n * const parsed = from(\n * () => JSON.parse(input),\n * (cause) => ({ type: 'PARSE_ERROR' as const, cause })\n * );\n * // parsed.error: { type: 'PARSE_ERROR', cause: SyntaxError }\n *\n * // Map to simple error codes\n * const value = from(\n * () => riskyOperation(),\n * () => 'OPERATION_FAILED' as const\n * );\n * ```\n */\nexport function from<T, E>(fn: () => T, onError: (cause: unknown) => E): Result<T, E>;\nexport function from<T, E>(fn: () => T, onError?: (cause: unknown) => E) {\n try {\n return ok(fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Wraps a Promise in a Result, converting rejections to errors.\n *\n * ## When to Use\n *\n * Use `fromPromise()` when:\n * - You have an existing Promise that might reject\n * - You want to convert Promise rejections to typed errors\n * - You're working with libraries that return Promises (fetch, database clients)\n * - You need to handle rejections without .catch() chains\n *\n * ## Why Use This\n *\n * - **Type-safe errors**: Convert Promise rejections to typed Result errors\n * - **Composable**: Results can be chained with `andThen`, `map`, etc.\n * - **Explicit handling**: Forces you to handle errors explicitly\n * - **No .catch() chains**: Cleaner than Promise.catch() patterns\n *\n * @param promise - The Promise to await (may reject)\n * @returns A Promise resolving to a Result with the resolved value or rejection reason\n *\n * @example\n * ```typescript\n * // Wrap fetch\n * const result = await fromPromise(\n * fetch('/api').then(r => r.json())\n * );\n * // result.ok: true if fetch succeeded, false if rejected\n * ```\n */\nexport function fromPromise<T>(promise: Promise<T>): AsyncResult<T, unknown>;\n/**\n * Wraps a Promise in a Result with custom error mapping.\n *\n * Use this overload when you want to map Promise rejections to your typed error union.\n *\n * @param promise - The Promise to await (may reject)\n * @param onError - Function to map the rejection reason to a typed error\n * @returns A Promise resolving to a Result with the resolved value or mapped error\n *\n * @example\n * ```typescript\n * // Map fetch errors to typed errors\n * const result = await fromPromise(\n * fetch('/api').then(r => {\n * if (!r.ok) throw new Error(`HTTP ${r.status}`);\n * return r.json();\n * }),\n * () => 'FETCH_FAILED' as const\n * );\n * // result.error: 'FETCH_FAILED' if fetch failed\n *\n * // Map with error details\n * const data = await fromPromise(\n * db.query(sql),\n * (cause) => ({ type: 'DB_ERROR' as const, message: String(cause) })\n * );\n * ```\n */\nexport function fromPromise<T, E>(\n promise: Promise<T>,\n onError: (cause: unknown) => E\n): AsyncResult<T, E>;\nexport async function fromPromise<T, E>(\n promise: Promise<T>,\n onError?: (cause: unknown) => E\n): AsyncResult<T, E | unknown> {\n try {\n return ok(await promise);\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Wraps an async function in a Result, catching both thrown exceptions and Promise rejections.\n *\n * ## When to Use\n *\n * Use `tryAsync()` when:\n * - You have an async function that might throw or reject\n * - You want to convert both exceptions and rejections to typed errors\n * - You're creating new async functions (use `fromPromise` for existing Promises)\n * - You need to handle errors without try/catch or .catch()\n *\n * ## Why Use This Instead of `fromPromise`\n *\n * - **Function form**: Takes a function, not a Promise (lazy evaluation)\n * - **Catches both**: Handles both thrown exceptions and Promise rejections\n * - **Cleaner syntax**: No need to wrap in Promise manually\n *\n * @param fn - The async function to execute (may throw or reject)\n * @returns A Promise resolving to a Result with the function's return value or error\n *\n * @example\n * ```typescript\n * // Wrap async function\n * const result = await tryAsync(async () => {\n * const data = await fetchData();\n * return processData(data);\n * });\n * ```\n */\nexport function tryAsync<T>(fn: () => Promise<T>): AsyncResult<T, unknown>;\n/**\n * Wraps an async function in a Result with custom error mapping.\n *\n * Use this overload when you want to map errors to your typed error union.\n *\n * @param fn - The async function to execute (may throw or reject)\n * @param onError - Function to map the error (exception or rejection) to a typed error\n * @returns A Promise resolving to a Result with the function's return value or mapped error\n *\n * @example\n * ```typescript\n * // Map errors to typed errors\n * const result = await tryAsync(\n * async () => await fetchData(),\n * () => 'FETCH_ERROR' as const\n * );\n *\n * // Map with error details\n * const data = await tryAsync(\n * async () => await processFile(path),\n * (cause) => ({ type: 'PROCESSING_ERROR' as const, cause })\n * );\n * ```\n */\nexport function tryAsync<T, E>(\n fn: () => Promise<T>,\n onError: (cause: unknown) => E\n): AsyncResult<T, E>;\nexport async function tryAsync<T, E>(\n fn: () => Promise<T>,\n onError?: (cause: unknown) => E\n): AsyncResult<T, E | unknown> {\n try {\n return ok(await fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Converts a nullable value to a Result.\n *\n * ## When to Use\n *\n * Use `fromNullable()` when:\n * - You have a value that might be `null` or `undefined`\n * - You want to treat null/undefined as an error case\n * - You're working with APIs that return nullable values (DOM APIs, optional properties)\n * - You want to avoid null checks scattered throughout your code\n *\n * ## Why Use This\n *\n * - **Type-safe**: Converts nullable types to non-nullable Results\n * - **Explicit errors**: Forces you to handle null/undefined cases\n * - **Composable**: Results can be chained with `andThen`, `map`, etc.\n * - **No null checks**: Eliminates need for `if (value == null)` checks\n *\n * @param value - The value that may be null or undefined\n * @param onNull - Function that returns an error when value is null/undefined\n * @returns A Result with the value if not null/undefined, otherwise the error from `onNull`\n *\n * @example\n * ```typescript\n * // Convert DOM element lookup\n * const element = fromNullable(\n * document.getElementById('app'),\n * () => 'ELEMENT_NOT_FOUND' as const\n * );\n *\n * // Convert optional property\n * const userId = fromNullable(\n * user.id,\n * () => 'USER_ID_MISSING' as const\n * );\n *\n * // Convert database query result\n * const record = fromNullable(\n * await db.find(id),\n * () => ({ type: 'NOT_FOUND' as const, id })\n * );\n * ```\n */\nexport function fromNullable<T, E>(\n value: T | null | undefined,\n onNull: () => E\n): Result<T, E> {\n return value != null ? ok(value) : err(onNull());\n}\n\n// =============================================================================\n// Transformers\n// =============================================================================\n\n/**\n * Transforms the success value of a Result.\n *\n * ## When to Use\n *\n * Use `map()` when:\n * - You need to transform a success value to another type\n * - You want to apply a pure function to the value\n * - You're building a pipeline of transformations\n * - The transformation cannot fail (use `andThen` if it can fail)\n *\n * ## Why Use This\n *\n * - **Functional style**: Composable, chainable transformations\n * - **Error-preserving**: Errors pass through unchanged\n * - **Type-safe**: TypeScript tracks the transformation\n * - **No unwrapping**: Avoids manual `if (r.ok)` checks\n *\n * @param r - The Result to transform\n * @param fn - Pure function that transforms the success value (must not throw)\n * @returns A new Result with the transformed value, or the original error if `r` was an error\n *\n * @example\n * ```typescript\n * // Transform numeric value\n * const doubled = map(ok(21), n => n * 2);\n * // doubled: { ok: true, value: 42 }\n *\n * // Transform object property\n * const name = map(fetchUser(id), user => user.name);\n *\n * // Chain transformations\n * const formatted = map(\n * map(parseNumber(input), n => n * 2),\n * n => `Result: ${n}`\n * );\n * ```\n */\nexport function map<T, U, E, C>(\n r: Result<T, E, C>,\n fn: (value: T) => U\n): Result<U, E, C> {\n return r.ok ? ok(fn(r.value)) : r;\n}\n\n/**\n * Transforms the error value of a Result.\n *\n * ## When to Use\n *\n * Use `mapError()` when:\n * - You need to normalize or transform error types\n * - You want to convert errors to a different error type\n * - You're building error handling pipelines\n * - You need to format error messages or codes\n *\n * ## Why Use This\n *\n * - **Error normalization**: Convert errors to a common format\n * - **Type transformation**: Change error type while preserving value type\n * - **Composable**: Can be chained with other transformers\n * - **Success-preserving**: Success values pass through unchanged\n *\n * @param r - The Result to transform\n * @param fn - Function that transforms the error value (must not throw)\n * @returns A new Result with the original value, or the transformed error if `r` was an error\n *\n * @example\n * ```typescript\n * // Normalize error codes\n * const normalized = mapError(err('not_found'), e => e.toUpperCase());\n * // normalized: { ok: false, error: 'NOT_FOUND' }\n *\n * // Convert error types\n * const typed = mapError(\n * err('404'),\n * code => ({ type: 'HTTP_ERROR' as const, status: parseInt(code) })\n * );\n *\n * // Format error messages\n * const formatted = mapError(\n * err('PARSE_ERROR'),\n * code => `Failed to parse: ${code}`\n * );\n * ```\n */\nexport function mapError<T, E, F, C>(\n r: Result<T, E, C>,\n fn: (error: E) => F\n): Result<T, F, C> {\n return r.ok ? r : err(fn(r.error), { cause: r.cause });\n}\n\n/**\n * Pattern matches on a Result, calling the appropriate handler.\n *\n * ## When to Use\n *\n * Use `match()` when:\n * - You need to handle both success and error cases\n * - You want to transform a Result to a different type\n * - You need exhaustive handling (both cases must be handled)\n * - You're building user-facing messages or responses\n *\n * ## Why Use This\n *\n * - **Exhaustive**: Forces you to handle both success and error cases\n * - **Type-safe**: TypeScript ensures both handlers are provided\n * - **Functional**: Pattern matching style, similar to Rust's `match` or Haskell's `case`\n * - **Single expression**: Can be used in expressions, not just statements\n *\n * @param r - The Result to match\n * @param handlers - Object with `ok` and `err` handler functions\n * @param handlers.ok - Function called with the success value\n * @param handlers.err - Function called with the error and optional cause\n * @returns The return value of the appropriate handler (both must return the same type `R`)\n *\n * @example\n * ```typescript\n * // Build user-facing messages\n * const message = match(result, {\n * ok: (user) => `Hello ${user.name}`,\n * err: (error) => `Error: ${error}`,\n * });\n *\n * // Transform to API response\n * const response = match(operation(), {\n * ok: (data) => ({ status: 200, body: data }),\n * err: (error) => ({ status: 400, error: String(error) }),\n * });\n *\n * // Handle with cause\n * const response = match(result, {\n * ok: (value) => ({ status: 'success', data: value }),\n * err: (error, cause) => ({ status: 'error', error, cause }),\n * });\n * ```\n */\nexport function match<T, E, C, R>(\n r: Result<T, E, C>,\n handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }\n): R {\n return r.ok ? handlers.ok(r.value) : handlers.err(r.error, r.cause);\n}\n\n/**\n * Chains Results together (flatMap/monadic bind).\n *\n * ## When to Use\n *\n * Use `andThen()` when:\n * - You need to chain operations that can fail\n * - The next operation depends on the previous success value\n * - You're building a pipeline of dependent operations\n * - You want to avoid nested `if (r.ok)` checks\n *\n * ## Why Use This Instead of `map`\n *\n * - **Can fail**: The chained function returns a Result (can fail)\n * - **Short-circuits**: If first Result fails, second operation never runs\n * - **Error accumulation**: Errors from both operations are in the union\n * - **Composable**: Can chain multiple operations together\n *\n * ## Common Pattern\n *\n * This is the fundamental building block for Result pipelines:\n * ```typescript\n * andThen(operation1(), value1 =>\n * andThen(operation2(value1), value2 =>\n * ok({ value1, value2 })\n * )\n * )\n * ```\n *\n * @param r - The first Result\n * @param fn - Function that takes the success value and returns a new Result (may fail)\n * @returns The Result from `fn` if `r` was successful, otherwise the original error\n *\n * @example\n * ```typescript\n * // Chain dependent operations\n * const userPosts = andThen(\n * fetchUser('1'),\n * user => fetchPosts(user.id)\n * );\n *\n * // Build complex pipelines\n * const result = andThen(parseInput(input), parsed =>\n * andThen(validate(parsed), validated =>\n * process(validated)\n * )\n * );\n *\n * // Chain with different error types\n * const data = andThen(\n * fetchUser(id), // Returns Result<User, 'FETCH_ERROR'>\n * user => fetchPosts(user.id) // Returns Result<Post[], 'NOT_FOUND'>\n * );\n * // data.error: 'FETCH_ERROR' | 'NOT_FOUND'\n * ```\n */\nexport function andThen<T, U, E, F, C1, C2>(\n r: Result<T, E, C1>,\n fn: (value: T) => Result<U, F, C2>\n): Result<U, E | F, C1 | C2> {\n return r.ok ? fn(r.value) : r;\n}\n\n/**\n * Executes a side effect on a successful Result without changing it.\n *\n * ## When to Use\n *\n * Use `tap()` when:\n * - You need to log, debug, or observe success values\n * - You want to perform side effects in a pipeline\n * - You need to mutate external state based on success\n * - You're debugging and want to inspect values without breaking the chain\n *\n * ## Why Use This\n *\n * - **Non-breaking**: Doesn't change the Result, just performs side effect\n * - **Composable**: Can be inserted anywhere in a pipeline\n * - **Type-preserving**: Returns the same Result type\n * - **Lazy**: Side effect only runs if Result is successful\n *\n * @param r - The Result to tap\n * @param fn - Side effect function called with the success value (return value ignored)\n * @returns The original Result unchanged (for chaining)\n *\n * @example\n * ```typescript\n * // Log success values\n * const logged = tap(result, user => console.log('Got user:', user.name));\n * // logged === result, but console.log was called\n *\n * // Debug in pipeline\n * const debugged = pipe(\n * fetchUser(id),\n * r => tap(r, user => console.log('Fetched:', user)),\n * r => map(r, user => user.name)\n * );\n *\n * // Mutate external state\n * const tracked = tap(result, data => {\n * analytics.track('operation_success', data);\n * });\n * ```\n */\nexport function tap<T, E, C>(\n r: Result<T, E, C>,\n fn: (value: T) => void\n): Result<T, E, C> {\n if (r.ok) fn(r.value);\n return r;\n}\n\n/**\n * Executes a side effect on an error Result without changing it.\n *\n * ## When to Use\n *\n * Use `tapError()` when:\n * - You need to log, debug, or observe error values\n * - You want to perform side effects on errors in a pipeline\n * - You need to report errors to external systems (logging, monitoring)\n * - You're debugging and want to inspect errors without breaking the chain\n *\n * ## Why Use This\n *\n * - **Non-breaking**: Doesn't change the Result, just performs side effect\n * - **Composable**: Can be inserted anywhere in a pipeline\n * - **Type-preserving**: Returns the same Result type\n * - **Lazy**: Side effect only runs if Result is an error\n *\n * @param r - The Result to tap\n * @param fn - Side effect function called with the error and optional cause (return value ignored)\n * @returns The original Result unchanged (for chaining)\n *\n * @example\n * ```typescript\n * // Log errors\n * const logged = tapError(result, (error, cause) => {\n * console.error('Error:', error, cause);\n * });\n *\n * // Report to error tracking\n * const tracked = tapError(result, (error, cause) => {\n * errorTracker.report(error, cause);\n * });\n *\n * // Debug in pipeline\n * const debugged = pipe(\n * operation(),\n * r => tapError(r, (err, cause) => console.error('Failed:', err)),\n * r => mapError(r, err => 'FORMATTED_ERROR')\n * );\n * ```\n */\nexport function tapError<T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => void\n): Result<T, E, C> {\n if (!r.ok) fn(r.error, r.cause);\n return r;\n}\n\n/**\n * Transforms the success value of a Result, catching any errors thrown by the transform.\n *\n * ## When to Use\n *\n * Use `mapTry()` when:\n * - Your transform function might throw exceptions\n * - You want to convert transform errors to typed errors\n * - You're working with libraries that throw (e.g., JSON.parse, Date parsing)\n * - You need to handle both Result errors and transform exceptions\n *\n * ## Why Use This Instead of `map`\n *\n * - **Exception-safe**: Catches exceptions from the transform function\n * - **Error mapping**: Converts thrown exceptions to typed errors\n * - **Dual error handling**: Handles both Result errors and transform exceptions\n *\n * @param result - The Result to transform\n * @param transform - Function to transform the success value (may throw exceptions)\n * @param onError - Function to map thrown exceptions to a typed error\n * @returns A Result with:\n * - Transformed value if both Result and transform succeed\n * - Original error if Result was an error\n * - Transform error if transform threw an exception\n *\n * @example\n * ```typescript\n * // Safe JSON parsing\n * const parsed = mapTry(\n * ok('{\"key\": \"value\"}'),\n * JSON.parse,\n * () => 'PARSE_ERROR' as const\n * );\n *\n * // Safe date parsing\n * const date = mapTry(\n * ok('2024-01-01'),\n * str => new Date(str),\n * () => 'INVALID_DATE' as const\n * );\n *\n * // Transform with error details\n * const processed = mapTry(\n * result,\n * value => riskyTransform(value),\n * (cause) => ({ type: 'TRANSFORM_ERROR' as const, cause })\n * );\n * ```\n */\nexport function mapTry<T, U, E, F, C>(\n result: Result<T, E, C>,\n transform: (value: T) => U,\n onError: (cause: unknown) => F\n): Result<U, E | F, C | unknown> {\n if (!result.ok) return result;\n try {\n return ok(transform(result.value));\n } catch (error) {\n return err(onError(error), { cause: error });\n }\n}\n\n/**\n * Transforms the error value of a Result, catching any errors thrown by the transform.\n *\n * ## When to Use\n *\n * Use `mapErrorTry()` when:\n * - Your error transform function might throw exceptions\n * - You're doing complex error transformations (e.g., string formatting, object construction)\n * - You want to handle both Result errors and transform exceptions\n * - You need to safely normalize error types\n *\n * ## Why Use This Instead of `mapError`\n *\n * - **Exception-safe**: Catches exceptions from the error transform function\n * - **Error mapping**: Converts thrown exceptions to typed errors\n * - **Dual error handling**: Handles both Result errors and transform exceptions\n *\n * @param result - The Result to transform\n * @param transform - Function to transform the error value (may throw exceptions)\n * @param onError - Function to map thrown exceptions to a typed error\n * @returns A Result with:\n * - Original value if Result was successful\n * - Transformed error if both Result was error and transform succeeded\n * - Transform error if transform threw an exception\n *\n * @example\n * ```typescript\n * // Safe error formatting\n * const formatted = mapErrorTry(\n * err('not_found'),\n * e => e.toUpperCase(), // Might throw if e is not a string\n * () => 'FORMAT_ERROR' as const\n * );\n *\n * // Complex error transformation\n * const normalized = mapErrorTry(\n * result,\n * error => ({ type: 'NORMALIZED', message: String(error) }),\n * () => 'TRANSFORM_ERROR' as const\n * );\n * ```\n */\nexport function mapErrorTry<T, E, F, G, C>(\n result: Result<T, E, C>,\n transform: (error: E) => F,\n onError: (cause: unknown) => G\n): Result<T, F | G, C | unknown> {\n if (result.ok) return result;\n try {\n return err(transform(result.error), { cause: result.cause });\n } catch (error) {\n return err(onError(error), { cause: error });\n }\n}\n\n/**\n * Transforms both the success value and error value of a Result simultaneously.\n *\n * ## When to Use\n *\n * Use `bimap()` when:\n * - You need to transform both success and error in one operation\n * - You're normalizing Results to a common format\n * - You want symmetric transformation of both cases\n * - You're building adapters between different Result types\n *\n * ## Why Use This Instead of `map` + `mapError`\n *\n * - **Single operation**: Transforms both cases in one call\n * - **Clearer intent**: Shows you're handling both cases symmetrically\n * - **Less code**: Avoids chaining map and mapError\n *\n * @param r - The Result to transform\n * @param onOk - Function that transforms the success value\n * @param onErr - Function that transforms the error value\n * @returns A new Result with transformed value or transformed error\n *\n * @example\n * ```typescript\n * // Normalize to API response format\n * const response = bimap(\n * fetchUser(id),\n * user => ({ status: 'success', data: user }),\n * error => ({ status: 'error', code: error })\n * );\n *\n * // Transform types\n * const stringified = bimap(\n * parseNumber(input),\n * n => `Value: ${n}`,\n * e => `Error: ${e}`\n * );\n *\n * // Adapt between error types\n * const adapted = bimap(\n * externalResult,\n * value => internalValue(value),\n * error => internalError(error)\n * );\n * ```\n */\nexport function bimap<T, U, E, F, C>(\n r: Result<T, E, C>,\n onOk: (value: T) => U,\n onErr: (error: E) => F\n): Result<U, F, C> {\n return r.ok ? ok(onOk(r.value)) : err(onErr(r.error), { cause: r.cause });\n}\n\n/**\n * Recovers from an error by returning a new Result.\n * Similar to neverthrow's `.orElse()`.\n *\n * ## When to Use\n *\n * Use `orElse()` when:\n * - You want to recover from errors with fallback operations\n * - The recovery might also fail (returns a Result)\n * - You need to chain fallback strategies\n * - You're implementing retry or fallback patterns\n *\n * ## Why Use This\n *\n * - **Fallback chains**: Try alternative operations on failure\n * - **Error recovery**: Convert errors to success with fallback values\n * - **Composable**: Can chain multiple orElse calls for cascading fallbacks\n * - **Type-safe**: TypeScript tracks the error union through recovery\n *\n * @param r - The Result to potentially recover from\n * @param fn - Function that takes the error and returns a new Result (may succeed or fail)\n * @returns The original Result if successful, or the result of the recovery function\n *\n * @example\n * ```typescript\n * // Fallback to default user\n * const user = orElse(\n * fetchUser(id),\n * error => error === 'NOT_FOUND' ? ok(defaultUser) : err(error)\n * );\n *\n * // Try cache, then database, then fail\n * const data = orElse(\n * orElse(\n * fetchFromCache(key),\n * () => fetchFromDatabase(key)\n * ),\n * () => err('DATA_UNAVAILABLE' as const)\n * );\n *\n * // Convert specific errors to success\n * const result = orElse(\n * riskyOperation(),\n * error => error.code === 'RETRY' ? ok(defaultValue) : err(error)\n * );\n * ```\n */\nexport function orElse<T, E, E2, C, C2>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => Result<T, E2, C2>\n): Result<T, E2, C2> {\n return r.ok ? r : fn(r.error, r.cause);\n}\n\n/**\n * Async version of orElse for recovering from errors with async operations.\n *\n * @param r - The Result or AsyncResult to potentially recover from\n * @param fn - Async function that takes the error and returns a new Result\n * @returns Promise of the original Result if successful, or the result of the recovery function\n *\n * @example\n * ```typescript\n * // Try primary API, fall back to secondary\n * const data = await orElseAsync(\n * await fetchFromPrimaryApi(),\n * async (error) => {\n * if (error === 'UNAVAILABLE') {\n * return await fetchFromSecondaryApi();\n * }\n * return err(error);\n * }\n * );\n * ```\n */\nexport async function orElseAsync<T, E, E2, C, C2>(\n r: Result<T, E, C> | Promise<Result<T, E, C>>,\n fn: (error: E, cause?: C) => Result<T, E2, C2> | Promise<Result<T, E2, C2>>\n): Promise<Result<T, E2, C2>> {\n const resolved = await r;\n return resolved.ok ? resolved : fn(resolved.error, resolved.cause);\n}\n\n/**\n * Recovers from an error by returning a plain value (not a Result).\n * Useful when you want to provide a default value on error.\n *\n * ## When to Use\n *\n * Use `recover()` when:\n * - You want to provide a fallback value on error\n * - Recovery cannot fail (unlike orElse which returns a Result)\n * - You're implementing default value patterns\n * - You want to guarantee a successful Result\n *\n * ## Why Use This Instead of `orElse`\n *\n * - **Simpler**: Recovery function returns plain value, not Result\n * - **Guaranteed success**: Always returns ok() after recovery\n * - **Clearer intent**: Shows recovery cannot fail\n *\n * @param r - The Result to potentially recover from\n * @param fn - Function that takes the error and returns a recovery value\n * @returns The original Result if successful, or ok(recoveryValue) if error\n *\n * @example\n * ```typescript\n * // Provide default user on NOT_FOUND\n * const user = recover(\n * fetchUser(id),\n * error => error === 'NOT_FOUND' ? defaultUser : guestUser\n * );\n *\n * // Convert all errors to default\n * const config = recover(\n * loadConfig(),\n * () => defaultConfig\n * );\n *\n * // Recover with error-based defaults\n * const value = recover(\n * parseNumber(input),\n * error => error === 'EMPTY' ? 0 : -1\n * );\n * ```\n */\nexport function recover<T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => T\n): Result<T, never, never> {\n return r.ok ? ok(r.value) : ok(fn(r.error, r.cause));\n}\n\n/**\n * Async version of recover for recovering with async operations.\n *\n * @param r - The Result or AsyncResult to potentially recover from\n * @param fn - Async function that takes the error and returns a recovery value\n * @returns Promise of ok(value) - either original or recovered\n *\n * @example\n * ```typescript\n * // Recover by fetching default from API\n * const user = await recoverAsync(\n * await fetchUser(id),\n * async (error) => await fetchDefaultUser()\n * );\n * ```\n */\nexport async function recoverAsync<T, E, C>(\n r: Result<T, E, C> | Promise<Result<T, E, C>>,\n fn: (error: E, cause?: C) => T | Promise<T>\n): Promise<Result<T, never, never>> {\n const resolved = await r;\n if (resolved.ok) return ok(resolved.value);\n return ok(await fn(resolved.error, resolved.cause));\n}\n\n// =============================================================================\n// Batch Operations\n// =============================================================================\n\ntype AllValues<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> ? V : never;\n};\ntype AllErrors<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> ? E : never;\n}[number];\ntype AllCauses<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> ? C : never;\n}[number];\n\n/**\n * Combines multiple Results into one, requiring all to succeed.\n *\n * ## When to Use\n *\n * Use `all()` when:\n * - You have multiple independent operations that all must succeed\n * - You want to short-circuit on the first error (fail-fast)\n * - You need all values together (e.g., combining API responses)\n * - Performance matters (stops on first error, doesn't wait for all)\n *\n * ## Why Use This\n *\n * - **Fail-fast**: Stops immediately on first error (better performance)\n * - **Type-safe**: TypeScript infers the array type from input\n * - **Short-circuit**: Doesn't evaluate remaining Results after error\n * - **Composable**: Can be chained with other operations\n *\n * ## Important\n *\n * - **Short-circuits**: Returns first error immediately, doesn't wait for all Results\n * - **All must succeed**: If any Result fails, the entire operation fails\n * - **Use `allSettled`**: If you need to collect all errors (e.g., form validation)\n *\n * @param results - Array of Results to combine (all must succeed)\n * @returns A Result with an array of all success values, or the first error encountered\n *\n * @example\n * ```typescript\n * // Combine multiple successful Results\n * const combined = all([ok(1), ok(2), ok(3)]);\n * // combined: { ok: true, value: [1, 2, 3] }\n *\n * // Short-circuits on first error\n * const error = all([ok(1), err('ERROR'), ok(3)]);\n * // error: { ok: false, error: 'ERROR' }\n * // Note: ok(3) is never evaluated\n *\n * // Combine API responses\n * const data = all([\n * fetchUser(id),\n * fetchPosts(id),\n * fetchComments(id)\n * ]);\n * // data.value: [user, posts, comments] if all succeed\n * ```\n */\nexport function all<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): Result<AllValues<T>, AllErrors<T>, AllCauses<T>> {\n const values: unknown[] = [];\n for (const result of results) {\n if (!result.ok) {\n return result as unknown as Result<AllValues<T>, AllErrors<T>, AllCauses<T>>;\n }\n values.push(result.value);\n }\n return ok(values) as Result<AllValues<T>, AllErrors<T>, AllCauses<T>>;\n}\n\n/**\n * Combines multiple Results or Promises of Results into one (async version of `all`).\n *\n * ## When to Use\n *\n * Use `allAsync()` when:\n * - You have multiple async operations that all must succeed\n * - You want to run operations in parallel (better performance)\n * - You want to short-circuit on the first error (fail-fast)\n * - You need all values together from parallel operations\n *\n * ## Why Use This Instead of `all`\n *\n * - **Parallel execution**: All Promises start immediately (faster)\n * - **Async support**: Works with Promises and AsyncResults\n * - **Promise rejection handling**: Converts Promise rejections to `PromiseRejectedError`\n *\n * ## Important\n *\n * - **Short-circuits**: Returns first error immediately, cancels remaining operations\n * - **Parallel**: All operations start simultaneously (unlike sequential `andThen`)\n * - **Use `allSettledAsync`**: If you need to collect all errors\n *\n * @param results - Array of Results or Promises of Results to combine (all must succeed)\n * @returns A Promise resolving to a Result with an array of all success values, or the first error\n *\n * @example\n * ```typescript\n * // Parallel API calls\n * const combined = await allAsync([\n * fetchUser('1'),\n * fetchPosts('1'),\n * fetchComments('1')\n * ]);\n * // All three calls start simultaneously\n * // combined: { ok: true, value: [user, posts, comments] } if all succeed\n *\n * // Mix Results and Promises\n * const data = await allAsync([\n * ok(cachedUser), // Already resolved\n * fetchPosts(userId), // Promise\n * ]);\n * ```\n */\nexport async function allAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never },\n { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number] | PromiseRejectedError,\n { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number] | PromiseRejectionCause\n >\n> {\n type Values = { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never };\n type Errors = { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number] | PromiseRejectedError;\n type Causes = { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number] | PromiseRejectionCause;\n\n if (results.length === 0) {\n return ok([]) as Result<Values, Errors, Causes>;\n }\n\n return new Promise((resolve) => {\n let settled = false;\n let pendingCount = results.length;\n const values: unknown[] = new Array(results.length);\n\n for (let i = 0; i < results.length; i++) {\n const index = i;\n Promise.resolve(results[index])\n .catch((reason) => err(\n { type: \"PROMISE_REJECTED\" as const, cause: reason },\n { cause: { type: \"PROMISE_REJECTION\" as const, reason } as PromiseRejectionCause }\n ))\n .then((result) => {\n if (settled) return;\n\n if (!result.ok) {\n settled = true;\n resolve(result as Result<Values, Errors, Causes>);\n return;\n }\n\n values[index] = result.value;\n pendingCount--;\n\n if (pendingCount === 0) {\n resolve(ok(values) as Result<Values, Errors, Causes>);\n }\n });\n }\n });\n}\n\nexport type SettledError<E, C = unknown> = { error: E; cause?: C };\n\ntype AllSettledResult<T extends readonly Result<unknown, unknown, unknown>[]> = Result<\n AllValues<T>,\n SettledError<AllErrors<T>, AllCauses<T>>[]\n>;\n\n/**\n * Combines multiple Results, collecting all errors instead of short-circuiting.\n *\n * ## When to Use\n *\n * Use `allSettled()` when:\n * - You need to see ALL errors, not just the first one\n * - You're doing form validation (show all field errors)\n * - You want to collect partial results (some succeed, some fail)\n * - You need to process all Results regardless of failures\n *\n * ## Why Use This Instead of `all`\n *\n * - **Collects all errors**: Returns array of all errors, not just first\n * - **No short-circuit**: Evaluates all Results even if some fail\n * - **Partial success**: Can see which operations succeeded and which failed\n * - **Better UX**: Show users all validation errors at once\n *\n * ## Important\n *\n * - **No short-circuit**: All Results are evaluated (slower if many fail early)\n * - **Error array**: Returns array of `{ error, cause }` objects, not single error\n * - **Use `all`**: If you want fail-fast behavior (better performance)\n *\n * @param results - Array of Results to combine (all are evaluated)\n * @returns A Result with:\n * - Array of all success values if all succeed\n * - Array of `{ error, cause }` objects if any fail\n *\n * @example\n * ```typescript\n * // Form validation - show all errors\n * const validated = allSettled([\n * validateEmail(email),\n * validatePassword(password),\n * validateAge(age),\n * ]);\n * // If email and password fail:\n * // { ok: false, error: [\n * // { error: 'INVALID_EMAIL' },\n * // { error: 'WEAK_PASSWORD' }\n * // ]}\n *\n * // Collect partial results\n * const results = allSettled([\n * fetchUser('1'), // succeeds\n * fetchUser('2'), // fails\n * fetchUser('3'), // succeeds\n * ]);\n * // Can see which succeeded and which failed\n * ```\n */\nexport function allSettled<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): AllSettledResult<T> {\n const values: unknown[] = [];\n const errors: SettledError<unknown>[] = [];\n\n for (const result of results) {\n if (result.ok) {\n values.push(result.value);\n } else {\n errors.push({ error: result.error, cause: result.cause });\n }\n }\n\n if (errors.length > 0) {\n return err(errors) as unknown as AllSettledResult<T>;\n }\n\n return ok(values) as unknown as AllSettledResult<T>;\n}\n\n/**\n * Splits an array of Results into separate arrays of success values and errors.\n *\n * ## When to Use\n *\n * Use `partition()` when:\n * - You have an array of Results and need to separate successes from failures\n * - You want to process successes and errors separately\n * - You're collecting results from multiple operations (some may fail)\n * - You need to handle partial success scenarios\n *\n * ## Why Use This\n *\n * - **Simple separation**: One call splits successes and errors\n * - **Type-safe**: TypeScript knows `values` is `T[]` and `errors` is `E[]`\n * - **No unwrapping**: Doesn't require manual `if (r.ok)` checks\n * - **Preserves order**: Maintains original array order in both arrays\n *\n * ## Common Pattern\n *\n * Often used after `Promise.all()` with Results:\n * ```typescript\n * const results = await Promise.all(ids.map(id => fetchUser(id)));\n * const { values: users, errors } = partition(results);\n * // Process successful users, handle errors separately\n * ```\n *\n * @param results - Array of Results to partition\n * @returns An object with:\n * - `values`: Array of all success values (type `T[]`)\n * - `errors`: Array of all error values (type `E[]`)\n *\n * @example\n * ```typescript\n * // Split successes and errors\n * const results = [ok(1), err('ERROR_1'), ok(3), err('ERROR_2')];\n * const { values, errors } = partition(results);\n * // values: [1, 3]\n * // errors: ['ERROR_1', 'ERROR_2']\n *\n * // Process batch operations\n * const userResults = await Promise.all(userIds.map(id => fetchUser(id)));\n * const { values: users, errors: fetchErrors } = partition(userResults);\n *\n * // Process successful users\n * users.forEach(user => processUser(user));\n *\n * // Handle errors\n * fetchErrors.forEach(error => logError(error));\n * ```\n */\nexport function partition<T, E, C>(\n results: readonly Result<T, E, C>[]\n): { values: T[]; errors: E[] } {\n const values: T[] = [];\n const errors: E[] = [];\n\n for (const result of results) {\n if (result.ok) {\n values.push(result.value);\n } else {\n errors.push(result.error);\n }\n }\n\n return { values, errors };\n}\n\ntype AnyValue<T extends readonly Result<unknown, unknown, unknown>[]> =\n T[number] extends Result<infer U, unknown, unknown> ? U : never;\ntype AnyErrors<T extends readonly Result<unknown, unknown, unknown>[]> = {\n -readonly [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> ? E : never;\n}[number];\ntype AnyCauses<T extends readonly Result<unknown, unknown, unknown>[]> = {\n -readonly [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> ? C : never;\n}[number];\n\n/**\n * Returns the first successful Result from an array (succeeds fast).\n *\n * ## When to Use\n *\n * Use `any()` when:\n * - You have multiple fallback options and need the first that succeeds\n * - You're trying multiple strategies (e.g., cache → DB → API)\n * - You want fail-fast success (stops on first success)\n * - You have redundant data sources and any one will do\n *\n * ## Why Use This\n *\n * - **Succeeds fast**: Returns immediately on first success (better performance)\n * - **Fallback pattern**: Perfect for trying multiple options\n * - **Short-circuits**: Stops evaluating after first success\n * - **Type-safe**: TypeScript infers the success type\n *\n * ## Important\n *\n * - **First success wins**: Returns first successful Result, ignores rest\n * - **All errors**: If all fail, returns first error (not all errors)\n * - **Empty array**: Returns `EmptyInputError` if array is empty\n * - **Use `all`**: If you need ALL to succeed\n *\n * @param results - Array of Results to check (evaluated in order)\n * @returns The first successful Result, or first error if all fail, or `EmptyInputError` if empty\n *\n * @example\n * ```typescript\n * // Try multiple fallback strategies\n * const data = any([\n * fetchFromCache(id),\n * fetchFromDB(id),\n * fetchFromAPI(id)\n * ]);\n * // Returns first that succeeds\n *\n * // Try multiple formats\n * const parsed = any([\n * parseJSON(input),\n * parseXML(input),\n * parseYAML(input)\n * ]);\n *\n * // All errors case\n * const allErrors = any([err('A'), err('B'), err('C')]);\n * // allErrors: { ok: false, error: 'A' } (first error)\n * ```\n */\nexport function any<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): Result<AnyValue<T>, AnyErrors<T> | EmptyInputError, AnyCauses<T>> {\n type ReturnErr = Result<never, AnyErrors<T> | EmptyInputError, AnyCauses<T>>;\n type ReturnOk = Result<AnyValue<T>, never, AnyCauses<T>>;\n\n if (results.length === 0) {\n return err({\n type: \"EMPTY_INPUT\",\n message: \"any() requires at least one Result\",\n }) as ReturnErr;\n }\n let firstError: Result<never, unknown, unknown> | null = null;\n for (const result of results) {\n if (result.ok) return result as ReturnOk;\n if (!firstError) firstError = result;\n }\n return firstError as ReturnErr;\n}\n\ntype AnyAsyncValue<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> =\n Awaited<T[number]> extends Result<infer U, unknown, unknown> ? U : never;\ntype AnyAsyncErrors<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> = {\n -readonly [K in keyof T]: Awaited<T[K]> extends Result<unknown, infer E, unknown>\n ? E\n : never;\n}[number];\ntype AnyAsyncCauses<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> = {\n -readonly [K in keyof T]: Awaited<T[K]> extends Result<unknown, unknown, infer C>\n ? C\n : never;\n}[number];\n\n/**\n * Returns the first successful Result from an array of Results or Promises (async version of `any`).\n *\n * ## When to Use\n *\n * Use `anyAsync()` when:\n * - You have multiple async fallback options and need the first that succeeds\n * - You're trying multiple async strategies in parallel (cache → DB → API)\n * - You want fail-fast success from parallel operations\n * - You have redundant async data sources and any one will do\n *\n * ## Why Use This Instead of `any`\n *\n * - **Parallel execution**: All Promises start immediately (faster)\n * - **Async support**: Works with Promises and AsyncResults\n * - **Promise rejection handling**: Converts Promise rejections to `PromiseRejectedError`\n *\n * ## Important\n *\n * - **First success wins**: Returns first successful Result (from any Promise)\n * - **Parallel**: All operations run simultaneously\n * - **All errors**: If all fail, returns first error encountered\n *\n * @param results - Array of Results or Promises of Results to check (all start in parallel)\n * @returns A Promise resolving to the first successful Result, or first error if all fail\n *\n * @example\n * ```typescript\n * // Try multiple async fallbacks in parallel\n * const data = await anyAsync([\n * fetchFromCache(id), // Fastest wins\n * fetchFromDB(id),\n * fetchFromAPI(id)\n * ]);\n *\n * // Try multiple API endpoints\n * const response = await anyAsync([\n * fetch('/api/v1/data'),\n * fetch('/api/v2/data'),\n * fetch('/backup-api/data')\n * ]);\n * ```\n */\nexport async function anyAsync<\n const T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[],\n>(\n results: T\n): Promise<\n Result<AnyAsyncValue<T>, AnyAsyncErrors<T> | EmptyInputError | PromiseRejectedError, AnyAsyncCauses<T> | PromiseRejectionCause>\n> {\n type ReturnErr = Result<\n never,\n AnyAsyncErrors<T> | EmptyInputError | PromiseRejectedError,\n AnyAsyncCauses<T> | PromiseRejectionCause\n >;\n type ReturnOk = Result<AnyAsyncValue<T>, never, AnyAsyncCauses<T>>;\n\n if (results.length === 0) {\n return err({\n type: \"EMPTY_INPUT\",\n message: \"anyAsync() requires at least one Result\",\n }) as ReturnErr;\n }\n\n return new Promise((resolve) => {\n let settled = false;\n let pendingCount = results.length;\n let firstError: Result<never, unknown, unknown> | null = null;\n\n for (const item of results) {\n Promise.resolve(item)\n .catch((reason) =>\n err(\n { type: \"PROMISE_REJECTED\" as const, cause: reason },\n { cause: { type: \"PROMISE_REJECTION\" as const, reason } as PromiseRejectionCause }\n )\n )\n .then((result) => {\n if (settled) return;\n\n if (result.ok) {\n settled = true;\n resolve(result as ReturnOk);\n return;\n }\n\n if (!firstError) firstError = result;\n pendingCount--;\n\n if (pendingCount === 0) {\n resolve(firstError as ReturnErr);\n }\n });\n }\n });\n}\n\ntype AllAsyncValues<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> = {\n [K in keyof T]: Awaited<T[K]> extends Result<infer V, unknown, unknown> ? V : never;\n};\ntype AllAsyncErrors<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> = {\n [K in keyof T]: Awaited<T[K]> extends Result<unknown, infer E, unknown> ? E : never;\n}[number];\ntype AllAsyncCauses<T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[]> = {\n [K in keyof T]: Awaited<T[K]> extends Result<unknown, unknown, infer C> ? C : never;\n}[number];\n\n/**\n * Combines multiple Results or Promises of Results, collecting all errors (async version of `allSettled`).\n *\n * ## When to Use\n *\n * Use `allSettledAsync()` when:\n * - You have multiple async operations and need ALL errors\n * - You're doing async form validation (show all field errors)\n * - You want to run operations in parallel and collect all results\n * - You need partial results from parallel operations\n *\n * ## Why Use This Instead of `allSettled`\n *\n * - **Parallel execution**: All Promises start immediately (faster)\n * - **Async support**: Works with Promises and AsyncResults\n * - **Promise rejection handling**: Converts Promise rejections to `PromiseRejectedError`\n *\n * ## Important\n *\n * - **No short-circuit**: All operations complete (even if some fail)\n * - **Parallel**: All operations run simultaneously\n * - **Error array**: Returns array of `{ error, cause }` objects\n *\n * @param results - Array of Results or Promises of Results to combine (all are evaluated)\n * @returns A Promise resolving to a Result with:\n * - Array of all success values if all succeed\n * - Array of `{ error, cause }` objects if any fail\n *\n * @example\n * ```typescript\n * // Async form validation\n * const validated = await allSettledAsync([\n * validateEmailAsync(email),\n * validatePasswordAsync(password),\n * checkUsernameAvailableAsync(username),\n * ]);\n *\n * // Parallel API calls with error collection\n * const results = await allSettledAsync([\n * fetchUser('1'),\n * fetchUser('2'),\n * fetchUser('3'),\n * ]);\n * // Can see which succeeded and which failed\n * ```\n */\nexport async function allSettledAsync<\n const T extends readonly MaybeAsyncResult<unknown, unknown, unknown>[],\n>(\n results: T\n): Promise<Result<AllAsyncValues<T>, SettledError<AllAsyncErrors<T> | PromiseRejectedError, AllAsyncCauses<T> | PromiseRejectionCause>[]>> {\n const settled = await Promise.all(\n results.map((item) =>\n Promise.resolve(item)\n .then((result) => ({ status: \"result\" as const, result }))\n .catch((reason) => ({\n status: \"rejected\" as const,\n error: { type: \"PROMISE_REJECTED\" as const, cause: reason } as PromiseRejectedError,\n cause: { type: \"PROMISE_REJECTION\" as const, reason } as PromiseRejectionCause,\n }))\n )\n );\n\n const values: unknown[] = [];\n const errors: SettledError<unknown, unknown>[] = [];\n\n for (const item of settled) {\n if (item.status === \"rejected\") {\n errors.push({ error: item.error, cause: item.cause });\n } else if (item.result.ok) {\n values.push(item.result.value);\n } else {\n errors.push({ error: item.result.error, cause: item.result.cause });\n }\n }\n\n if (errors.length > 0) {\n return err(errors) as unknown as Result<AllAsyncValues<T>, SettledError<AllAsyncErrors<T> | PromiseRejectedError, AllAsyncCauses<T> | PromiseRejectionCause>[]>;\n }\n return ok(values) as unknown as Result<AllAsyncValues<T>, SettledError<AllAsyncErrors<T> | PromiseRejectedError, AllAsyncCauses<T> | PromiseRejectionCause>[]>;\n}\n","/**\n * @jagreehal/workflow/workflow\n *\n * Workflow orchestration with createWorkflow.\n * Use this for typed async workflows with automatic error inference.\n */\n\nimport {\n run,\n ok,\n err,\n createEarlyExit,\n isEarlyExit,\n type EarlyExit,\n type StepFailureMeta,\n type Result,\n type AsyncResult,\n type UnexpectedError,\n type RunStep,\n type WorkflowEvent,\n type StepOptions,\n type RetryOptions,\n type TimeoutOptions,\n type ErrorOf,\n type CauseOf,\n} from \"./core\";\n\n// Re-export types that workflow users commonly need\nexport type {\n Result,\n AsyncResult,\n UnexpectedError,\n RunStep,\n WorkflowEvent,\n StepOptions,\n} from \"./core\";\n\n// =============================================================================\n// Step Cache Types\n// =============================================================================\n\n/**\n * Interface for step result caching.\n * Implement this interface to provide custom caching strategies.\n * A simple Map<string, Result> works for in-memory caching.\n *\n * Note: Cache stores Result<unknown, unknown, unknown> because different steps\n * have different value/error/cause types. The actual runtime values are preserved;\n * only the static types are widened. For error results, the cause value is encoded\n * in CachedErrorCause to preserve metadata for proper replay.\n *\n * @example\n * // Simple in-memory cache\n * const cache = new Map<string, Result<unknown, unknown, unknown>>();\n *\n * // Or implement custom cache with TTL, LRU, etc.\n * const cache: StepCache = {\n * get: (key) => myCache.get(key),\n * set: (key, result) => myCache.set(key, result, { ttl: 60000 }),\n * has: (key) => myCache.has(key),\n * delete: (key) => myCache.delete(key),\n * clear: () => myCache.clear(),\n * };\n */\nexport interface StepCache {\n get(key: string): Result<unknown, unknown, unknown> | undefined;\n set(key: string, result: Result<unknown, unknown, unknown>): void;\n has(key: string): boolean;\n delete(key: string): boolean;\n clear(): void;\n}\n\n/**\n * Entry for a saved step result with optional metadata.\n * The meta field preserves origin information for proper replay.\n */\nexport interface ResumeStateEntry {\n result: Result<unknown, unknown, unknown>;\n /** Optional metadata for error origin (from step_complete event) */\n meta?: StepFailureMeta;\n}\n\n/**\n * Resume state for workflow replay.\n * Pre-populate step results to skip execution on resume.\n *\n * Note: When saving to persistent storage, you may need custom serialization\n * for complex cause types. JSON.stringify works for simple values, but Error\n * objects and other non-plain types require special handling.\n *\n * @example\n * // Collect from step_complete events using the helper\n * const collector = createStepCollector();\n * const workflow = createWorkflow({ fetchUser }, {\n * onEvent: collector.handleEvent,\n * });\n * // Later: collector.getState() returns ResumeState\n *\n * @example\n * // Resume with saved state\n * const workflow = createWorkflow({ fetchUser }, {\n * resumeState: { steps: savedSteps }\n * });\n */\nexport interface ResumeState {\n /** Map of step keys to their cached results with optional metadata */\n steps: Map<string, ResumeStateEntry>;\n}\n\n/**\n * Create a collector for step results to build resume state.\n *\n * ## When to Use\n *\n * Use `createStepCollector` when you need to:\n * - **Save workflow state** for later replay/resume\n * - **Persist step results** to a database or file system\n * - **Build resume state** from workflow execution\n * - **Enable workflow replay** after application restarts\n *\n * ## Why Use This Instead of Manual Collection\n *\n * - **Automatic filtering**: Only collects `step_complete` events (ignores other events)\n * - **Metadata preservation**: Captures both result and meta for proper error replay\n * - **Type-safe**: Returns properly typed `ResumeState`\n * - **Convenient API**: Simple `handleEvent` → `getState` pattern\n *\n * ## How It Works\n *\n * 1. Create collector and pass `handleEvent` to workflow's `onEvent` option\n * 2. Workflow emits `step_complete` events for keyed steps\n * 3. Collector automatically captures these events\n * 4. Call `getState()` to get the collected `ResumeState`\n * 5. Persist state (e.g., to database) for later resume\n *\n * ## Important Notes\n *\n * - Only steps with a `key` option are collected (unkeyed steps are not saved)\n * - The collector preserves error metadata for proper replay behavior\n * - State can be serialized to JSON (but complex cause types may need custom handling)\n *\n * @returns An object with:\n * - `handleEvent`: Function to pass to workflow's `onEvent` option\n * - `getState`: Get collected resume state (call after workflow execution)\n * - `clear`: Clear all collected state\n *\n * @example\n * ```typescript\n * // Collect state during workflow execution\n * const collector = createStepCollector();\n *\n * const workflow = createWorkflow({ fetchUser, fetchPosts }, {\n * onEvent: collector.handleEvent, // Pass collector's handler\n * });\n *\n * await workflow(async (step) => {\n * // Only keyed steps are collected\n * const user = await step(() => fetchUser(\"1\"), { key: \"user:1\" });\n * const posts = await step(() => fetchPosts(user.id), { key: `posts:${user.id}` });\n * return { user, posts };\n * });\n *\n * // Get collected state for persistence\n * const state = collector.getState();\n * // state.steps contains: 'user:1' and 'posts:1' entries\n *\n * // Save to database\n * await db.saveWorkflowState(workflowId, state);\n * ```\n *\n * @example\n * ```typescript\n * // Resume workflow from saved state\n * const savedState = await db.loadWorkflowState(workflowId);\n * const workflow = createWorkflow({ fetchUser, fetchPosts }, {\n * resumeState: savedState // Pre-populate cache from saved state\n * });\n *\n * // Cached steps skip execution, new steps run normally\n * await workflow(async (step) => {\n * const user = await step(() => fetchUser(\"1\"), { key: \"user:1\" }); // Cache hit\n * const posts = await step(() => fetchPosts(user.id), { key: `posts:${user.id}` }); // Cache hit\n * return { user, posts };\n * });\n * ```\n */\nexport function createStepCollector(): {\n handleEvent: (event: WorkflowEvent<unknown>) => void;\n getState: () => ResumeState;\n clear: () => void;\n} {\n const steps = new Map<string, ResumeStateEntry>();\n\n return {\n handleEvent: (event: WorkflowEvent<unknown>) => {\n if (isStepComplete(event)) {\n steps.set(event.stepKey, { result: event.result, meta: event.meta });\n }\n },\n getState: () => ({ steps: new Map(steps) }),\n clear: () => steps.clear(),\n };\n}\n\n// =============================================================================\n// Cache Entry Encoding (preserves StepFailureMeta for proper replay)\n// =============================================================================\n\n/**\n * Marker for cached error entries that include step failure metadata.\n * This allows us to preserve origin:\"throw\" vs origin:\"result\" when replaying,\n * while also preserving the original cause value for direct cache access.\n * @internal\n */\ninterface CachedErrorCause<C = unknown> {\n __cachedMeta: true;\n /** The original cause from the step result (preserved for direct access) */\n originalCause: C;\n /** Metadata for proper replay behavior */\n meta: StepFailureMeta;\n}\n\nfunction isCachedErrorCause(cause: unknown): cause is CachedErrorCause {\n return (\n typeof cause === \"object\" &&\n cause !== null &&\n (cause as CachedErrorCause).__cachedMeta === true\n );\n}\n\n/**\n * Encode an error result for caching, preserving both the original cause\n * and metadata needed for proper replay.\n */\nfunction encodeCachedError<E, C>(\n error: E,\n meta: StepFailureMeta,\n originalCause: C\n): Result<never, E, CachedErrorCause<C>> {\n return err(error, {\n cause: { __cachedMeta: true, originalCause, meta } as CachedErrorCause<C>,\n });\n}\n\nfunction decodeCachedMeta(cause: unknown): StepFailureMeta {\n if (isCachedErrorCause(cause)) {\n return cause.meta;\n }\n // Fallback for any non-encoded cause (shouldn't happen, but safe default)\n return { origin: \"result\", resultCause: cause };\n}\n\n// =============================================================================\n// createWorkflow Types\n// =============================================================================\n\n/**\n * Constraint for Result-returning functions\n * Used by createWorkflow to ensure only valid functions are passed\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyResultFn = (...args: any[]) => Result<any, any, any> | Promise<Result<any, any, any>>;\n\n/**\n * Extract union of error types from a deps object\n * Example: ErrorsOfDeps<{ fetchUser: typeof fetchUser, fetchPosts: typeof fetchPosts }>\n * yields: 'NOT_FOUND' | 'FETCH_ERROR'\n */\nexport type ErrorsOfDeps<Deps extends Record<string, AnyResultFn>> = {\n [K in keyof Deps]: ErrorOf<Deps[K]>;\n}[keyof Deps];\n\n/**\n * Extract union of cause types from a deps object.\n * Example: CausesOfDeps<{ fetchUser: typeof fetchUser }> where fetchUser returns Result<User, \"NOT_FOUND\", Error>\n * yields: Error\n *\n * Note: This represents the domain cause types from declared functions.\n * However, workflow results may also have unknown causes from step.try failures\n * or uncaught exceptions, so the actual Result cause type is `unknown`.\n */\nexport type CausesOfDeps<Deps extends Record<string, AnyResultFn>> =\n CauseOf<Deps[keyof Deps]>;\n\n/**\n * Non-strict workflow options\n * Returns E | UnexpectedError (safe default)\n */\nexport type WorkflowOptions<E, C = void> = {\n onError?: (error: E | UnexpectedError, stepName?: string) => void;\n /** Unified event stream for workflow and step lifecycle */\n onEvent?: (event: WorkflowEvent<E | UnexpectedError>, ctx: C) => void;\n /** Create per-run context for event correlation */\n createContext?: () => C;\n /** Step result cache - only steps with a `key` option are cached */\n cache?: StepCache;\n /** Pre-populate cache from saved state for workflow resume */\n resumeState?: ResumeState | (() => ResumeState | Promise<ResumeState>);\n catchUnexpected?: never; // prevent footgun: can't use without strict: true\n strict?: false; // default\n};\n\n/**\n * Strict workflow options\n * Returns E | U (closed error union)\n */\nexport type WorkflowOptionsStrict<E, U, C = void> = {\n strict: true; // discriminator\n catchUnexpected: (cause: unknown) => U;\n onError?: (error: E | U, stepName?: string) => void;\n /** Unified event stream for workflow and step lifecycle */\n onEvent?: (event: WorkflowEvent<E | U>, ctx: C) => void;\n /** Create per-run context for event correlation */\n createContext?: () => C;\n /** Step result cache - only steps with a `key` option are cached */\n cache?: StepCache;\n /** Pre-populate cache from saved state for workflow resume */\n resumeState?: ResumeState | (() => ResumeState | Promise<ResumeState>);\n};\n\n/**\n * Workflow return type (non-strict)\n * Supports both argument-less and argument-passing call patterns\n *\n * Note: Cause type is `unknown` because:\n * - step.try errors have thrown values as cause\n * - Uncaught exceptions produce unknown causes\n * - Different steps may have different cause types\n * The cause IS preserved at runtime; narrow based on error type if needed.\n */\nexport interface Workflow<E, Deps> {\n /**\n * Execute workflow without arguments (original API)\n */\n <T>(fn: (step: RunStep<E>, deps: Deps) => T | Promise<T>): AsyncResult<T, E | UnexpectedError, unknown>;\n\n /**\n * Execute workflow with typed arguments\n * @param args - Typed arguments passed to the callback (type inferred at call site)\n * @param fn - Callback receives (step, deps, args)\n */\n <T, Args>(\n args: Args,\n fn: (step: RunStep<E>, deps: Deps, args: Args) => T | Promise<T>\n ): AsyncResult<T, E | UnexpectedError, unknown>;\n}\n\n/**\n * Workflow return type (strict)\n * Supports both argument-less and argument-passing call patterns\n *\n * Note: Cause type is `unknown` because catchUnexpected receives thrown\n * values which have unknown type.\n */\nexport interface WorkflowStrict<E, U, Deps> {\n /**\n * Execute workflow without arguments (original API)\n */\n <T>(fn: (step: RunStep<E>, deps: Deps) => T | Promise<T>): AsyncResult<T, E | U, unknown>;\n\n /**\n * Execute workflow with typed arguments\n * @param args - Typed arguments passed to the callback (type inferred at call site)\n * @param fn - Callback receives (step, deps, args)\n */\n <T, Args>(\n args: Args,\n fn: (step: RunStep<E>, deps: Deps, args: Args) => T | Promise<T>\n ): AsyncResult<T, E | U, unknown>;\n}\n\n// =============================================================================\n// createWorkflow - Automatic Error Type Inference\n// =============================================================================\n\n/**\n * Create a typed workflow with automatic error inference.\n *\n * ## When to Use `createWorkflow`\n *\n * Use `createWorkflow` when you have:\n * - **Multiple dependent async operations** that need to run sequentially\n * - **Complex error handling** where you want type-safe error unions\n * - **Need for observability** via event streams (onEvent)\n * - **Step caching** requirements for expensive operations\n * - **Resume/replay** capabilities for long-running workflows\n * - **Human-in-the-loop** workflows requiring approvals\n *\n * ## Why Use `createWorkflow` Instead of `run()`\n *\n * 1. **Automatic Error Type Inference**: Errors are computed from your declared functions\n * - No manual error union management\n * - TypeScript ensures all possible errors are handled\n * - Refactoring is safer - adding/removing functions updates error types automatically\n *\n * 2. **Step Caching**: Expensive operations can be cached by key\n * - Prevents duplicate API calls\n * - Useful for idempotent operations\n * - Supports resume state for workflow replay\n *\n * 3. **Event Stream**: Built-in observability via `onEvent`\n * - Track workflow and step lifecycle\n * - Monitor performance (durationMs)\n * - Build dashboards and debugging tools\n *\n * 4. **Resume State**: Save and replay workflows\n * - Useful for long-running processes\n * - Supports human-in-the-loop workflows\n * - Enables workflow persistence across restarts\n *\n * ## How It Works\n *\n * 1. **Declare Dependencies**: Pass an object of Result-returning functions\n * 2. **Automatic Inference**: Error types are extracted from function return types\n * 3. **Execute Workflow**: Call the returned workflow function with your logic\n * 4. **Early Exit**: `step()` unwraps Results - on error, workflow exits immediately\n *\n * ## Error Type Inference\n *\n * The error union is automatically computed from all declared functions:\n * - Each function's error type is extracted\n * - Union of all errors is created\n * - `UnexpectedError` is added for uncaught exceptions (unless strict mode)\n *\n * ## Strict Mode\n *\n * Use `strict: true` with `catchUnexpected` for closed error unions:\n * - Removes `UnexpectedError` from the union\n * - All errors must be explicitly handled\n * - Useful for production code where you want exhaustive error handling\n *\n * @param deps - Object mapping names to Result-returning functions.\n * These functions must return `Result<T, E>` or `Promise<Result<T, E>>`.\n * The error types (`E`) from all functions are automatically combined into a union.\n * @param options - Optional configuration:\n * - `onEvent`: Callback for workflow/step lifecycle events\n * - `onError`: Callback for error logging/debugging\n * - `cache`: Step result cache (Map or custom StepCache implementation)\n * - `resumeState`: Pre-populated step results for workflow replay\n * - `createContext`: Factory for per-run context (passed to onEvent)\n * - `strict`: Enable strict mode (requires `catchUnexpected`)\n * - `catchUnexpected`: Map uncaught exceptions to typed errors (required in strict mode)\n *\n * @returns A workflow function that accepts your workflow logic and returns an AsyncResult.\n * The error type is automatically inferred from the `deps` parameter.\n *\n * @example\n * ```typescript\n * // Basic usage - automatic error inference\n * const fetchUser = async (id: string): AsyncResult<User, 'NOT_FOUND'> =>\n * id === '1' ? ok({ id, name: 'Alice' }) : err('NOT_FOUND');\n *\n * const fetchPosts = async (userId: string): AsyncResult<Post[], 'FETCH_ERROR'> =>\n * ok([{ id: 1, title: 'Hello' }]);\n *\n * const getPosts = createWorkflow({ fetchUser, fetchPosts });\n *\n * const result = await getPosts(async (step) => {\n * const user = await step(fetchUser('1'));\n * const posts = await step(fetchPosts(user.id));\n * return { user, posts };\n * });\n * // result.error: 'NOT_FOUND' | 'FETCH_ERROR' | UnexpectedError\n * ```\n *\n * @example\n * ```typescript\n * // With destructuring in callback (optional but convenient)\n * const result = await getPosts(async (step, { fetchUser, fetchPosts }) => {\n * const user = await step(fetchUser('1'));\n * const posts = await step(fetchPosts(user.id));\n * return { user, posts };\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Strict mode - closed error union (no UnexpectedError)\n * const getPosts = createWorkflow(\n * { fetchUser, fetchPosts },\n * {\n * strict: true,\n * catchUnexpected: () => 'UNEXPECTED' as const\n * }\n * );\n * // result.error: 'NOT_FOUND' | 'FETCH_ERROR' | 'UNEXPECTED' (exactly)\n * ```\n *\n * @example\n * ```typescript\n * // With step caching\n * const cache = new Map<string, Result<unknown, unknown>>();\n * const workflow = createWorkflow({ fetchUser }, { cache });\n *\n * const result = await workflow(async (step) => {\n * // First call executes fetchUser\n * const user1 = await step(() => fetchUser('1'), { key: 'user:1' });\n * // Second call with same key uses cache (fetchUser not called again)\n * const user2 = await step(() => fetchUser('1'), { key: 'user:1' });\n * return user1; // user1 === user2\n * });\n * ```\n *\n * @example\n * ```typescript\n * // With event stream for observability\n * const workflow = createWorkflow({ fetchUser }, {\n * onEvent: (event) => {\n * if (event.type === 'step_start') {\n * console.log(`Step ${event.name} started`);\n * }\n * if (event.type === 'step_success') {\n * console.log(`Step ${event.name} completed in ${event.durationMs}ms`);\n * }\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // With resume state for workflow replay\n * const savedState = { steps: new Map([['user:1', { result: ok({ id: '1', name: 'Alice' }) }]]) };\n * const workflow = createWorkflow({ fetchUser }, { resumeState: savedState });\n *\n * const result = await workflow(async (step) => {\n * // This step uses cached result from savedState (fetchUser not called)\n * const user = await step(() => fetchUser('1'), { key: 'user:1' });\n * return user;\n * });\n * ```\n *\n * @example\n * ```typescript\n * // With typed arguments (new API)\n * const workflow = createWorkflow({ fetchUser, fetchPosts });\n *\n * const result = await workflow(\n * { userId: '1' }, // Typed arguments\n * async (step, { fetchUser, fetchPosts }, { userId }) => {\n * const user = await step(fetchUser(userId));\n * const posts = await step(fetchPosts(user.id));\n * return { user, posts };\n * }\n * );\n * ```\n */\nexport function createWorkflow<\n const Deps extends Readonly<Record<string, AnyResultFn>>,\n C = void\n>(\n deps: Deps,\n options?: WorkflowOptions<ErrorsOfDeps<Deps>, C>\n): Workflow<ErrorsOfDeps<Deps>, Deps>;\n\nexport function createWorkflow<\n const Deps extends Readonly<Record<string, AnyResultFn>>,\n U,\n C = void\n>(\n deps: Deps,\n options: WorkflowOptionsStrict<ErrorsOfDeps<Deps>, U, C>\n): WorkflowStrict<ErrorsOfDeps<Deps>, U, Deps>;\n\n// Implementation\nexport function createWorkflow<\n const Deps extends Readonly<Record<string, AnyResultFn>>,\n U = never,\n C = void\n>(\n deps: Deps,\n options?: WorkflowOptions<ErrorsOfDeps<Deps>, C> | WorkflowOptionsStrict<ErrorsOfDeps<Deps>, U, C>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): any {\n type E = ErrorsOfDeps<Deps>;\n\n // Overloaded workflow executor function\n // Signature 1: No args (original API)\n function workflowExecutor<T>(\n fn: (step: RunStep<E>, deps: Deps) => T | Promise<T>\n ): Promise<Result<T, E | U | UnexpectedError, unknown>>;\n // Signature 2: With args (new API)\n function workflowExecutor<T, Args>(\n args: Args,\n fn: (step: RunStep<E>, deps: Deps, args: Args) => T | Promise<T>\n ): Promise<Result<T, E | U | UnexpectedError, unknown>>;\n // Implementation\n async function workflowExecutor<T, Args = undefined>(\n fnOrArgs: ((step: RunStep<E>, deps: Deps) => T | Promise<T>) | Args,\n maybeFn?: (step: RunStep<E>, deps: Deps, args: Args) => T | Promise<T>\n ): Promise<Result<T, E | U | UnexpectedError, unknown>> {\n // Detect calling pattern: if second arg is a function, first arg is args\n // This correctly handles functions as args (e.g., workflow(requestFactory, callback))\n const hasArgs = typeof maybeFn === \"function\";\n const args = hasArgs ? (fnOrArgs as Args) : undefined;\n const userFn = hasArgs\n ? maybeFn\n : (fnOrArgs as (step: RunStep<E>, deps: Deps) => T | Promise<T>);\n // Generate workflowId for this run\n const workflowId = crypto.randomUUID();\n\n // Create context for this run\n const context = options?.createContext?.() as C;\n\n // Helper to emit workflow events\n const emitEvent = (event: WorkflowEvent<E | U | UnexpectedError>) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (options as any)?.onEvent?.(event, context);\n };\n\n // Emit workflow_start\n const startTs = Date.now();\n const startTime = performance.now();\n emitEvent({\n type: \"workflow_start\",\n workflowId,\n ts: startTs,\n });\n\n // Get cache from options (or create one if resumeState is provided)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const resumeStateOption = (options as any)?.resumeState as\n | ResumeState\n | (() => ResumeState | Promise<ResumeState>)\n | undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let cache = (options as any)?.cache as StepCache | undefined;\n\n // If resumeState is provided but cache isn't, auto-create an in-memory cache\n if (resumeStateOption && !cache) {\n cache = new Map<string, Result<unknown, unknown, unknown>>();\n }\n\n // Pre-populate cache from resumeState\n if (resumeStateOption && cache) {\n const resumeState =\n typeof resumeStateOption === \"function\"\n ? await resumeStateOption()\n : resumeStateOption;\n\n for (const [key, entry] of resumeState.steps) {\n const { result, meta } = entry;\n if (result.ok) {\n cache.set(key, result);\n } else {\n // Encode error results with metadata for proper replay\n // Use provided meta if available, otherwise default to origin:\"result\"\n const effectiveMeta = meta ?? { origin: \"result\" as const, resultCause: result.cause };\n // Preserve original cause alongside metadata\n cache.set(key, encodeCachedError(result.error, effectiveMeta, result.cause));\n }\n }\n }\n\n // Helper to parse step options\n const parseStepOptions = (opts?: StepOptions | string): { name?: string; key?: string } => {\n if (typeof opts === \"string\") return { name: opts };\n return opts ?? {};\n };\n\n // Create a cached step wrapper\n const createCachedStep = (realStep: RunStep<E>): RunStep<E> => {\n if (!cache) {\n // No cache configured, just return real step\n return realStep;\n }\n\n // Wrap the main step function\n const cachedStepFn = async <StepT, StepE extends E, StepC = unknown>(\n operationOrResult:\n | (() => Result<StepT, StepE, StepC> | AsyncResult<StepT, StepE, StepC>)\n | Result<StepT, StepE, StepC>\n | AsyncResult<StepT, StepE, StepC>,\n stepOptions?: StepOptions | string\n ): Promise<StepT> => {\n const { name, key } = parseStepOptions(stepOptions);\n\n // Only use cache if key is provided\n if (key && cache.has(key)) {\n // Cache hit\n emitEvent({\n type: \"step_cache_hit\",\n workflowId,\n stepKey: key,\n name,\n ts: Date.now(),\n });\n\n const cached = cache.get(key)!;\n if (cached.ok) {\n return cached.value as StepT;\n }\n // Cached error - throw early exit with preserved metadata (origin + cause)\n // This bypasses realStep to avoid replaying step_start/step_error events\n const meta = decodeCachedMeta(cached.cause);\n throw createEarlyExit(cached.error as StepE, meta);\n }\n\n // Cache miss - emit event if key was provided\n if (key) {\n emitEvent({\n type: \"step_cache_miss\",\n workflowId,\n stepKey: key,\n name,\n ts: Date.now(),\n });\n }\n\n // Execute the real step - wrap in function form to satisfy overload\n const wrappedOp = typeof operationOrResult === \"function\"\n ? operationOrResult\n : () => operationOrResult;\n\n try {\n const value = await realStep(wrappedOp, stepOptions);\n // Cache successful result if key provided\n if (key) {\n cache.set(key, ok(value));\n }\n return value;\n } catch (thrown) {\n // Cache error results with full metadata if key provided and this is an early exit\n if (key && isEarlyExit(thrown)) {\n const exit = thrown as EarlyExit<StepE>;\n // Extract original cause from metadata for preservation\n const originalCause =\n exit.meta.origin === \"result\"\n ? exit.meta.resultCause\n : exit.meta.thrown;\n cache.set(key, encodeCachedError(exit.error, exit.meta, originalCause));\n }\n throw thrown;\n }\n };\n\n // Wrap step.try\n cachedStepFn.try = async <StepT, Err extends E>(\n operation: () => StepT | Promise<StepT>,\n opts:\n | { error: Err; name?: string; key?: string }\n | { onError: (cause: unknown) => Err; name?: string; key?: string }\n ): Promise<StepT> => {\n const { name, key } = opts;\n\n // Only use cache if key is provided\n if (key && cache.has(key)) {\n // Cache hit\n emitEvent({\n type: \"step_cache_hit\",\n workflowId,\n stepKey: key,\n name,\n ts: Date.now(),\n });\n\n const cached = cache.get(key)!;\n if (cached.ok) {\n return cached.value as StepT;\n }\n // Cached error - throw early exit with preserved metadata (origin + thrown)\n // This bypasses realStep.try to avoid replaying instrumentation\n const meta = decodeCachedMeta(cached.cause);\n throw createEarlyExit(cached.error as Err, meta);\n }\n\n // Cache miss - emit event if key was provided\n if (key) {\n emitEvent({\n type: \"step_cache_miss\",\n workflowId,\n stepKey: key,\n name,\n ts: Date.now(),\n });\n }\n\n // Execute the real step.try\n try {\n const value = await realStep.try(operation, opts);\n // Cache successful result if key provided\n if (key) {\n cache.set(key, ok(value));\n }\n return value;\n } catch (thrown) {\n // Cache error results with full metadata if key provided and this is an early exit\n if (key && isEarlyExit(thrown)) {\n const exit = thrown as EarlyExit<Err>;\n // Extract original cause from metadata for preservation\n const originalCause =\n exit.meta.origin === \"result\"\n ? exit.meta.resultCause\n : exit.meta.thrown;\n cache.set(key, encodeCachedError(exit.error, exit.meta, originalCause));\n }\n throw thrown;\n }\n };\n\n // Wrap step.fromResult - delegate to real step (caching handled by key in opts)\n cachedStepFn.fromResult = async <StepT, ResultE, Err extends E>(\n operation: () => Result<StepT, ResultE, unknown> | AsyncResult<StepT, ResultE, unknown>,\n opts:\n | { error: Err; name?: string; key?: string }\n | { onError: (resultError: ResultE) => Err; name?: string; key?: string }\n ): Promise<StepT> => {\n const { name, key } = opts;\n\n // Only use cache if key is provided\n if (key && cache.has(key)) {\n // Cache hit\n emitEvent({\n type: \"step_cache_hit\",\n workflowId,\n stepKey: key,\n name,\n ts: Date.now(),\n });\n\n const cached = cache.get(key)!;\n if (cached.ok) {\n return cached.value as StepT;\n }\n // Cached error - throw early exit with preserved metadata\n const meta = decodeCachedMeta(cached.cause);\n throw createEarlyExit(cached.error as Err, meta);\n }\n\n // Cache miss - emit event if key was provided\n if (key) {\n emitEvent({\n type: \"step_cache_miss\",\n workflowId,\n stepKey: key,\n name,\n ts: Date.now(),\n });\n }\n\n // Execute the real step.fromResult\n try {\n const value = await realStep.fromResult(operation, opts);\n // Cache successful result if key provided\n if (key) {\n cache.set(key, ok(value));\n }\n return value;\n } catch (thrown) {\n // Cache error results with full metadata if key provided and this is an early exit\n if (key && isEarlyExit(thrown)) {\n const exit = thrown as EarlyExit<Err>;\n const originalCause =\n exit.meta.origin === \"result\"\n ? exit.meta.resultCause\n : exit.meta.thrown;\n cache.set(key, encodeCachedError(exit.error, exit.meta, originalCause));\n }\n throw thrown;\n }\n };\n\n // Wrap step.parallel - delegate to real step (no caching for scope wrappers)\n cachedStepFn.parallel = realStep.parallel;\n\n // Wrap step.race - delegate to real step (no caching for scope wrappers)\n cachedStepFn.race = realStep.race;\n\n // Wrap step.allSettled - delegate to real step (no caching for scope wrappers)\n cachedStepFn.allSettled = realStep.allSettled;\n\n // Wrap step.retry - use cachedStepFn to ensure caching/resume works with keyed steps\n cachedStepFn.retry = <StepT, StepE extends E, StepC = unknown>(\n operation: () => Result<StepT, StepE, StepC> | AsyncResult<StepT, StepE, StepC>,\n options: RetryOptions & { name?: string; key?: string; timeout?: TimeoutOptions }\n ): Promise<StepT> => {\n // Delegate to cachedStepFn with retry options merged into StepOptions\n // This ensures the cache layer is consulted for keyed steps\n return cachedStepFn(operation, {\n name: options.name,\n key: options.key,\n retry: {\n attempts: options.attempts,\n backoff: options.backoff,\n initialDelay: options.initialDelay,\n maxDelay: options.maxDelay,\n jitter: options.jitter,\n retryOn: options.retryOn,\n onRetry: options.onRetry,\n },\n timeout: options.timeout,\n });\n };\n\n // Wrap step.withTimeout - use cachedStepFn to ensure caching/resume works with keyed steps\n cachedStepFn.withTimeout = <StepT, StepE extends E, StepC = unknown>(\n operation:\n | (() => Result<StepT, StepE, StepC> | AsyncResult<StepT, StepE, StepC>)\n | ((signal: AbortSignal) => Result<StepT, StepE, StepC> | AsyncResult<StepT, StepE, StepC>),\n options: TimeoutOptions & { name?: string; key?: string }\n ): Promise<StepT> => {\n // Delegate to cachedStepFn with timeout options\n // This ensures the cache layer is consulted for keyed steps\n return cachedStepFn(\n operation as () => Result<StepT, StepE, StepC> | AsyncResult<StepT, StepE, StepC>,\n {\n name: options.name,\n key: options.key,\n timeout: options,\n }\n );\n };\n\n return cachedStepFn as RunStep<E>;\n };\n\n // Wrap the user's callback to pass cached step, deps, and args (when present)\n const wrappedFn = hasArgs\n ? (step: RunStep<E>) => (userFn as (step: RunStep<E>, deps: Deps, args: Args) => T | Promise<T>)(createCachedStep(step), deps, args as Args)\n : (step: RunStep<E>) => (userFn as (step: RunStep<E>, deps: Deps) => T | Promise<T>)(createCachedStep(step), deps);\n\n let result: Result<T, E | U | UnexpectedError, unknown>;\n\n if (options?.strict === true) {\n // Strict mode - use run.strict for closed error union\n const strictOptions = options as WorkflowOptionsStrict<E, U, C>;\n result = await run.strict<T, E | U, C>(wrappedFn as (step: RunStep<E | U>) => Promise<T> | T, {\n onError: strictOptions.onError,\n onEvent: strictOptions.onEvent as ((event: WorkflowEvent<E | U | UnexpectedError>, ctx: C) => void) | undefined,\n catchUnexpected: strictOptions.catchUnexpected,\n workflowId,\n context,\n });\n } else {\n // Non-strict mode - use run with onError for typed errors + UnexpectedError\n const normalOptions = options as WorkflowOptions<E, C> | undefined;\n result = await run<T, E, C>(wrappedFn as (step: RunStep<E | UnexpectedError>) => Promise<T> | T, {\n onError: normalOptions?.onError ?? (() => {}),\n onEvent: normalOptions?.onEvent,\n workflowId,\n context,\n });\n }\n\n // Emit workflow_success or workflow_error\n const durationMs = performance.now() - startTime;\n if (result.ok) {\n emitEvent({\n type: \"workflow_success\",\n workflowId,\n ts: Date.now(),\n durationMs,\n });\n } else {\n emitEvent({\n type: \"workflow_error\",\n workflowId,\n ts: Date.now(),\n durationMs,\n error: result.error,\n });\n }\n\n return result;\n }\n\n return workflowExecutor;\n}\n\n// =============================================================================\n// Type Guard Helpers\n// =============================================================================\n\n/**\n * Type guard to check if an event is a step_complete event.\n * Use this to filter events for state persistence.\n *\n * @param event - The workflow event to check\n * @returns `true` if the event is a step_complete event, `false` otherwise\n *\n * @example\n * ```typescript\n * const savedSteps = new Map<string, Result<unknown, unknown>>();\n *\n * const workflow = createWorkflow({ fetchUser }, {\n * onEvent: (event) => {\n * if (isStepComplete(event)) {\n * savedSteps.set(event.stepKey, event.result);\n * }\n * }\n * });\n * ```\n */\nexport function isStepComplete(\n event: WorkflowEvent<unknown>\n): event is Extract<WorkflowEvent<unknown>, { type: \"step_complete\" }> {\n return event.type === \"step_complete\";\n}\n\n// =============================================================================\n// Human-in-the-Loop (HITL) Support\n// =============================================================================\n\n/**\n * Standard error type for steps awaiting human approval.\n * Use this as the error type for approval-gated steps.\n *\n * @example\n * const requireApproval = async (userId: string): AsyncResult<Approval, PendingApproval> => {\n * const status = await checkApprovalStatus(userId);\n * if (status === 'pending') {\n * return err({ type: 'PENDING_APPROVAL', stepKey: `approval:${userId}` });\n * }\n * return ok(status.approval);\n * };\n */\nexport type PendingApproval = {\n type: \"PENDING_APPROVAL\";\n /** Step key for correlation when resuming */\n stepKey: string;\n /** Optional reason for the pending state */\n reason?: string;\n /** Optional metadata for the approval request */\n metadata?: Record<string, unknown>;\n};\n\n/**\n * Error returned when approval is rejected.\n */\nexport type ApprovalRejected = {\n type: \"APPROVAL_REJECTED\";\n /** Step key for correlation */\n stepKey: string;\n /** Reason the approval was rejected */\n reason: string;\n};\n\n/**\n * Type guard to check if an error is a PendingApproval.\n *\n * @param error - The error to check\n * @returns `true` if the error is a PendingApproval, `false` otherwise\n *\n * @example\n * ```typescript\n * const result = await workflow(...);\n * if (!result.ok && isPendingApproval(result.error)) {\n * console.log(`Waiting for approval: ${result.error.stepKey}`);\n * }\n * ```\n */\nexport function isPendingApproval(error: unknown): error is PendingApproval {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as PendingApproval).type === \"PENDING_APPROVAL\"\n );\n}\n\n/**\n * Type guard to check if an error is an ApprovalRejected.\n *\n * @param error - The error to check\n * @returns `true` if the error is an ApprovalRejected, `false` otherwise\n */\nexport function isApprovalRejected(error: unknown): error is ApprovalRejected {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as ApprovalRejected).type === \"APPROVAL_REJECTED\"\n );\n}\n\n/**\n * Create a PendingApproval error result.\n * Convenience helper for approval-gated steps.\n *\n * @param stepKey - Stable key for this approval step (used for resume)\n * @param options - Optional reason and metadata for the pending approval\n * @returns A Result with a PendingApproval error\n *\n * @example\n * ```typescript\n * const requireApproval = async (userId: string) => {\n * const status = await db.getApproval(userId);\n * if (!status) return pendingApproval(`approval:${userId}`);\n * return ok(status);\n * };\n * ```\n */\nexport function pendingApproval(\n stepKey: string,\n options?: { reason?: string; metadata?: Record<string, unknown> }\n): Result<never, PendingApproval> {\n return err({\n type: \"PENDING_APPROVAL\",\n stepKey,\n reason: options?.reason,\n metadata: options?.metadata,\n });\n}\n\n/**\n * Options for creating an approval-gated step.\n */\nexport interface ApprovalStepOptions<T> {\n /** Stable key for this approval step (used for resume) */\n key: string;\n /** Function to check current approval status from external source */\n checkApproval: () => Promise<\n | { status: \"pending\" }\n | { status: \"approved\"; value: T }\n | { status: \"rejected\"; reason: string }\n >;\n /** Optional reason shown when pending */\n pendingReason?: string;\n /** Optional metadata for the approval request */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Create a Result-returning function that checks external approval status.\n *\n * ## When to Use\n *\n * Use `createApprovalStep` when you need:\n * - **Human-in-the-loop workflows**: Steps that require human approval\n * - **External approval systems**: Integrate with approval databases/APIs\n * - **Workflow pausing**: Workflows that pause and resume after approval\n * - **Approval tracking**: Track who approved what and when\n *\n * ## Why Use This Instead of Manual Approval Checks\n *\n * - **Standardized pattern**: Consistent approval step interface\n * - **Type-safe**: Returns typed `PendingApproval` or `ApprovalRejected` errors\n * - **Resume-friendly**: Works seamlessly with `injectApproval()` and resume state\n * - **Metadata support**: Can include approval reason and metadata\n *\n * ## How It Works\n *\n * 1. Create approval step with `checkApproval` function\n * 2. `checkApproval` returns one of:\n * - `{ status: 'pending' }` - Approval not yet granted (workflow pauses)\n * - `{ status: 'approved', value: T }` - Approval granted (workflow continues)\n * - `{ status: 'rejected', reason: string }` - Approval rejected (workflow fails)\n * 3. Use in workflow with `step()` - workflow pauses if pending\n * 4. When approval granted externally, use `injectApproval()` to resume\n *\n * ## Typical Approval Flow\n *\n * 1. Workflow executes → reaches approval step\n * 2. `checkApproval()` called → returns `{ status: 'pending' }`\n * 3. Workflow returns `PendingApproval` error\n * 4. Save workflow state → persist for later resume\n * 5. Show approval UI → user sees pending approval\n * 6. User grants/rejects → update approval system\n * 7. Inject approval → call `injectApproval()` with approved value\n * 8. Resume workflow → continue from approval step\n *\n * @param options - Configuration for the approval step:\n * - `key`: Stable key for this approval (must match step key in workflow)\n * - `checkApproval`: Async function that checks current approval status\n * - `pendingReason`: Optional reason shown when approval is pending\n * - `metadata`: Optional metadata attached to the approval request\n *\n * @returns A function that returns an AsyncResult checking approval status.\n * The function can be used directly with `step()` in workflows.\n *\n * @example\n * ```typescript\n * // Create approval step that checks database\n * const requireManagerApproval = createApprovalStep<{ approvedBy: string }>({\n * key: 'manager-approval',\n * checkApproval: async () => {\n * const approval = await db.getApproval('manager-approval');\n * if (!approval) {\n * return { status: 'pending' }; // Workflow pauses here\n * }\n * if (approval.rejected) {\n * return { status: 'rejected', reason: approval.reason };\n * }\n * return {\n * status: 'approved',\n * value: { approvedBy: approval.approvedBy }\n * };\n * },\n * pendingReason: 'Waiting for manager approval',\n * });\n *\n * // Use in workflow\n * const workflow = createWorkflow({ requireManagerApproval });\n * const result = await workflow(async (step) => {\n * const approval = await step(requireManagerApproval, { key: 'manager-approval' });\n * // If pending, workflow exits with PendingApproval error\n * // If approved, continues with approval value\n * return approval;\n * });\n *\n * // Handle pending state\n * if (!result.ok && isPendingApproval(result.error)) {\n * // Workflow paused - show approval UI\n * showApprovalUI(result.error.stepKey);\n * }\n * ```\n *\n * @example\n * ```typescript\n * // With approval injection for resume\n * const collector = createHITLCollector();\n * const workflow = createWorkflow({ requireApproval }, {\n * onEvent: collector.handleEvent,\n * });\n *\n * const result = await workflow(async (step) => {\n * const approval = await step(requireApproval, { key: 'approval:1' });\n * return approval;\n * });\n *\n * // When approval granted externally\n * if (collector.hasPendingApprovals()) {\n * const resumeState = collector.injectApproval('approval:1', {\n * approvedBy: 'admin@example.com'\n * });\n *\n * // Resume workflow\n * const workflow2 = createWorkflow({ requireApproval }, { resumeState });\n * const result2 = await workflow2(async (step) => {\n * const approval = await step(requireApproval, { key: 'approval:1' });\n * return approval; // Now succeeds with injected value\n * });\n * }\n * ```\n */\nexport function createApprovalStep<T>(\n options: ApprovalStepOptions<T>\n): () => AsyncResult<T, PendingApproval | ApprovalRejected> {\n return async (): AsyncResult<T, PendingApproval | ApprovalRejected> => {\n const result = await options.checkApproval();\n\n switch (result.status) {\n case \"pending\":\n return err({\n type: \"PENDING_APPROVAL\",\n stepKey: options.key,\n reason: options.pendingReason,\n metadata: options.metadata,\n });\n case \"rejected\":\n return err({\n type: \"APPROVAL_REJECTED\",\n stepKey: options.key,\n reason: result.reason,\n });\n case \"approved\":\n return ok(result.value);\n }\n };\n}\n\n// =============================================================================\n// Resume State Helpers for HITL\n// =============================================================================\n\n/**\n * Inject an approved value into resume state.\n * Use this when an external approval is granted and you want to resume the workflow.\n *\n * @param state - The resume state to update\n * @param options - Object with stepKey and the approved value\n * @returns A new ResumeState with the approval injected\n *\n * @example\n * ```typescript\n * // When approval is granted externally:\n * const updatedState = injectApproval(savedState, {\n * stepKey: 'deploy:prod',\n * value: { approvedBy: 'admin', approvedAt: Date.now() }\n * });\n *\n * // Resume workflow with the approval injected\n * const workflow = createWorkflow({ ... }, { resumeState: updatedState });\n * ```\n */\nexport function injectApproval<T>(\n state: ResumeState,\n options: { stepKey: string; value: T }\n): ResumeState {\n const newSteps = new Map(state.steps);\n newSteps.set(options.stepKey, {\n result: ok(options.value),\n });\n return { steps: newSteps };\n}\n\n/**\n * Remove a step from resume state (e.g., to force re-execution).\n *\n * @param state - The resume state to update\n * @param stepKey - The key of the step to remove\n * @returns A new ResumeState with the step removed\n *\n * @example\n * ```typescript\n * // Force a step to re-execute on resume\n * const updatedState = clearStep(savedState, 'approval:123');\n * ```\n */\nexport function clearStep(state: ResumeState, stepKey: string): ResumeState {\n const newSteps = new Map(state.steps);\n newSteps.delete(stepKey);\n return { steps: newSteps };\n}\n\n/**\n * Check if a step in resume state has a pending approval error.\n *\n * @param state - The resume state to check\n * @param stepKey - The key of the step to check\n * @returns `true` if the step has a pending approval, `false` otherwise\n *\n * @example\n * ```typescript\n * if (hasPendingApproval(savedState, 'deploy:prod')) {\n * // Show approval UI\n * }\n * ```\n */\nexport function hasPendingApproval(\n state: ResumeState,\n stepKey: string\n): boolean {\n const entry = state.steps.get(stepKey);\n if (!entry || entry.result.ok) return false;\n return isPendingApproval(entry.result.error);\n}\n\n/**\n * Get all pending approval step keys from resume state.\n *\n * @param state - The resume state to check\n * @returns Array of step keys that have pending approvals\n *\n * @example\n * ```typescript\n * const pendingKeys = getPendingApprovals(savedState);\n * // ['deploy:prod', 'deploy:staging']\n * ```\n */\nexport function getPendingApprovals(state: ResumeState): string[] {\n const pending: string[] = [];\n for (const [key, entry] of state.steps) {\n if (!entry.result.ok && isPendingApproval(entry.result.error)) {\n pending.push(key);\n }\n }\n return pending;\n}\n\n// =============================================================================\n// Enhanced Collector for HITL\n// =============================================================================\n\n/**\n * Extended step collector that tracks pending approvals.\n * Use this for HITL workflows that need to track approval state.\n *\n * @returns An object with methods to handle events, get state, and manage approvals\n *\n * @example\n * ```typescript\n * const collector = createHITLCollector();\n *\n * const workflow = createWorkflow({ fetchUser, requireApproval }, {\n * onEvent: collector.handleEvent,\n * });\n *\n * const result = await workflow(async (step) => {\n * const user = await step(() => fetchUser(\"1\"), { key: \"user:1\" });\n * const approval = await step(requireApproval, { key: \"approval:1\" });\n * return { user, approval };\n * });\n *\n * // Check for pending approvals\n * if (collector.hasPendingApprovals()) {\n * const pending = collector.getPendingApprovals();\n * // pending: [{ stepKey: 'approval:1', error: PendingApproval }]\n * await saveToDatabase(collector.getState());\n * }\n *\n * // Later, when approved:\n * const resumeState = collector.injectApproval('approval:1', { approvedBy: 'admin' });\n * ```\n */\nexport function createHITLCollector(): {\n /** Handle workflow events (pass to onEvent option) */\n handleEvent: (event: WorkflowEvent<unknown>) => void;\n /** Get collected resume state */\n getState: () => ResumeState;\n /** Clear all collected state */\n clear: () => void;\n /** Check if any steps have pending approvals */\n hasPendingApprovals: () => boolean;\n /** Get all pending approval entries with their errors */\n getPendingApprovals: () => Array<{ stepKey: string; error: PendingApproval }>;\n /** Inject an approval result, updating the collector's internal state. Returns a copy for use as resumeState. */\n injectApproval: <T>(stepKey: string, value: T) => ResumeState;\n} {\n const steps = new Map<string, ResumeStateEntry>();\n\n return {\n handleEvent: (event: WorkflowEvent<unknown>) => {\n if (isStepComplete(event)) {\n steps.set(event.stepKey, { result: event.result, meta: event.meta });\n }\n },\n getState: () => ({ steps: new Map(steps) }),\n clear: () => steps.clear(),\n hasPendingApprovals: () => {\n for (const entry of steps.values()) {\n if (!entry.result.ok && isPendingApproval(entry.result.error)) {\n return true;\n }\n }\n return false;\n },\n getPendingApprovals: () => {\n const pending: Array<{ stepKey: string; error: PendingApproval }> = [];\n for (const [key, entry] of steps) {\n if (!entry.result.ok && isPendingApproval(entry.result.error)) {\n pending.push({ stepKey: key, error: entry.result.error as PendingApproval });\n }\n }\n return pending;\n },\n injectApproval: <T>(stepKey: string, value: T): ResumeState => {\n // Mutate internal state so collector reflects the approval\n steps.set(stepKey, { result: ok(value) });\n // Return a copy for use as resumeState\n return { steps: new Map(steps) };\n },\n };\n}\n","/**\n * @jagreehal/workflow/conditional\n *\n * Conditional step execution helpers for workflows.\n * These helpers allow you to conditionally execute steps based on runtime conditions,\n * with proper event emission for skipped steps.\n */\n\nimport type { WorkflowEvent } from \"./core\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Options for conditional execution.\n */\nexport type ConditionalOptions = {\n /**\n * Human-readable name for the conditional step.\n * Used in step_skipped events for debugging and visualization.\n */\n name?: string;\n\n /**\n * Stable identity key for the conditional step.\n * Used in step_skipped events for tracking and visualization.\n */\n key?: string;\n\n /**\n * Optional reason explaining why the step was skipped.\n * Included in step_skipped events.\n */\n reason?: string;\n};\n\n/**\n * Context for conditional execution, used to emit events.\n */\nexport type ConditionalContext = {\n /**\n * The workflow ID for event emission.\n */\n workflowId: string;\n\n /**\n * Event emitter function.\n */\n onEvent?: (event: WorkflowEvent<unknown>) => void;\n};\n\n/**\n * Type for operations that can be either sync or async.\n */\ntype MaybeAsync<T> = T | Promise<T>;\n\n/**\n * Type for the operation function passed to conditional helpers.\n */\ntype Operation<T> = () => MaybeAsync<T>;\n\n// =============================================================================\n// Internal Helpers\n// =============================================================================\n\n/**\n * Generate a unique decision ID for tracking conditional decisions.\n * @internal\n */\nfunction generateDecisionId(): string {\n return `decision_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * Emit a step_skipped event.\n * @internal\n */\nfunction emitSkipped(\n ctx: ConditionalContext | undefined,\n options: ConditionalOptions | undefined,\n decisionId: string\n): void {\n if (!ctx?.onEvent) return;\n\n ctx.onEvent({\n type: \"step_skipped\",\n workflowId: ctx.workflowId,\n stepKey: options?.key,\n name: options?.name,\n reason: options?.reason,\n decisionId,\n ts: Date.now(),\n });\n}\n\n// =============================================================================\n// Conditional Helpers\n// =============================================================================\n\n/**\n * Run a step only if condition is true, return undefined if skipped.\n *\n * Use this when you want to conditionally execute a step and handle\n * the undefined case yourself. For a version with a default value,\n * use `whenOr`.\n *\n * @param condition - Boolean condition to evaluate\n * @param operation - Function that performs the step (only called if condition is true)\n * @param options - Optional configuration for the conditional step\n * @param ctx - Optional context for event emission\n * @returns The result of the operation if condition is true, undefined otherwise\n *\n * @example\n * ```typescript\n * const result = await workflow(async (step) => {\n * const user = await step(fetchUser(id));\n *\n * // Only runs if user is premium\n * const premium = await when(\n * user.isPremium,\n * () => step(() => fetchPremiumData(user.id), { name: 'premium-data' }),\n * { name: 'check-premium', reason: 'User is not premium' }\n * );\n *\n * return { user, premium };\n * });\n * ```\n */\nexport function when<T>(\n condition: boolean,\n operation: Operation<T>,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): Promise<T | undefined>;\n\n/**\n * Synchronous overload for when the operation returns a non-Promise value.\n */\nexport function when<T>(\n condition: boolean,\n operation: () => T,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): T | undefined | Promise<T | undefined>;\n\nexport function when<T>(\n condition: boolean,\n operation: Operation<T>,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): MaybeAsync<T | undefined> {\n if (condition) {\n return operation();\n }\n\n const decisionId = generateDecisionId();\n emitSkipped(ctx, options, decisionId);\n return undefined;\n}\n\n/**\n * Run a step only if condition is false, return undefined if skipped.\n *\n * Use this when you want to conditionally execute a step when a condition\n * is NOT met. For a version with a default value, use `unlessOr`.\n *\n * @param condition - Boolean condition to evaluate\n * @param operation - Function that performs the step (only called if condition is false)\n * @param options - Optional configuration for the conditional step\n * @param ctx - Optional context for event emission\n * @returns The result of the operation if condition is false, undefined otherwise\n *\n * @example\n * ```typescript\n * const result = await workflow(async (step) => {\n * const user = await step(fetchUser(id));\n *\n * // Only runs if user is NOT verified\n * const verification = await unless(\n * user.isVerified,\n * () => step(() => sendVerificationEmail(user.email), { name: 'send-verification' }),\n * { name: 'check-verification', reason: 'User is already verified' }\n * );\n *\n * return { user, verification };\n * });\n * ```\n */\nexport function unless<T>(\n condition: boolean,\n operation: Operation<T>,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): Promise<T | undefined>;\n\n/**\n * Synchronous overload for unless when the operation returns a non-Promise value.\n */\nexport function unless<T>(\n condition: boolean,\n operation: () => T,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): T | undefined | Promise<T | undefined>;\n\nexport function unless<T>(\n condition: boolean,\n operation: Operation<T>,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): MaybeAsync<T | undefined> {\n return when(!condition, operation, options, ctx);\n}\n\n/**\n * Run a step only if condition is true, return default value if skipped.\n *\n * Use this when you want to conditionally execute a step and provide\n * a fallback value when the condition is not met.\n *\n * @param condition - Boolean condition to evaluate\n * @param operation - Function that performs the step (only called if condition is true)\n * @param defaultValue - Value to return if condition is false\n * @param options - Optional configuration for the conditional step\n * @param ctx - Optional context for event emission\n * @returns The result of the operation if condition is true, defaultValue otherwise\n *\n * @example\n * ```typescript\n * const result = await workflow(async (step) => {\n * const user = await step(fetchUser(id));\n *\n * // Get premium limits or use default for non-premium users\n * const limits = await whenOr(\n * user.isPremium,\n * () => step(() => fetchPremiumLimits(user.id), { name: 'premium-limits' }),\n * { maxRequests: 100, maxStorage: 1000 }, // default for non-premium\n * { name: 'check-premium-limits', reason: 'Using default limits for non-premium user' }\n * );\n *\n * return { user, limits };\n * });\n * ```\n */\nexport function whenOr<T, D>(\n condition: boolean,\n operation: Operation<T>,\n defaultValue: D,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): Promise<T | D>;\n\n/**\n * Synchronous overload for whenOr when the operation returns a non-Promise value.\n */\nexport function whenOr<T, D>(\n condition: boolean,\n operation: () => T,\n defaultValue: D,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): T | D | Promise<T | D>;\n\nexport function whenOr<T, D>(\n condition: boolean,\n operation: Operation<T>,\n defaultValue: D,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): MaybeAsync<T | D> {\n if (condition) {\n return operation();\n }\n\n const decisionId = generateDecisionId();\n emitSkipped(ctx, options, decisionId);\n return defaultValue;\n}\n\n/**\n * Run a step only if condition is false, return default value if skipped.\n *\n * Use this when you want to conditionally execute a step when a condition\n * is NOT met, with a fallback value for when the condition is true.\n *\n * @param condition - Boolean condition to evaluate\n * @param operation - Function that performs the step (only called if condition is false)\n * @param defaultValue - Value to return if condition is true\n * @param options - Optional configuration for the conditional step\n * @param ctx - Optional context for event emission\n * @returns The result of the operation if condition is false, defaultValue otherwise\n *\n * @example\n * ```typescript\n * const result = await workflow(async (step) => {\n * const user = await step(fetchUser(id));\n *\n * // Generate new token if user is NOT authenticated, otherwise use existing\n * const token = await unlessOr(\n * user.isAuthenticated,\n * () => step(() => generateNewToken(user.id), { name: 'generate-token' }),\n * user.existingToken, // use existing token if authenticated\n * { name: 'check-auth-for-token', reason: 'Using existing token for authenticated user' }\n * );\n *\n * return { user, token };\n * });\n * ```\n */\nexport function unlessOr<T, D>(\n condition: boolean,\n operation: Operation<T>,\n defaultValue: D,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): Promise<T | D>;\n\n/**\n * Synchronous overload for unlessOr when the operation returns a non-Promise value.\n */\nexport function unlessOr<T, D>(\n condition: boolean,\n operation: () => T,\n defaultValue: D,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): T | D | Promise<T | D>;\n\nexport function unlessOr<T, D>(\n condition: boolean,\n operation: Operation<T>,\n defaultValue: D,\n options?: ConditionalOptions,\n ctx?: ConditionalContext\n): MaybeAsync<T | D> {\n return whenOr(!condition, operation, defaultValue, options, ctx);\n}\n\n// =============================================================================\n// Factory Functions for Workflow Integration\n// =============================================================================\n\n/**\n * Create a set of conditional helpers bound to a workflow context.\n *\n * Use this factory when you want to automatically emit step_skipped events\n * to the workflow's event stream without passing context manually.\n *\n * @param ctx - The workflow context containing workflowId and onEvent\n * @returns Object with bound when, unless, whenOr, and unlessOr functions\n *\n * @example\n * ```typescript\n * const result = await run(async (step) => {\n * const ctx = { workflowId, onEvent };\n * const { when, whenOr } = createConditionalHelpers(ctx);\n *\n * const user = await step(fetchUser(id));\n *\n * const premium = await when(\n * user.isPremium,\n * () => step(() => fetchPremiumData(user.id)),\n * { name: 'premium-data' }\n * );\n *\n * return { user, premium };\n * }, { onEvent, workflowId });\n * ```\n */\nexport function createConditionalHelpers(ctx: ConditionalContext) {\n return {\n /**\n * Run a step only if condition is true, return undefined if skipped.\n */\n when: <T>(\n condition: boolean,\n operation: Operation<T>,\n options?: ConditionalOptions\n ): MaybeAsync<T | undefined> => when(condition, operation, options, ctx),\n\n /**\n * Run a step only if condition is false, return undefined if skipped.\n */\n unless: <T>(\n condition: boolean,\n operation: Operation<T>,\n options?: ConditionalOptions\n ): MaybeAsync<T | undefined> => unless(condition, operation, options, ctx),\n\n /**\n * Run a step only if condition is true, return default value if skipped.\n */\n whenOr: <T, D>(\n condition: boolean,\n operation: Operation<T>,\n defaultValue: D,\n options?: ConditionalOptions\n ): MaybeAsync<T | D> => whenOr(condition, operation, defaultValue, options, ctx),\n\n /**\n * Run a step only if condition is false, return default value if skipped.\n */\n unlessOr: <T, D>(\n condition: boolean,\n operation: Operation<T>,\n defaultValue: D,\n options?: ConditionalOptions\n ): MaybeAsync<T | D> => unlessOr(condition, operation, defaultValue, options, ctx),\n };\n}\n","/**\n * Circuit Breaker for Steps\n *\n * Prevents cascading failures by tracking step failure rates and\n * short-circuiting calls when a threshold is exceeded.\n *\n * Uses the circuit breaker pattern with three states:\n * - CLOSED: Normal operation (steps executing)\n * - OPEN: Fast-fail mode (steps blocked)\n * - HALF_OPEN: Testing if service recovered\n *\n * @example\n * ```typescript\n * import { createCircuitBreaker } from '@jagreehal/workflow';\n *\n * const breaker = createCircuitBreaker({\n * failureThreshold: 5,\n * resetTimeout: 30000,\n * halfOpenMax: 3,\n * });\n *\n * const result = await workflow(async (step) => {\n * const data = await breaker.execute(\n * () => step(() => callExternalApi()),\n * { name: 'external-api' }\n * );\n * return data;\n * });\n * ```\n */\n\nimport { err, type Result, type AsyncResult } from \"./core\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Circuit breaker state.\n */\nexport type CircuitState = \"CLOSED\" | \"OPEN\" | \"HALF_OPEN\";\n\n/**\n * Configuration for circuit breaker behavior.\n */\nexport interface CircuitBreakerConfig {\n /**\n * Number of failures within the window before opening the circuit.\n * @default 5\n */\n failureThreshold: number;\n\n /**\n * Time in ms to wait before transitioning from OPEN to HALF_OPEN.\n * @default 30000 (30 seconds)\n */\n resetTimeout: number;\n\n /**\n * Time window in ms for counting failures.\n * Failures older than this are discarded.\n * @default 60000 (1 minute)\n */\n windowSize: number;\n\n /**\n * Maximum number of test requests allowed in HALF_OPEN state.\n * If all succeed, circuit closes. If any fail, circuit reopens.\n * @default 3\n */\n halfOpenMax: number;\n\n /**\n * Optional callback when circuit state changes.\n */\n onStateChange?: (from: CircuitState, to: CircuitState, name?: string) => void;\n}\n\n/**\n * Error thrown when the circuit is open and calls are blocked.\n */\nexport class CircuitOpenError extends Error {\n readonly type = \"CIRCUIT_OPEN\" as const;\n readonly circuitName: string;\n readonly state: CircuitState;\n readonly retryAfterMs: number;\n\n constructor(options: {\n circuitName: string;\n state: CircuitState;\n retryAfterMs: number;\n message?: string;\n }) {\n super(\n options.message ??\n `Circuit breaker \"${options.circuitName}\" is ${options.state}. ` +\n `Retry after ${Math.ceil(options.retryAfterMs / 1000)}s`\n );\n this.name = \"CircuitOpenError\";\n this.circuitName = options.circuitName;\n this.state = options.state;\n this.retryAfterMs = options.retryAfterMs;\n }\n}\n\n/**\n * Type guard for CircuitOpenError.\n */\nexport function isCircuitOpenError(error: unknown): error is CircuitOpenError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as CircuitOpenError).type === \"CIRCUIT_OPEN\"\n );\n}\n\n/**\n * Failure record for tracking failures within the window.\n */\ninterface FailureRecord {\n timestamp: number;\n error: unknown;\n}\n\n/**\n * Circuit breaker statistics.\n */\nexport interface CircuitBreakerStats {\n state: CircuitState;\n failureCount: number;\n successCount: number;\n lastFailureTime: number | null;\n lastSuccessTime: number | null;\n halfOpenSuccesses: number;\n}\n\n// =============================================================================\n// Default Configuration\n// =============================================================================\n\nconst DEFAULT_CONFIG: CircuitBreakerConfig = {\n failureThreshold: 5,\n resetTimeout: 30_000,\n windowSize: 60_000,\n halfOpenMax: 3,\n};\n\n// =============================================================================\n// Circuit Breaker Implementation\n// =============================================================================\n\n/**\n * Circuit breaker instance for protecting external calls.\n */\nexport interface CircuitBreaker {\n /**\n * Execute an operation with circuit breaker protection.\n * Throws CircuitOpenError if the circuit is open.\n *\n * @param operation - The operation to execute\n * @param options - Optional name for logging/metrics\n * @returns The operation result\n * @throws CircuitOpenError if circuit is open\n */\n execute<T>(\n operation: () => T | Promise<T>,\n options?: { name?: string }\n ): Promise<T>;\n\n /**\n * Execute a Result-returning operation with circuit breaker protection.\n * Returns a CircuitOpenError result instead of throwing.\n *\n * @param operation - The operation returning a Result\n * @param options - Optional name for logging/metrics\n * @returns Result with the value or CircuitOpenError\n */\n executeResult<T, E>(\n operation: () => Result<T, E> | AsyncResult<T, E>,\n options?: { name?: string }\n ): AsyncResult<T, E | CircuitOpenError>;\n\n /**\n * Get current circuit state.\n */\n getState(): CircuitState;\n\n /**\n * Get circuit breaker statistics.\n */\n getStats(): CircuitBreakerStats;\n\n /**\n * Manually reset the circuit breaker to CLOSED state.\n */\n reset(): void;\n\n /**\n * Manually open the circuit (for testing or manual intervention).\n */\n forceOpen(): void;\n\n /**\n * Record a manual success (useful for health checks).\n */\n recordSuccess(): void;\n\n /**\n * Record a manual failure (useful for health checks).\n */\n recordFailure(error?: unknown): void;\n}\n\n/**\n * Create a circuit breaker instance.\n *\n * @param name - Name for this circuit breaker (used in errors and logging)\n * @param config - Configuration options\n * @returns A CircuitBreaker instance\n *\n * @example\n * ```typescript\n * const apiBreaker = createCircuitBreaker('external-api', {\n * failureThreshold: 5,\n * resetTimeout: 30000,\n * });\n *\n * // In workflow\n * const data = await apiBreaker.execute(() =>\n * step(() => fetchFromApi(id))\n * );\n * ```\n */\nexport function createCircuitBreaker(\n name: string,\n config?: Partial<CircuitBreakerConfig>\n): CircuitBreaker {\n const effectiveConfig: CircuitBreakerConfig = {\n ...DEFAULT_CONFIG,\n ...config,\n };\n\n let state: CircuitState = \"CLOSED\";\n let failures: FailureRecord[] = [];\n let lastFailureTime: number | null = null;\n let lastSuccessTime: number | null = null;\n let successCount = 0;\n let halfOpenSuccesses = 0;\n\n /**\n * Clean up old failures outside the time window.\n */\n function cleanupFailures(): void {\n const now = Date.now();\n failures = failures.filter(\n (f) => now - f.timestamp < effectiveConfig.windowSize\n );\n }\n\n /**\n * Transition to a new state.\n */\n function transitionTo(newState: CircuitState): void {\n if (state !== newState) {\n const oldState = state;\n state = newState;\n if (newState === \"HALF_OPEN\") {\n halfOpenSuccesses = 0;\n }\n effectiveConfig.onStateChange?.(oldState, newState, name);\n }\n }\n\n /**\n * Check if we should transition from OPEN to HALF_OPEN.\n */\n function checkOpenToHalfOpen(): boolean {\n if (state !== \"OPEN\" || lastFailureTime === null) {\n return false;\n }\n const now = Date.now();\n if (now - lastFailureTime >= effectiveConfig.resetTimeout) {\n transitionTo(\"HALF_OPEN\");\n return true;\n }\n return false;\n }\n\n /**\n * Record a successful operation.\n */\n function handleSuccess(): void {\n lastSuccessTime = Date.now();\n successCount++;\n\n if (state === \"HALF_OPEN\") {\n halfOpenSuccesses++;\n if (halfOpenSuccesses >= effectiveConfig.halfOpenMax) {\n // All test requests succeeded, close the circuit\n transitionTo(\"CLOSED\");\n failures = [];\n }\n }\n }\n\n /**\n * Record a failed operation.\n */\n function handleFailure(error: unknown): void {\n const now = Date.now();\n lastFailureTime = now;\n\n // Clean up old failures first\n cleanupFailures();\n\n // Add new failure\n failures.push({ timestamp: now, error });\n\n if (state === \"HALF_OPEN\") {\n // Any failure in HALF_OPEN reopens the circuit\n transitionTo(\"OPEN\");\n } else if (state === \"CLOSED\") {\n // Check if we should open the circuit\n if (failures.length >= effectiveConfig.failureThreshold) {\n transitionTo(\"OPEN\");\n }\n }\n }\n\n /**\n * Check if the circuit allows execution.\n * Returns the remaining wait time if blocked, or 0 if allowed.\n */\n function canExecute(): number {\n if (state === \"CLOSED\") {\n return 0;\n }\n\n if (state === \"OPEN\") {\n // Check if we should transition to HALF_OPEN\n if (checkOpenToHalfOpen()) {\n return 0; // Now in HALF_OPEN, allow execution\n }\n // Still OPEN, calculate remaining wait time\n const now = Date.now();\n const elapsed = lastFailureTime ? now - lastFailureTime : 0;\n return Math.max(0, effectiveConfig.resetTimeout - elapsed);\n }\n\n // HALF_OPEN - allow limited test requests\n return 0;\n }\n\n return {\n async execute<T>(\n operation: () => T | Promise<T>,\n _options?: { name?: string }\n ): Promise<T> {\n const waitTime = canExecute();\n if (waitTime > 0) {\n throw new CircuitOpenError({\n circuitName: name,\n state,\n retryAfterMs: waitTime,\n });\n }\n\n try {\n const result = await operation();\n handleSuccess();\n return result;\n } catch (error) {\n handleFailure(error);\n throw error;\n }\n },\n\n async executeResult<T, E>(\n operation: () => Result<T, E> | AsyncResult<T, E>,\n _options?: { name?: string }\n ): AsyncResult<T, E | CircuitOpenError> {\n const waitTime = canExecute();\n if (waitTime > 0) {\n return err(\n new CircuitOpenError({\n circuitName: name,\n state,\n retryAfterMs: waitTime,\n })\n );\n }\n\n try {\n const result = await operation();\n if (result.ok) {\n handleSuccess();\n } else {\n handleFailure(result.error);\n }\n return result;\n } catch (error) {\n handleFailure(error);\n throw error;\n }\n },\n\n getState(): CircuitState {\n // Check for automatic transition before returning\n if (state === \"OPEN\") {\n checkOpenToHalfOpen();\n }\n return state;\n },\n\n getStats(): CircuitBreakerStats {\n cleanupFailures();\n return {\n state: this.getState(),\n failureCount: failures.length,\n successCount,\n lastFailureTime,\n lastSuccessTime,\n halfOpenSuccesses,\n };\n },\n\n reset(): void {\n transitionTo(\"CLOSED\");\n failures = [];\n halfOpenSuccesses = 0;\n },\n\n forceOpen(): void {\n lastFailureTime = Date.now();\n transitionTo(\"OPEN\");\n },\n\n recordSuccess(): void {\n handleSuccess();\n },\n\n recordFailure(error?: unknown): void {\n handleFailure(error ?? new Error(\"Manual failure\"));\n },\n };\n}\n\n// =============================================================================\n// Presets\n// =============================================================================\n\n/**\n * Preset configurations for common use cases.\n */\nexport const circuitBreakerPresets = {\n /**\n * Aggressive circuit breaker for critical paths.\n * Opens quickly (3 failures) and recovers slowly (60s).\n */\n critical: {\n failureThreshold: 3,\n resetTimeout: 60_000,\n windowSize: 30_000,\n halfOpenMax: 1,\n } satisfies Partial<CircuitBreakerConfig>,\n\n /**\n * Standard circuit breaker for typical API calls.\n * Balanced between stability and availability.\n */\n standard: {\n failureThreshold: 5,\n resetTimeout: 30_000,\n windowSize: 60_000,\n halfOpenMax: 3,\n } satisfies Partial<CircuitBreakerConfig>,\n\n /**\n * Lenient circuit breaker for non-critical operations.\n * Opens slowly (10 failures) and recovers quickly (15s).\n */\n lenient: {\n failureThreshold: 10,\n resetTimeout: 15_000,\n windowSize: 120_000,\n halfOpenMax: 5,\n } satisfies Partial<CircuitBreakerConfig>,\n} as const;\n","/**\n * Saga / Compensation Pattern\n *\n * Define compensating actions for steps that need rollback on downstream failures.\n * When a workflow fails after some steps have completed, compensations run in\n * reverse order automatically.\n *\n * @example\n * ```typescript\n * import { createSagaWorkflow, ok, err } from '@jagreehal/workflow';\n *\n * const checkout = createSagaWorkflow({ reserveInventory, chargeCard, sendEmail });\n *\n * const result = await checkout(async (saga) => {\n * const reservation = await saga.step(\n * () => reserveInventory(items),\n * { compensate: (res) => releaseInventory(res.reservationId) }\n * );\n *\n * const payment = await saga.step(\n * () => chargeCard(amount),\n * { compensate: (p) => refundPayment(p.txId) }\n * );\n *\n * await saga.step(() => sendEmail(userId)); // No compensation needed\n *\n * return { reservation, payment };\n * });\n * // On failure: compensations run in reverse order automatically\n * ```\n */\n\nimport {\n ok,\n err,\n type Result,\n type AsyncResult,\n type UnexpectedError,\n isEarlyExit,\n createEarlyExit,\n type EarlyExit,\n type WorkflowEvent,\n} from \"./core\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * A compensation action to run on rollback.\n */\nexport type CompensationAction<T> = (value: T) => void | Promise<void>;\n\n/**\n * Options for a saga step.\n */\nexport interface SagaStepOptions<T> {\n /**\n * Name for the step (used in logging and events).\n */\n name?: string;\n\n /**\n * Compensation action to run if a later step fails.\n * Receives the value returned by this step.\n */\n compensate?: CompensationAction<T>;\n}\n\n/**\n * A recorded compensation with its value.\n */\ninterface RecordedCompensation<T = unknown> {\n name?: string;\n value: T;\n compensate: CompensationAction<T>;\n}\n\n/**\n * Error returned when compensation actions fail.\n */\nexport interface SagaCompensationError {\n type: \"SAGA_COMPENSATION_ERROR\";\n /** The original error that triggered the saga rollback */\n originalError: unknown;\n /** Errors from failed compensation actions */\n compensationErrors: Array<{\n stepName?: string;\n error: unknown;\n }>;\n}\n\n/**\n * Type guard for SagaCompensationError.\n */\nexport function isSagaCompensationError(\n error: unknown\n): error is SagaCompensationError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as SagaCompensationError).type === \"SAGA_COMPENSATION_ERROR\"\n );\n}\n\n/**\n * Saga execution context provided to the workflow callback.\n */\nexport interface SagaContext<E = unknown> {\n /**\n * Execute a step with optional compensation.\n *\n * @param operation - The operation to execute (returns Result)\n * @param options - Step options including compensation action\n * @returns The unwrapped success value\n */\n step: <T, StepE extends E, StepC = unknown>(\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n options?: SagaStepOptions<T>\n ) => Promise<T>;\n\n /**\n * Execute a throwing operation with optional compensation.\n *\n * @param operation - The operation to execute (may throw)\n * @param options - Step options including error mapping and compensation\n * @returns The success value\n */\n tryStep: <T, Err extends E>(\n operation: () => T | Promise<T>,\n options: {\n error: Err;\n name?: string;\n compensate?: CompensationAction<T>;\n } | {\n onError: (cause: unknown) => Err;\n name?: string;\n compensate?: CompensationAction<T>;\n }\n ) => Promise<T>;\n\n /**\n * Get all recorded compensations (for debugging/testing).\n */\n getCompensations: () => Array<{ name?: string; hasValue: boolean }>;\n}\n\n/**\n * Saga event types for observability.\n */\nexport type SagaEvent =\n | { type: \"saga_start\"; sagaId: string; ts: number }\n | { type: \"saga_success\"; sagaId: string; ts: number; durationMs: number }\n | { type: \"saga_error\"; sagaId: string; ts: number; durationMs: number; error: unknown }\n | { type: \"saga_compensation_start\"; sagaId: string; ts: number; stepCount: number }\n | { type: \"saga_compensation_step\"; sagaId: string; stepName?: string; ts: number; success: boolean; error?: unknown }\n | { type: \"saga_compensation_end\"; sagaId: string; ts: number; durationMs: number; success: boolean; failedCount: number };\n\n/**\n * Options for createSagaWorkflow.\n */\nexport interface SagaWorkflowOptions<E> {\n /**\n * Called when errors occur.\n */\n onError?: (error: E | UnexpectedError | SagaCompensationError, stepName?: string) => void;\n\n /**\n * Event stream for saga lifecycle events.\n */\n onEvent?: (event: SagaEvent | WorkflowEvent<E | UnexpectedError>) => void;\n\n /**\n * Whether to throw if compensation actions fail.\n * If false, original error is returned with compensation errors attached.\n * @default false\n */\n throwOnCompensationFailure?: boolean;\n}\n\n/**\n * Result type for saga workflow.\n */\nexport type SagaResult<T, E> = Result<T, E | UnexpectedError | SagaCompensationError, unknown>;\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Helper type for Result-returning functions.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyResultFn = (...args: any[]) => Result<any, any, any> | Promise<Result<any, any, any>>;\n\n/**\n * Extract union of error types from a deps object.\n */\ntype ErrorsOfDeps<Deps extends Record<string, AnyResultFn>> = {\n [K in keyof Deps]: Deps[K] extends (...args: never[]) => infer R\n ? R extends Promise<infer PR>\n ? PR extends { ok: false; error: infer E }\n ? E\n : never\n : R extends { ok: false; error: infer E }\n ? E\n : never\n : never;\n}[keyof Deps];\n\n/**\n * Create a saga workflow with automatic compensation on failure.\n *\n * @param deps - Object mapping names to Result-returning functions\n * @param options - Saga workflow options\n * @returns A saga executor function\n *\n * @example\n * ```typescript\n * const saga = createSagaWorkflow({ reserveInventory, chargeCard });\n *\n * const result = await saga(async (ctx) => {\n * const reservation = await ctx.step(\n * () => reserveInventory(items),\n * { compensate: (res) => releaseInventory(res.id) }\n * );\n *\n * const payment = await ctx.step(\n * () => chargeCard(amount),\n * { compensate: (p) => refundPayment(p.txId) }\n * );\n *\n * return { reservation, payment };\n * });\n * ```\n */\nexport function createSagaWorkflow<\n const Deps extends Readonly<Record<string, AnyResultFn>>\n>(\n deps: Deps,\n options?: SagaWorkflowOptions<ErrorsOfDeps<Deps>>\n): <T>(\n fn: (saga: SagaContext<ErrorsOfDeps<Deps>>, deps: Deps) => Promise<T>\n) => Promise<SagaResult<T, ErrorsOfDeps<Deps>>> {\n type E = ErrorsOfDeps<Deps>;\n\n return async <T>(\n fn: (saga: SagaContext<E>, deps: Deps) => Promise<T>\n ): Promise<SagaResult<T, E | UnexpectedError | SagaCompensationError>> => {\n const sagaId = crypto.randomUUID();\n const startTime = performance.now();\n const compensations: RecordedCompensation[] = [];\n\n const emitEvent = (event: SagaEvent | WorkflowEvent<E | UnexpectedError>) => {\n options?.onEvent?.(event);\n };\n\n emitEvent({\n type: \"saga_start\",\n sagaId,\n ts: Date.now(),\n });\n\n /**\n * Run all compensations in reverse order.\n */\n async function runCompensations(\n _originalError: unknown\n ): Promise<Array<{ stepName?: string; error: unknown }>> {\n const errors: Array<{ stepName?: string; error: unknown }> = [];\n\n emitEvent({\n type: \"saga_compensation_start\",\n sagaId,\n ts: Date.now(),\n stepCount: compensations.length,\n });\n\n const compensationStartTime = performance.now();\n\n // Run compensations in reverse order\n for (let i = compensations.length - 1; i >= 0; i--) {\n const comp = compensations[i];\n try {\n await comp.compensate(comp.value);\n emitEvent({\n type: \"saga_compensation_step\",\n sagaId,\n stepName: comp.name,\n ts: Date.now(),\n success: true,\n });\n } catch (error) {\n errors.push({ stepName: comp.name, error });\n emitEvent({\n type: \"saga_compensation_step\",\n sagaId,\n stepName: comp.name,\n ts: Date.now(),\n success: false,\n error,\n });\n }\n }\n\n emitEvent({\n type: \"saga_compensation_end\",\n sagaId,\n ts: Date.now(),\n durationMs: performance.now() - compensationStartTime,\n success: errors.length === 0,\n failedCount: errors.length,\n });\n\n return errors;\n }\n\n // Create saga context\n const sagaContext: SagaContext<E> = {\n async step<T, StepE extends E, StepC = unknown>(\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n stepOptions?: SagaStepOptions<T>\n ): Promise<T> {\n const result = await operation();\n\n if (result.ok) {\n // Record compensation if provided\n if (stepOptions?.compensate) {\n compensations.push({\n name: stepOptions.name,\n value: result.value,\n compensate: stepOptions.compensate as CompensationAction<unknown>,\n });\n }\n return result.value;\n }\n\n // Step failed - throw early exit to trigger compensation\n throw createEarlyExit(result.error as unknown as E, {\n origin: \"result\",\n resultCause: result.cause,\n });\n },\n\n async tryStep<T, Err extends E>(\n operation: () => T | Promise<T>,\n opts: {\n error: Err;\n name?: string;\n compensate?: CompensationAction<T>;\n } | {\n onError: (cause: unknown) => Err;\n name?: string;\n compensate?: CompensationAction<T>;\n }\n ): Promise<T> {\n const mapToError = \"error\" in opts ? () => opts.error : opts.onError;\n\n try {\n const value = await operation();\n\n // Record compensation if provided\n if (opts.compensate) {\n compensations.push({\n name: opts.name,\n value,\n compensate: opts.compensate as CompensationAction<unknown>,\n });\n }\n\n return value;\n } catch (thrown) {\n const mapped = mapToError(thrown);\n throw createEarlyExit(mapped as unknown as E, {\n origin: \"throw\",\n thrown,\n });\n }\n },\n\n getCompensations() {\n return compensations.map((c) => ({\n name: c.name,\n hasValue: c.value !== undefined,\n }));\n },\n };\n\n try {\n const result = await fn(sagaContext, deps);\n\n const durationMs = performance.now() - startTime;\n emitEvent({\n type: \"saga_success\",\n sagaId,\n ts: Date.now(),\n durationMs,\n });\n\n return ok(result);\n } catch (thrown) {\n const durationMs = performance.now() - startTime;\n\n // Extract the actual error from early exit\n let originalError: unknown;\n if (isEarlyExit(thrown)) {\n originalError = (thrown as EarlyExit<E>).error;\n } else {\n originalError = thrown;\n }\n\n emitEvent({\n type: \"saga_error\",\n sagaId,\n ts: Date.now(),\n durationMs,\n error: originalError,\n });\n\n // Run compensations\n const compensationErrors = await runCompensations(originalError);\n\n // Handle compensation failures\n if (compensationErrors.length > 0) {\n const sagaError: SagaCompensationError = {\n type: \"SAGA_COMPENSATION_ERROR\",\n originalError,\n compensationErrors,\n };\n\n options?.onError?.(sagaError);\n\n if (options?.throwOnCompensationFailure) {\n throw sagaError;\n }\n\n return err(sagaError);\n }\n\n // Compensation succeeded - return original error\n options?.onError?.(originalError as E);\n\n // Wrap non-typed errors as UnexpectedError\n if (!isEarlyExit(thrown)) {\n return err({\n type: \"UNEXPECTED_ERROR\",\n cause: { type: \"UNCAUGHT_EXCEPTION\", thrown },\n } as UnexpectedError);\n }\n\n return err(originalError as E);\n }\n };\n}\n\n/**\n * Run a saga with explicit compensation registration.\n *\n * Lower-level API for when you don't want automatic error inference\n * from a deps object.\n *\n * @example\n * ```typescript\n * const result = await runSaga<CheckoutResult, CheckoutError>(async (saga) => {\n * const reservation = await saga.step(\n * () => reserveInventory(items),\n * { compensate: (res) => releaseInventory(res.id) }\n * );\n * return { reservation };\n * });\n * ```\n */\nexport async function runSaga<T, E>(\n fn: (saga: SagaContext<E>) => Promise<T>,\n options?: Omit<SagaWorkflowOptions<E>, \"onEvent\"> & {\n onEvent?: (event: SagaEvent) => void;\n }\n): Promise<SagaResult<T, E>> {\n const sagaId = crypto.randomUUID();\n const startTime = performance.now();\n const compensations: RecordedCompensation[] = [];\n\n const emitEvent = (event: SagaEvent) => {\n options?.onEvent?.(event);\n };\n\n emitEvent({\n type: \"saga_start\",\n sagaId,\n ts: Date.now(),\n });\n\n /**\n * Run all compensations in reverse order.\n */\n async function runCompensations(\n _originalError: unknown\n ): Promise<Array<{ stepName?: string; error: unknown }>> {\n const errors: Array<{ stepName?: string; error: unknown }> = [];\n\n emitEvent({\n type: \"saga_compensation_start\",\n sagaId,\n ts: Date.now(),\n stepCount: compensations.length,\n });\n\n const compensationStartTime = performance.now();\n\n // Run compensations in reverse order\n for (let i = compensations.length - 1; i >= 0; i--) {\n const comp = compensations[i];\n try {\n await comp.compensate(comp.value);\n emitEvent({\n type: \"saga_compensation_step\",\n sagaId,\n stepName: comp.name,\n ts: Date.now(),\n success: true,\n });\n } catch (error) {\n errors.push({ stepName: comp.name, error });\n emitEvent({\n type: \"saga_compensation_step\",\n sagaId,\n stepName: comp.name,\n ts: Date.now(),\n success: false,\n error,\n });\n }\n }\n\n emitEvent({\n type: \"saga_compensation_end\",\n sagaId,\n ts: Date.now(),\n durationMs: performance.now() - compensationStartTime,\n success: errors.length === 0,\n failedCount: errors.length,\n });\n\n return errors;\n }\n\n // Create saga context\n const sagaContext: SagaContext<E> = {\n async step<T, StepE extends E, StepC = unknown>(\n operation: () => Result<T, StepE, StepC> | AsyncResult<T, StepE, StepC>,\n stepOptions?: SagaStepOptions<T>\n ): Promise<T> {\n const result = await operation();\n\n if (result.ok) {\n if (stepOptions?.compensate) {\n compensations.push({\n name: stepOptions.name,\n value: result.value,\n compensate: stepOptions.compensate as CompensationAction<unknown>,\n });\n }\n return result.value;\n }\n\n throw createEarlyExit(result.error as unknown as E, {\n origin: \"result\",\n resultCause: result.cause,\n });\n },\n\n async tryStep<T, Err extends E>(\n operation: () => T | Promise<T>,\n opts: {\n error: Err;\n name?: string;\n compensate?: CompensationAction<T>;\n } | {\n onError: (cause: unknown) => Err;\n name?: string;\n compensate?: CompensationAction<T>;\n }\n ): Promise<T> {\n const mapToError = \"error\" in opts ? () => opts.error : opts.onError;\n\n try {\n const value = await operation();\n\n if (opts.compensate) {\n compensations.push({\n name: opts.name,\n value,\n compensate: opts.compensate as CompensationAction<unknown>,\n });\n }\n\n return value;\n } catch (thrown) {\n const mapped = mapToError(thrown);\n throw createEarlyExit(mapped as unknown as E, {\n origin: \"throw\",\n thrown,\n });\n }\n },\n\n getCompensations() {\n return compensations.map((c) => ({\n name: c.name,\n hasValue: c.value !== undefined,\n }));\n },\n };\n\n try {\n const result = await fn(sagaContext);\n\n const durationMs = performance.now() - startTime;\n emitEvent({\n type: \"saga_success\",\n sagaId,\n ts: Date.now(),\n durationMs,\n });\n\n return ok(result);\n } catch (thrown) {\n const durationMs = performance.now() - startTime;\n\n let originalError: unknown;\n if (isEarlyExit(thrown)) {\n originalError = (thrown as EarlyExit<E>).error;\n } else {\n originalError = thrown;\n }\n\n emitEvent({\n type: \"saga_error\",\n sagaId,\n ts: Date.now(),\n durationMs,\n error: originalError,\n });\n\n const compensationErrors = await runCompensations(originalError);\n\n if (compensationErrors.length > 0) {\n const sagaError: SagaCompensationError = {\n type: \"SAGA_COMPENSATION_ERROR\",\n originalError,\n compensationErrors,\n };\n\n options?.onError?.(sagaError);\n\n if (options?.throwOnCompensationFailure) {\n throw sagaError;\n }\n\n return err(sagaError);\n }\n\n options?.onError?.(originalError as E);\n\n if (!isEarlyExit(thrown)) {\n return err({\n type: \"UNEXPECTED_ERROR\",\n cause: { type: \"UNCAUGHT_EXCEPTION\", thrown },\n } as UnexpectedError);\n }\n\n return err(originalError as E);\n }\n}\n","/**\n * Rate Limiting / Concurrency Control\n *\n * Control throughput for steps that hit rate-limited APIs or shared resources.\n *\n * @example\n * ```typescript\n * import { createRateLimiter, createConcurrencyLimiter } from '@jagreehal/workflow';\n *\n * // Rate limiting (requests per second)\n * const rateLimiter = createRateLimiter({ maxPerSecond: 10 });\n *\n * // Concurrency limiting (max concurrent)\n * const concurrencyLimiter = createConcurrencyLimiter({ maxConcurrent: 5 });\n *\n * const result = await workflow(async (step) => {\n * // Wrap operations with rate limiting\n * const data = await rateLimiter.execute(() =>\n * step(() => callRateLimitedApi())\n * );\n *\n * // Wrap batch operations with concurrency control\n * const results = await concurrencyLimiter.executeAll(\n * ids.map(id => () => step(() => fetchItem(id)))\n * );\n *\n * return { data, results };\n * });\n * ```\n */\n\nimport { err, type Result, type AsyncResult } from \"./core\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Configuration for rate limiter.\n */\nexport interface RateLimiterConfig {\n /**\n * Maximum operations per second.\n */\n maxPerSecond: number;\n\n /**\n * Burst capacity - allows brief spikes above the rate.\n * @default maxPerSecond * 2\n */\n burstCapacity?: number;\n\n /**\n * Strategy when rate limit is exceeded.\n * - 'wait': Wait until a slot is available (default)\n * - 'reject': Reject immediately with error\n * @default 'wait'\n */\n strategy?: \"wait\" | \"reject\";\n}\n\n/**\n * Configuration for concurrency limiter.\n */\nexport interface ConcurrencyLimiterConfig {\n /**\n * Maximum concurrent operations.\n */\n maxConcurrent: number;\n\n /**\n * Strategy when limit is reached.\n * - 'queue': Queue and wait (default)\n * - 'reject': Reject immediately\n * @default 'queue'\n */\n strategy?: \"queue\" | \"reject\";\n\n /**\n * Maximum queue size (only for 'queue' strategy).\n * @default Infinity\n */\n maxQueueSize?: number;\n}\n\n/**\n * Error when rate/concurrency limit is exceeded and strategy is 'reject'.\n */\nexport interface RateLimitExceededError {\n type: \"RATE_LIMIT_EXCEEDED\";\n limiterName: string;\n retryAfterMs?: number;\n}\n\n/**\n * Error when concurrency limit queue is full.\n */\nexport interface QueueFullError {\n type: \"QUEUE_FULL\";\n limiterName: string;\n queueSize: number;\n maxQueueSize: number;\n}\n\n/**\n * Type guard for RateLimitExceededError.\n */\nexport function isRateLimitExceededError(\n error: unknown\n): error is RateLimitExceededError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as RateLimitExceededError).type === \"RATE_LIMIT_EXCEEDED\"\n );\n}\n\n/**\n * Type guard for QueueFullError.\n */\nexport function isQueueFullError(error: unknown): error is QueueFullError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as QueueFullError).type === \"QUEUE_FULL\"\n );\n}\n\n/**\n * Statistics for rate limiter.\n */\nexport interface RateLimiterStats {\n availableTokens: number;\n maxTokens: number;\n tokensPerSecond: number;\n waitingCount: number;\n}\n\n/**\n * Statistics for concurrency limiter.\n */\nexport interface ConcurrencyLimiterStats {\n activeCount: number;\n maxConcurrent: number;\n queueSize: number;\n maxQueueSize: number;\n}\n\n// =============================================================================\n// Rate Limiter (Token Bucket)\n// =============================================================================\n\n/**\n * Rate limiter interface.\n */\nexport interface RateLimiter {\n /**\n * Execute an operation with rate limiting.\n * @param operation - The operation to execute\n * @returns The operation result\n */\n execute<T>(operation: () => T | Promise<T>): Promise<T>;\n\n /**\n * Execute a Result-returning operation with rate limiting.\n */\n executeResult<T, E>(\n operation: () => Result<T, E> | AsyncResult<T, E>\n ): AsyncResult<T, E | RateLimitExceededError>;\n\n /**\n * Get current statistics.\n */\n getStats(): RateLimiterStats;\n\n /**\n * Reset the rate limiter.\n */\n reset(): void;\n}\n\n/**\n * Create a token bucket rate limiter.\n *\n * @param name - Name for the limiter (used in errors)\n * @param config - Rate limiter configuration\n * @returns A RateLimiter instance\n *\n * @example\n * ```typescript\n * const limiter = createRateLimiter('api-calls', {\n * maxPerSecond: 10,\n * burstCapacity: 20,\n * });\n *\n * // In workflow\n * const data = await limiter.execute(() =>\n * step(() => callApi())\n * );\n * ```\n */\nexport function createRateLimiter(\n name: string,\n config: RateLimiterConfig\n): RateLimiter {\n const { maxPerSecond, strategy = \"wait\" } = config;\n const maxTokens = config.burstCapacity ?? maxPerSecond * 2;\n\n let tokens = maxTokens;\n let lastRefill = Date.now();\n const refillRate = maxPerSecond / 1000; // tokens per ms\n\n // Queue for waiting requests\n const waitQueue: Array<() => void> = [];\n\n /**\n * Refill tokens based on elapsed time.\n */\n function refill(): void {\n const now = Date.now();\n const elapsed = now - lastRefill;\n const tokensToAdd = elapsed * refillRate;\n tokens = Math.min(maxTokens, tokens + tokensToAdd);\n lastRefill = now;\n }\n\n /**\n * Try to consume a token.\n * Returns remaining wait time if no tokens available.\n */\n function tryConsume(): number {\n refill();\n if (tokens >= 1) {\n tokens -= 1;\n return 0;\n }\n // Calculate wait time for next token\n const tokensNeeded = 1 - tokens;\n return Math.ceil(tokensNeeded / refillRate);\n }\n\n /**\n * Wait for a token to be available.\n */\n async function waitForToken(): Promise<void> {\n return new Promise((resolve) => {\n const check = () => {\n const waitTime = tryConsume();\n if (waitTime === 0) {\n resolve();\n } else {\n waitQueue.push(check);\n setTimeout(() => {\n const idx = waitQueue.indexOf(check);\n if (idx !== -1) {\n waitQueue.splice(idx, 1);\n check();\n }\n }, waitTime);\n }\n };\n check();\n });\n }\n\n return {\n async execute<T>(operation: () => T | Promise<T>): Promise<T> {\n const waitTime = tryConsume();\n\n if (waitTime > 0) {\n if (strategy === \"reject\") {\n throw {\n type: \"RATE_LIMIT_EXCEEDED\",\n limiterName: name,\n retryAfterMs: waitTime,\n } as RateLimitExceededError;\n }\n\n // Wait strategy\n await waitForToken();\n }\n\n return operation();\n },\n\n async executeResult<T, E>(\n operation: () => Result<T, E> | AsyncResult<T, E>\n ): AsyncResult<T, E | RateLimitExceededError> {\n const waitTime = tryConsume();\n\n if (waitTime > 0) {\n if (strategy === \"reject\") {\n return err({\n type: \"RATE_LIMIT_EXCEEDED\",\n limiterName: name,\n retryAfterMs: waitTime,\n });\n }\n\n // Wait strategy\n await waitForToken();\n }\n\n return operation();\n },\n\n getStats(): RateLimiterStats {\n refill();\n return {\n availableTokens: Math.floor(tokens),\n maxTokens,\n tokensPerSecond: maxPerSecond,\n waitingCount: waitQueue.length,\n };\n },\n\n reset(): void {\n tokens = maxTokens;\n lastRefill = Date.now();\n // Clear wait queue\n waitQueue.length = 0;\n },\n };\n}\n\n// =============================================================================\n// Concurrency Limiter\n// =============================================================================\n\n/**\n * Concurrency limiter interface.\n */\nexport interface ConcurrencyLimiter {\n /**\n * Execute an operation with concurrency limiting.\n * @param operation - The operation to execute\n * @returns The operation result\n */\n execute<T>(operation: () => T | Promise<T>): Promise<T>;\n\n /**\n * Execute multiple operations with concurrency control.\n * @param operations - Array of operation factories\n * @returns Array of results (in order)\n */\n executeAll<T>(operations: Array<() => T | Promise<T>>): Promise<T[]>;\n\n /**\n * Execute a Result-returning operation with concurrency limiting.\n */\n executeResult<T, E>(\n operation: () => Result<T, E> | AsyncResult<T, E>\n ): AsyncResult<T, E | QueueFullError>;\n\n /**\n * Get current statistics.\n */\n getStats(): ConcurrencyLimiterStats;\n\n /**\n * Reset the concurrency limiter.\n */\n reset(): void;\n}\n\n/**\n * Create a concurrency limiter.\n *\n * @param name - Name for the limiter (used in errors)\n * @param config - Concurrency limiter configuration\n * @returns A ConcurrencyLimiter instance\n *\n * @example\n * ```typescript\n * const limiter = createConcurrencyLimiter('db-pool', {\n * maxConcurrent: 10,\n * });\n *\n * // Execute with concurrency control\n * const results = await limiter.executeAll(\n * ids.map(id => () => fetchItem(id))\n * );\n * ```\n */\nexport function createConcurrencyLimiter(\n name: string,\n config: ConcurrencyLimiterConfig\n): ConcurrencyLimiter {\n const { maxConcurrent, strategy = \"queue\", maxQueueSize = Infinity } = config;\n\n let activeCount = 0;\n const queue: Array<{ resolve: () => void; reject: (e: unknown) => void }> = [];\n\n /**\n * Acquire a slot.\n */\n async function acquire(): Promise<void> {\n if (activeCount < maxConcurrent) {\n activeCount++;\n return;\n }\n\n if (strategy === \"reject\") {\n throw {\n type: \"QUEUE_FULL\",\n limiterName: name,\n queueSize: queue.length,\n maxQueueSize,\n } as QueueFullError;\n }\n\n // Queue strategy\n if (queue.length >= maxQueueSize) {\n throw {\n type: \"QUEUE_FULL\",\n limiterName: name,\n queueSize: queue.length,\n maxQueueSize,\n } as QueueFullError;\n }\n\n return new Promise<void>((resolve, reject) => {\n queue.push({ resolve, reject });\n });\n }\n\n /**\n * Release a slot.\n */\n function release(): void {\n activeCount--;\n if (queue.length > 0 && activeCount < maxConcurrent) {\n activeCount++;\n const next = queue.shift();\n next?.resolve();\n }\n }\n\n return {\n async execute<T>(operation: () => T | Promise<T>): Promise<T> {\n await acquire();\n try {\n return await operation();\n } finally {\n release();\n }\n },\n\n async executeAll<T>(operations: Array<() => T | Promise<T>>): Promise<T[]> {\n const results: T[] = new Array(operations.length);\n const executing: Promise<void>[] = [];\n\n for (let i = 0; i < operations.length; i++) {\n const index = i;\n const promise = this.execute(operations[index]).then((result) => {\n results[index] = result;\n });\n executing.push(promise);\n }\n\n await Promise.all(executing);\n return results;\n },\n\n async executeResult<T, E>(\n operation: () => Result<T, E> | AsyncResult<T, E>\n ): AsyncResult<T, E | QueueFullError> {\n try {\n await acquire();\n } catch (error) {\n if (isQueueFullError(error)) {\n return err(error);\n }\n throw error;\n }\n\n try {\n return await operation();\n } finally {\n release();\n }\n },\n\n getStats(): ConcurrencyLimiterStats {\n return {\n activeCount,\n maxConcurrent,\n queueSize: queue.length,\n maxQueueSize,\n };\n },\n\n reset(): void {\n activeCount = 0;\n // Reject all queued operations\n while (queue.length > 0) {\n const item = queue.shift();\n item?.reject(new Error(\"Limiter reset\"));\n }\n },\n };\n}\n\n// =============================================================================\n// Combined Limiter\n// =============================================================================\n\n/**\n * Configuration for combined rate + concurrency limiter.\n */\nexport interface CombinedLimiterConfig {\n /**\n * Rate limiting configuration.\n */\n rate?: RateLimiterConfig;\n\n /**\n * Concurrency limiting configuration.\n */\n concurrency?: ConcurrencyLimiterConfig;\n}\n\n/**\n * Create a combined rate + concurrency limiter.\n *\n * Operations are first rate-limited, then concurrency-limited.\n *\n * @param name - Name for the limiter\n * @param config - Combined limiter configuration\n * @returns An object with both limiters and a combined execute function\n *\n * @example\n * ```typescript\n * const limiter = createCombinedLimiter('api', {\n * rate: { maxPerSecond: 10 },\n * concurrency: { maxConcurrent: 5 },\n * });\n *\n * const result = await limiter.execute(() => callApi());\n * ```\n */\nexport function createCombinedLimiter(\n name: string,\n config: CombinedLimiterConfig\n): {\n rate?: RateLimiter;\n concurrency?: ConcurrencyLimiter;\n execute: <T>(operation: () => T | Promise<T>) => Promise<T>;\n} {\n const rate = config.rate ? createRateLimiter(`${name}-rate`, config.rate) : undefined;\n const concurrency = config.concurrency\n ? createConcurrencyLimiter(`${name}-concurrency`, config.concurrency)\n : undefined;\n\n return {\n rate,\n concurrency,\n\n async execute<T>(operation: () => T | Promise<T>): Promise<T> {\n // Apply rate limiting first\n let op = operation;\n if (rate) {\n const originalOp = op;\n op = () => rate.execute(originalOp);\n }\n\n // Then apply concurrency limiting\n if (concurrency) {\n return concurrency.execute(op);\n }\n\n return op();\n },\n };\n}\n\n// =============================================================================\n// Presets\n// =============================================================================\n\n/**\n * Preset configurations for common use cases.\n */\nexport const rateLimiterPresets = {\n /**\n * Typical API rate limit (10 req/s).\n */\n api: {\n maxPerSecond: 10,\n burstCapacity: 20,\n strategy: \"wait\",\n } satisfies RateLimiterConfig,\n\n /**\n * Database pool limit (concurrent connections).\n */\n database: {\n maxConcurrent: 10,\n strategy: \"queue\",\n maxQueueSize: 100,\n } satisfies ConcurrencyLimiterConfig,\n\n /**\n * Aggressive rate limit for external APIs (5 req/s).\n */\n external: {\n maxPerSecond: 5,\n burstCapacity: 10,\n strategy: \"wait\",\n } satisfies RateLimiterConfig,\n} as const;\n","/**\n * Workflow Versioning and Migration\n *\n * Handle schema changes when resuming workflows that were persisted\n * with older step shapes.\n *\n * @example\n * ```typescript\n * import { createVersionedWorkflow } from '@jagreehal/workflow';\n *\n * const workflow = createVersionedWorkflow(\n * { fetchUser, chargeCard },\n * {\n * version: 2,\n * migrations: {\n * 1: (state) => migrateV1ToV2(state),\n * },\n * resumeState: loadState(runId),\n * }\n * );\n * ```\n */\n\nimport type { ResumeState, ResumeStateEntry } from \"./workflow\";\nimport { ok, err, type Result } from \"./core\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Version number type.\n */\nexport type Version = number;\n\n/**\n * Migration function that transforms state from one version to the next.\n */\nexport type MigrationFn = (state: ResumeState) => ResumeState | Promise<ResumeState>;\n\n/**\n * Map of migrations keyed by the source version.\n * Migration at key N transforms state from version N to version N+1.\n */\nexport type Migrations = Record<Version, MigrationFn>;\n\n/**\n * Versioned state includes the version number.\n */\nexport interface VersionedState {\n version: Version;\n state: ResumeState;\n}\n\n/**\n * Configuration for versioned workflow.\n */\nexport interface VersionedWorkflowConfig {\n /**\n * Current workflow version.\n */\n version: Version;\n\n /**\n * Migrations for upgrading old states.\n * Key is the source version, value transforms to next version.\n */\n migrations?: Migrations;\n\n /**\n * Strict mode - fail if state version is higher than current.\n * @default true\n */\n strictVersioning?: boolean;\n}\n\n/**\n * Error when version migration fails.\n */\nexport interface MigrationError {\n type: \"MIGRATION_ERROR\";\n fromVersion: Version;\n toVersion: Version;\n cause: unknown;\n}\n\n/**\n * Error when state version is incompatible.\n */\nexport interface VersionIncompatibleError {\n type: \"VERSION_INCOMPATIBLE\";\n stateVersion: Version;\n currentVersion: Version;\n reason: string;\n}\n\n/**\n * Type guard for MigrationError.\n */\nexport function isMigrationError(error: unknown): error is MigrationError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as MigrationError).type === \"MIGRATION_ERROR\"\n );\n}\n\n/**\n * Type guard for VersionIncompatibleError.\n */\nexport function isVersionIncompatibleError(\n error: unknown\n): error is VersionIncompatibleError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as VersionIncompatibleError).type === \"VERSION_INCOMPATIBLE\"\n );\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Migrate state from one version to another.\n *\n * @param state - The versioned state to migrate\n * @param targetVersion - The target version\n * @param migrations - Migration functions\n * @returns The migrated state or an error\n *\n * @example\n * ```typescript\n * const migrated = await migrateState(\n * { version: 1, state: oldState },\n * 3,\n * {\n * 1: (s) => transformV1ToV2(s),\n * 2: (s) => transformV2ToV3(s),\n * }\n * );\n * ```\n */\nexport async function migrateState(\n state: VersionedState,\n targetVersion: Version,\n migrations: Migrations\n): Promise<Result<VersionedState, MigrationError | VersionIncompatibleError>> {\n let currentState = state.state;\n let currentVersion = state.version;\n\n // Check if state is from the future\n if (currentVersion > targetVersion) {\n return err({\n type: \"VERSION_INCOMPATIBLE\",\n stateVersion: currentVersion,\n currentVersion: targetVersion,\n reason: \"State version is higher than current workflow version. Cannot downgrade.\",\n });\n }\n\n // Already at target version\n if (currentVersion === targetVersion) {\n return ok({ version: currentVersion, state: currentState });\n }\n\n // Apply migrations sequentially\n while (currentVersion < targetVersion) {\n const migration = migrations[currentVersion];\n\n if (!migration) {\n return err({\n type: \"VERSION_INCOMPATIBLE\",\n stateVersion: state.version,\n currentVersion: targetVersion,\n reason: `No migration found for version ${currentVersion} to ${currentVersion + 1}`,\n });\n }\n\n try {\n currentState = await migration(currentState);\n currentVersion++;\n } catch (cause) {\n return err({\n type: \"MIGRATION_ERROR\",\n fromVersion: currentVersion,\n toVersion: currentVersion + 1,\n cause,\n });\n }\n }\n\n return ok({ version: currentVersion, state: currentState });\n}\n\n/**\n * Create a versioned resume state loader.\n *\n * This wraps a state loader to automatically apply migrations\n * when loading older state versions.\n *\n * @param config - Versioning configuration\n * @returns A function that loads and migrates state\n *\n * @example\n * ```typescript\n * const loadVersionedState = createVersionedStateLoader({\n * version: 3,\n * migrations: {\n * 1: migrateV1ToV2,\n * 2: migrateV2ToV3,\n * },\n * });\n *\n * // In workflow\n * const workflow = createWorkflow(deps, {\n * resumeState: () => loadVersionedState(savedState),\n * });\n * ```\n */\nexport function createVersionedStateLoader(\n config: VersionedWorkflowConfig\n): (\n versionedState: VersionedState | null | undefined\n) => Promise<Result<ResumeState | undefined, MigrationError | VersionIncompatibleError>> {\n const { version, migrations = {}, strictVersioning = true } = config;\n\n return async (\n versionedState: VersionedState | null | undefined\n ): Promise<Result<ResumeState | undefined, MigrationError | VersionIncompatibleError>> => {\n // No saved state\n if (!versionedState) {\n return ok(undefined);\n }\n\n // Check for future version\n if (strictVersioning && versionedState.version > version) {\n return err({\n type: \"VERSION_INCOMPATIBLE\",\n stateVersion: versionedState.version,\n currentVersion: version,\n reason: \"Saved state is from a newer workflow version\",\n });\n }\n\n // Same version - no migration needed\n if (versionedState.version === version) {\n return ok(versionedState.state);\n }\n\n // Apply migrations\n const result = await migrateState(versionedState, version, migrations);\n if (!result.ok) {\n return result;\n }\n\n return ok(result.value.state);\n };\n}\n\n/**\n * Create versioned state from current resume state.\n *\n * Use this when saving state to storage.\n *\n * @param state - The current resume state\n * @param version - The current workflow version\n * @returns A versioned state object\n *\n * @example\n * ```typescript\n * const collector = createStepCollector();\n * // ... run workflow ...\n *\n * const versionedState = createVersionedState(collector.getState(), 2);\n * await db.saveWorkflowState(workflowId, versionedState);\n * ```\n */\nexport function createVersionedState(\n state: ResumeState,\n version: Version\n): VersionedState {\n return { version, state };\n}\n\n/**\n * Parse versioned state from JSON.\n *\n * Handles the serialization/deserialization of ResumeState with Map.\n *\n * @param json - The JSON string or parsed object\n * @returns The versioned state or null if invalid\n *\n * @example\n * ```typescript\n * const json = await db.loadWorkflowState(workflowId);\n * const versionedState = parseVersionedState(json);\n * if (versionedState) {\n * const loader = createVersionedStateLoader(config);\n * const state = await loader(versionedState);\n * }\n * ```\n */\ninterface SerializedVersionedState {\n version: number;\n state: { steps: Array<[string, ResumeStateEntry]> };\n}\n\nexport function parseVersionedState(\n json: string | SerializedVersionedState | null | undefined\n): VersionedState | null {\n if (!json) return null;\n\n try {\n const parsed: unknown = typeof json === \"string\" ? JSON.parse(json) : json;\n\n // Type guard\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n !(\"version\" in parsed) ||\n typeof (parsed as SerializedVersionedState).version !== \"number\" ||\n !(\"state\" in parsed) ||\n !(parsed as SerializedVersionedState).state ||\n !Array.isArray((parsed as SerializedVersionedState).state.steps)\n ) {\n return null;\n }\n\n const typedParsed = parsed as SerializedVersionedState;\n\n // Convert steps array back to Map\n const steps = new Map<string, ResumeStateEntry>(typedParsed.state.steps);\n\n return {\n version: typedParsed.version,\n state: { steps },\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Serialize versioned state to JSON.\n *\n * Converts the Map to an array for JSON serialization.\n *\n * @param state - The versioned state\n * @returns JSON string\n *\n * @example\n * ```typescript\n * const json = stringifyVersionedState(versionedState);\n * await db.saveWorkflowState(workflowId, json);\n * ```\n */\nexport function stringifyVersionedState(state: VersionedState): string {\n return JSON.stringify({\n version: state.version,\n state: {\n steps: Array.from(state.state.steps.entries()),\n },\n });\n}\n\n// =============================================================================\n// Migration Helpers\n// =============================================================================\n\n/**\n * Create a migration that renames step keys.\n *\n * @param renames - Map of old key to new key\n * @returns A migration function\n *\n * @example\n * ```typescript\n * const migrations = {\n * 1: createKeyRenameMigration({\n * 'user:fetch': 'user:load',\n * 'order:create': 'order:submit',\n * }),\n * };\n * ```\n */\nexport function createKeyRenameMigration(\n renames: Record<string, string>\n): MigrationFn {\n return (state: ResumeState): ResumeState => {\n const newSteps = new Map<string, ResumeStateEntry>();\n\n for (const [key, entry] of state.steps) {\n const newKey = renames[key] ?? key;\n newSteps.set(newKey, entry);\n }\n\n return { steps: newSteps };\n };\n}\n\n/**\n * Create a migration that removes specific step keys.\n *\n * @param keysToRemove - Array of keys to remove\n * @returns A migration function\n *\n * @example\n * ```typescript\n * const migrations = {\n * 1: createKeyRemoveMigration(['deprecated:step', 'old:cache']),\n * };\n * ```\n */\nexport function createKeyRemoveMigration(keysToRemove: string[]): MigrationFn {\n const keysSet = new Set(keysToRemove);\n return (state: ResumeState): ResumeState => {\n const newSteps = new Map<string, ResumeStateEntry>();\n\n for (const [key, entry] of state.steps) {\n if (!keysSet.has(key)) {\n newSteps.set(key, entry);\n }\n }\n\n return { steps: newSteps };\n };\n}\n\n/**\n * Create a migration that transforms step values.\n *\n * @param transforms - Map of key to transform function\n * @returns A migration function\n *\n * @example\n * ```typescript\n * const migrations = {\n * 1: createValueTransformMigration({\n * 'user:fetch': (entry) => ({\n * ...entry,\n * result: entry.result.ok\n * ? ok({ ...entry.result.value, newField: 'default' })\n * : entry.result,\n * }),\n * }),\n * };\n * ```\n */\nexport function createValueTransformMigration(\n transforms: Record<string, (entry: ResumeStateEntry) => ResumeStateEntry>\n): MigrationFn {\n return (state: ResumeState): ResumeState => {\n const newSteps = new Map<string, ResumeStateEntry>();\n\n for (const [key, entry] of state.steps) {\n const transform = transforms[key];\n newSteps.set(key, transform ? transform(entry) : entry);\n }\n\n return { steps: newSteps };\n };\n}\n\n/**\n * Compose multiple migrations into a single migration.\n *\n * @param migrations - Array of migration functions\n * @returns A single migration function that applies all migrations in order\n *\n * @example\n * ```typescript\n * const migrations = {\n * 1: composeMigrations([\n * createKeyRenameMigration({ 'old': 'new' }),\n * createKeyRemoveMigration(['deprecated']),\n * ]),\n * };\n * ```\n */\nexport function composeMigrations(migrations: MigrationFn[]): MigrationFn {\n return async (state: ResumeState): Promise<ResumeState> => {\n let currentState = state;\n for (const migration of migrations) {\n currentState = await migration(currentState);\n }\n return currentState;\n };\n}\n","/**\n * Autotel Integration\n *\n * First-class OpenTelemetry spans and metrics from the workflow event stream\n * using the autotel library.\n *\n * @example\n * ```typescript\n * import { createAutotelAdapter } from '@jagreehal/workflow/autotel';\n * import { init } from 'autotel';\n *\n * // Initialize autotel\n * init({ service: 'checkout-api' });\n *\n * // Create adapter\n * const otel = createAutotelAdapter({ serviceName: 'checkout' });\n *\n * // Use with workflow\n * const workflow = createWorkflow(deps, { onEvent: otel.handleEvent });\n *\n * // Automatic spans: workflow > step > retry attempts\n * // Automatic metrics: step_duration, retry_count, error_rate\n * ```\n */\n\nimport type { WorkflowEvent } from \"./core\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Configuration for the autotel adapter.\n */\nexport interface AutotelAdapterConfig {\n /**\n * Service name for spans (used as prefix).\n */\n serviceName?: string;\n\n /**\n * Whether to create spans for each step.\n * @default true\n */\n createStepSpans?: boolean;\n\n /**\n * Whether to record metrics for steps.\n * @default true\n */\n recordMetrics?: boolean;\n\n /**\n * Custom attributes to add to all spans.\n */\n defaultAttributes?: Record<string, string | number | boolean>;\n\n /**\n * Whether to record retry events as span events.\n * @default true\n */\n recordRetryEvents?: boolean;\n\n /**\n * Whether to mark workflow errors as span errors.\n * @default true\n */\n markErrorsOnSpan?: boolean;\n}\n\n/**\n * Span tracking info.\n */\ninterface SpanInfo {\n workflowId: string;\n stepId?: string;\n stepKey?: string;\n name?: string;\n startTime: number;\n}\n\n/**\n * Active spans tracking.\n */\ninterface ActiveSpans {\n workflows: Map<string, SpanInfo>;\n steps: Map<string, SpanInfo>;\n}\n\n/**\n * Metrics collected by the adapter.\n */\nexport interface AutotelMetrics {\n stepDurations: Array<{ \n name: string; \n durationMs: number; \n success: boolean;\n attributes?: Record<string, string | number | boolean>;\n }>;\n retryCount: number;\n errorCount: number;\n cacheHits: number;\n cacheMisses: number;\n /** Default attributes applied to all metrics */\n defaultAttributes: Record<string, string | number | boolean>;\n}\n\n/**\n * Autotel adapter interface.\n */\nexport interface AutotelAdapter {\n /**\n * Handle workflow events (pass to onEvent option).\n */\n handleEvent: (event: WorkflowEvent<unknown>) => void;\n\n /**\n * Get current active spans count (for debugging).\n */\n getActiveSpansCount: () => { workflows: number; steps: number };\n\n /**\n * Get collected metrics.\n */\n getMetrics: () => AutotelMetrics;\n\n /**\n * Reset adapter state.\n */\n reset: () => void;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Create an autotel adapter for workflow observability.\n *\n * This adapter translates workflow events into metrics and tracking data.\n * When autotel is installed and initialized, it will also create OpenTelemetry spans.\n *\n * @param config - Adapter configuration\n * @returns An AutotelAdapter instance\n *\n * @example\n * ```typescript\n * import { createAutotelAdapter } from '@jagreehal/workflow/autotel';\n *\n * const otel = createAutotelAdapter({ serviceName: 'checkout' });\n *\n * const workflow = createWorkflow(deps, {\n * onEvent: otel.handleEvent,\n * });\n * ```\n */\nexport function createAutotelAdapter(\n config: AutotelAdapterConfig = {}\n): AutotelAdapter {\n const {\n serviceName = \"workflow\",\n createStepSpans = true,\n recordMetrics = true,\n defaultAttributes = {},\n recordRetryEvents = true,\n markErrorsOnSpan = true,\n } = config;\n\n // Track active spans\n const activeSpans: ActiveSpans = {\n workflows: new Map(),\n steps: new Map(),\n };\n\n // Metrics counters\n const metrics: AutotelMetrics = {\n stepDurations: [],\n retryCount: 0,\n errorCount: 0,\n cacheHits: 0,\n cacheMisses: 0,\n defaultAttributes,\n };\n\n /**\n * Get span name for a step.\n */\n function getSpanName(name?: string, stepKey?: string): string {\n if (name) return `${serviceName}.${name}`;\n if (stepKey) return `${serviceName}.step.${stepKey}`;\n return `${serviceName}.step`;\n }\n\n /**\n * Handle workflow event.\n */\n function handleEvent(event: WorkflowEvent<unknown>): void {\n switch (event.type) {\n case \"workflow_start\":\n activeSpans.workflows.set(event.workflowId, {\n workflowId: event.workflowId,\n startTime: event.ts,\n });\n break;\n\n case \"workflow_success\":\n case \"workflow_error\":\n {\n const span = activeSpans.workflows.get(event.workflowId);\n if (span) {\n activeSpans.workflows.delete(event.workflowId);\n }\n\n if (event.type === \"workflow_error\" && markErrorsOnSpan) {\n metrics.errorCount++;\n }\n }\n break;\n\n case \"step_start\":\n if (createStepSpans) {\n const stepId = event.stepId;\n activeSpans.steps.set(stepId, {\n workflowId: event.workflowId,\n stepId,\n stepKey: event.stepKey,\n name: event.name,\n startTime: event.ts,\n });\n }\n break;\n\n case \"step_success\":\n case \"step_error\":\n if (createStepSpans) {\n const stepId = event.stepId;\n const span = activeSpans.steps.get(stepId);\n if (span) {\n activeSpans.steps.delete(stepId);\n\n if (recordMetrics) {\n metrics.stepDurations.push({\n name: getSpanName(span.name, span.stepKey),\n durationMs: event.durationMs,\n success: event.type === \"step_success\",\n // Include default attributes with each step metric\n attributes: Object.keys(defaultAttributes).length > 0 \n ? { ...defaultAttributes } \n : undefined,\n });\n }\n\n if (event.type === \"step_error\" && markErrorsOnSpan) {\n metrics.errorCount++;\n }\n }\n }\n break;\n\n case \"step_retry\":\n if (recordRetryEvents) {\n metrics.retryCount++;\n }\n break;\n\n case \"step_cache_hit\":\n metrics.cacheHits++;\n break;\n\n case \"step_cache_miss\":\n metrics.cacheMisses++;\n break;\n\n default:\n // Other events (timeout, etc.) - can be extended\n break;\n }\n }\n\n return {\n handleEvent,\n\n getActiveSpansCount() {\n return {\n workflows: activeSpans.workflows.size,\n steps: activeSpans.steps.size,\n };\n },\n\n getMetrics() {\n return { ...metrics, stepDurations: [...metrics.stepDurations] };\n },\n\n reset() {\n activeSpans.workflows.clear();\n activeSpans.steps.clear();\n metrics.stepDurations.length = 0;\n metrics.retryCount = 0;\n metrics.errorCount = 0;\n metrics.cacheHits = 0;\n metrics.cacheMisses = 0;\n // Note: defaultAttributes are preserved across resets\n },\n };\n}\n\n/**\n * Create an event handler that integrates with autotel's trace() function.\n *\n * This is a higher-level integration that creates actual OpenTelemetry spans\n * for each workflow and step when autotel is initialized.\n *\n * @param options - Configuration options\n * @returns An event handler function\n *\n * @example\n * ```typescript\n * import { createAutotelEventHandler } from '@jagreehal/workflow/autotel';\n * import { init } from 'autotel';\n *\n * init({ service: 'checkout-api' });\n *\n * const handler = createAutotelEventHandler({ serviceName: 'checkout' });\n *\n * const workflow = createWorkflow(deps, {\n * onEvent: handler,\n * });\n * ```\n */\nexport function createAutotelEventHandler(options?: {\n serviceName?: string;\n includeStepDetails?: boolean;\n}): (event: WorkflowEvent<unknown>) => void {\n const { serviceName = \"workflow\", includeStepDetails = true } = options ?? {};\n\n // This is a simple pass-through handler\n // The actual autotel integration happens via the trace() wrapper\n return (event: WorkflowEvent<unknown>) => {\n // Log events for debugging when AUTOTEL_DEBUG is set\n if (process.env.AUTOTEL_DEBUG === \"true\") {\n const prefix = `[${serviceName}]`;\n switch (event.type) {\n case \"workflow_start\":\n console.log(`${prefix} Workflow started: ${event.workflowId}`);\n break;\n case \"workflow_success\":\n console.log(`${prefix} Workflow success: ${event.workflowId} (${event.durationMs}ms)`);\n break;\n case \"workflow_error\":\n console.log(`${prefix} Workflow error: ${event.workflowId}`, event.error);\n break;\n case \"step_start\":\n if (includeStepDetails) {\n console.log(`${prefix} Step started: ${event.name ?? event.stepKey ?? event.stepId}`);\n }\n break;\n case \"step_success\":\n if (includeStepDetails) {\n console.log(`${prefix} Step success: ${event.name ?? event.stepKey ?? event.stepId} (${event.durationMs}ms)`);\n }\n break;\n case \"step_error\":\n if (includeStepDetails) {\n console.log(`${prefix} Step error: ${event.name ?? event.stepKey ?? event.stepId}`, event.error);\n }\n break;\n case \"step_retry\":\n console.log(`${prefix} Step retry: ${event.name ?? event.stepKey ?? event.stepId} (attempt ${event.attempt}/${event.maxAttempts})`);\n break;\n }\n }\n };\n}\n\n/**\n * Wrapper type for autotel trace function.\n * Use this when you want to explicitly type the autotel integration.\n */\nexport type AutotelTraceFn = <T>(\n name: string,\n fn: (ctx: { setAttribute: (key: string, value: unknown) => void }) => T | Promise<T>\n) => T | Promise<T>;\n\n/**\n * Create a workflow wrapper that adds autotel tracing.\n *\n * This creates a higher-order function that wraps workflow execution\n * in an autotel trace span.\n *\n * @param traceFn - The autotel trace function\n * @param options - Wrapper options\n * @returns A workflow wrapper function\n *\n * @example\n * ```typescript\n * import { trace } from 'autotel';\n * import { withAutotelTracing } from '@jagreehal/workflow/autotel';\n *\n * const traced = withAutotelTracing(trace, { serviceName: 'checkout' });\n *\n * const result = await traced('process-order', async () => {\n * return workflow(async (step) => {\n * // ... workflow logic\n * });\n * });\n * ```\n */\nexport function withAutotelTracing(\n traceFn: AutotelTraceFn,\n options?: { serviceName?: string }\n): <T>(\n name: string,\n fn: () => T | Promise<T>,\n attributes?: Record<string, string | number | boolean>\n) => Promise<T> {\n const { serviceName = \"workflow\" } = options ?? {};\n\n return async <T>(\n name: string,\n fn: () => T | Promise<T>,\n attributes?: Record<string, string | number | boolean>\n ): Promise<T> => {\n const spanName = `${serviceName}.${name}`;\n return traceFn(spanName, async (ctx) => {\n if (attributes) {\n for (const [key, value] of Object.entries(attributes)) {\n ctx.setAttribute(key, value);\n }\n }\n return fn();\n }) as Promise<T>;\n };\n}\n","/**\n * @jagreehal/workflow/webhook\n *\n * Webhook and event trigger adapters for exposing workflows as HTTP endpoints.\n * Framework-agnostic handlers that work with Express, Hono, Fastify, etc.\n */\n\nimport { type Result, type AsyncResult, ok, err, isOk } from \"./core\";\nimport { type Workflow, type WorkflowStrict, type UnexpectedError } from \"./workflow\";\n\n// =============================================================================\n// Request/Response Types\n// =============================================================================\n\n/**\n * Generic HTTP request representation.\n * Abstracts away framework-specific request objects.\n */\nexport interface WebhookRequest<Body = unknown> {\n /** HTTP method (GET, POST, PUT, DELETE, etc.) */\n method: string;\n /** Request path (e.g., \"/api/checkout\") */\n path: string;\n /** Request headers */\n headers: Record<string, string | string[] | undefined>;\n /** Parsed request body (JSON) */\n body: Body;\n /** Query parameters */\n query: Record<string, string | string[] | undefined>;\n /** Path parameters (e.g., { id: \"123\" }) */\n params: Record<string, string>;\n /** Raw request object from framework (for advanced use cases) */\n raw?: unknown;\n}\n\n/**\n * Generic HTTP response representation.\n */\nexport interface WebhookResponse<T = unknown> {\n /** HTTP status code */\n status: number;\n /** Response headers */\n headers?: Record<string, string>;\n /** Response body (will be JSON serialized) */\n body: T;\n}\n\n/**\n * Error response body structure.\n */\nexport interface ErrorResponseBody {\n error: {\n type: string;\n message?: string;\n details?: unknown;\n };\n}\n\n// =============================================================================\n// Validation Types\n// =============================================================================\n\n/**\n * Input validation result.\n */\nexport type ValidationResult<T, E = string> = Result<T, E>;\n\n/**\n * Standard validation error type.\n */\nexport interface ValidationError {\n type: \"VALIDATION_ERROR\";\n message: string;\n field?: string;\n details?: unknown;\n}\n\n/**\n * Type guard for ValidationError.\n */\nexport function isValidationError(e: unknown): e is ValidationError {\n return (\n typeof e === \"object\" &&\n e !== null &&\n (e as ValidationError).type === \"VALIDATION_ERROR\"\n );\n}\n\n// =============================================================================\n// Handler Configuration\n// =============================================================================\n\n/**\n * Configuration for creating a webhook handler.\n *\n * @template TInput - The validated input type\n * @template TOutput - The workflow output type\n * @template TError - The workflow error type\n * @template TBody - The raw request body type\n */\nexport interface WebhookHandlerConfig<TInput, TOutput, TError, TBody = unknown> {\n /**\n * Validate and transform the incoming request.\n * Return ok(input) to proceed, or err(validationError) to reject.\n *\n * @param req - The incoming request\n * @returns Validated input or validation error\n */\n validateInput: (\n req: WebhookRequest<TBody>\n ) => ValidationResult<TInput, ValidationError> | Promise<ValidationResult<TInput, ValidationError>>;\n\n /**\n * Map workflow result to HTTP response.\n * Called for both success and error cases.\n *\n * @param result - The workflow result\n * @param req - The original request (for context)\n * @returns HTTP response\n */\n mapResult: (\n result: Result<TOutput, TError | UnexpectedError>,\n req: WebhookRequest<TBody>\n ) => WebhookResponse;\n\n /**\n * Optional: Map validation errors to HTTP response.\n * Defaults to 400 Bad Request with error details.\n *\n * @param error - The validation error\n * @param req - The original request\n * @returns HTTP response\n */\n mapValidationError?: (\n error: ValidationError,\n req: WebhookRequest<TBody>\n ) => WebhookResponse<ErrorResponseBody>;\n\n /**\n * Optional: Handle unexpected errors during request processing.\n * Defaults to 500 Internal Server Error.\n *\n * @param error - The unexpected error\n * @param req - The original request\n * @returns HTTP response\n */\n mapUnexpectedError?: (\n error: unknown,\n req: WebhookRequest<TBody>\n ) => WebhookResponse<ErrorResponseBody>;\n\n /**\n * Optional: Request middleware.\n * Transform or enrich the request before validation.\n *\n * @param req - The incoming request\n * @returns Transformed request\n */\n beforeValidation?: (\n req: WebhookRequest<TBody>\n ) => WebhookRequest<TBody> | Promise<WebhookRequest<TBody>>;\n\n /**\n * Optional: Response middleware.\n * Transform the response before sending.\n *\n * @param response - The response to send\n * @param req - The original request\n * @returns Transformed response\n */\n afterResponse?: (\n response: WebhookResponse,\n req: WebhookRequest<TBody>\n ) => WebhookResponse | Promise<WebhookResponse>;\n}\n\n/**\n * A webhook handler function that processes requests.\n */\nexport type WebhookHandler<TBody = unknown> = (\n req: WebhookRequest<TBody>\n) => Promise<WebhookResponse>;\n\n// =============================================================================\n// Default Mappers\n// =============================================================================\n\n/**\n * Default validation error mapper.\n * Returns 400 Bad Request with error details.\n */\nexport function defaultValidationErrorMapper(\n error: ValidationError\n): WebhookResponse<ErrorResponseBody> {\n return {\n status: 400,\n body: {\n error: {\n type: error.type,\n message: error.message,\n details: error.field ? { field: error.field } : error.details,\n },\n },\n };\n}\n\n/**\n * Default unexpected error mapper.\n * Returns 500 Internal Server Error.\n */\nexport function defaultUnexpectedErrorMapper(\n _error: unknown\n): WebhookResponse<ErrorResponseBody> {\n return {\n status: 500,\n body: {\n error: {\n type: \"INTERNAL_ERROR\",\n message: \"An unexpected error occurred\",\n },\n },\n };\n}\n\n// =============================================================================\n// Workflow Handler Factory\n// =============================================================================\n\n/**\n * Create a webhook handler for a workflow.\n *\n * This factory creates an HTTP handler function that:\n * 1. Validates the incoming request\n * 2. Executes the workflow with the validated input\n * 3. Maps the result to an HTTP response\n *\n * The handler is framework-agnostic and returns a standard response object.\n * Use framework adapters (createExpressHandler, createHonoHandler, etc.) to\n * integrate with specific frameworks.\n *\n * @template TInput - The validated input type passed to the workflow\n * @template TOutput - The workflow output type\n * @template TError - The workflow error type\n * @template TBody - The raw request body type\n * @template TDeps - The workflow dependencies type\n *\n * @param workflow - The workflow function to execute\n * @param workflowFn - The workflow body function (step, deps, input) => output\n * @param config - Handler configuration\n * @returns A webhook handler function\n *\n * @example\n * ```typescript\n * const checkoutWorkflow = createWorkflow({ chargeCard, sendEmail });\n *\n * const handler = createWebhookHandler(\n * checkoutWorkflow,\n * async (step, deps, input: CheckoutInput) => {\n * const charge = await step(() => deps.chargeCard(input.amount));\n * await step(() => deps.sendEmail(input.email, charge.receiptUrl));\n * return { chargeId: charge.id };\n * },\n * {\n * validateInput: (req) => {\n * const { amount, email } = req.body;\n * if (!amount || !email) {\n * return err({ type: 'VALIDATION_ERROR', message: 'Missing required fields' });\n * }\n * return ok({ amount, email });\n * },\n * mapResult: (result) => {\n * if (result.ok) {\n * return { status: 200, body: result.value };\n * }\n * if (result.error === 'CARD_DECLINED') {\n * return { status: 402, body: { error: { type: 'CARD_DECLINED' } } };\n * }\n * return { status: 500, body: { error: { type: 'UNKNOWN' } } };\n * },\n * }\n * );\n *\n * // Use with Express\n * app.post('/checkout', async (req, res) => {\n * const response = await handler(toWebhookRequest(req));\n * res.status(response.status).json(response.body);\n * });\n * ```\n */\nexport function createWebhookHandler<\n TInput,\n TOutput,\n TError,\n TBody = unknown,\n TDeps = unknown\n>(\n workflow: Workflow<TError, TDeps> | WorkflowStrict<TError, unknown, TDeps>,\n workflowFn: (\n step: Parameters<Parameters<Workflow<TError, TDeps>>[1]>[0],\n deps: TDeps,\n input: TInput\n ) => TOutput | Promise<TOutput>,\n config: WebhookHandlerConfig<TInput, TOutput, TError, TBody>\n): WebhookHandler<TBody> {\n const {\n validateInput,\n mapResult,\n mapValidationError = defaultValidationErrorMapper,\n mapUnexpectedError = defaultUnexpectedErrorMapper,\n beforeValidation,\n afterResponse,\n } = config;\n\n return async (req: WebhookRequest<TBody>): Promise<WebhookResponse> => {\n try {\n // Apply request middleware if provided\n const processedReq = beforeValidation ? await beforeValidation(req) : req;\n\n // Validate input\n const validationResult = await validateInput(processedReq);\n\n if (!isOk(validationResult)) {\n const response = mapValidationError(validationResult.error, processedReq);\n return afterResponse ? await afterResponse(response, processedReq) : response;\n }\n\n // Execute workflow with validated input\n const workflowResult = await (workflow as Workflow<TError, TDeps>)(\n validationResult.value,\n (step, deps) => workflowFn(step, deps, validationResult.value)\n );\n\n // Map result to response\n const response = mapResult(workflowResult, processedReq);\n\n // Apply response middleware if provided\n return afterResponse ? await afterResponse(response, processedReq) : response;\n } catch (error) {\n // Handle unexpected errors during request processing\n const response = mapUnexpectedError(error, req);\n return afterResponse ? await afterResponse(response, req) : response;\n }\n };\n}\n\n// =============================================================================\n// Simple Handler (No Workflow)\n// =============================================================================\n\n/**\n * Configuration for a simple webhook handler without workflow.\n */\nexport interface SimpleHandlerConfig<TInput, TOutput, TError, TBody = unknown> {\n validateInput: (\n req: WebhookRequest<TBody>\n ) => ValidationResult<TInput, ValidationError> | Promise<ValidationResult<TInput, ValidationError>>;\n\n handler: (input: TInput, req: WebhookRequest<TBody>) => AsyncResult<TOutput, TError>;\n\n mapResult: (\n result: Result<TOutput, TError>,\n req: WebhookRequest<TBody>\n ) => WebhookResponse;\n\n mapValidationError?: (\n error: ValidationError,\n req: WebhookRequest<TBody>\n ) => WebhookResponse<ErrorResponseBody>;\n\n mapUnexpectedError?: (\n error: unknown,\n req: WebhookRequest<TBody>\n ) => WebhookResponse<ErrorResponseBody>;\n}\n\n/**\n * Create a simple webhook handler without workflow orchestration.\n * Useful for simple endpoints that don't need step-based error handling.\n *\n * @example\n * ```typescript\n * const handler = createSimpleHandler({\n * validateInput: (req) => {\n * const { id } = req.params;\n * if (!id) return err({ type: 'VALIDATION_ERROR', message: 'Missing id' });\n * return ok({ id });\n * },\n * handler: async ({ id }) => {\n * const user = await db.findUser(id);\n * return user ? ok(user) : err('NOT_FOUND' as const);\n * },\n * mapResult: (result) => {\n * if (result.ok) return { status: 200, body: result.value };\n * return { status: 404, body: { error: { type: 'NOT_FOUND' } } };\n * },\n * });\n * ```\n */\nexport function createSimpleHandler<TInput, TOutput, TError, TBody = unknown>(\n config: SimpleHandlerConfig<TInput, TOutput, TError, TBody>\n): WebhookHandler<TBody> {\n const {\n validateInput,\n handler,\n mapResult,\n mapValidationError = defaultValidationErrorMapper,\n mapUnexpectedError = defaultUnexpectedErrorMapper,\n } = config;\n\n return async (req: WebhookRequest<TBody>): Promise<WebhookResponse> => {\n try {\n const validationResult = await validateInput(req);\n\n if (!isOk(validationResult)) {\n return mapValidationError(validationResult.error, req);\n }\n\n const result = await handler(validationResult.value, req);\n return mapResult(result, req);\n } catch (error) {\n return mapUnexpectedError(error, req);\n }\n };\n}\n\n// =============================================================================\n// Result Mappers (Helpers)\n// =============================================================================\n\n/**\n * Standard error mapping configuration.\n */\nexport interface ErrorMapping<TError> {\n /** The error value to match */\n error: TError;\n /** HTTP status code for this error */\n status: number;\n /** Optional custom message */\n message?: string;\n}\n\n/**\n * Create a result mapper from error mappings.\n * Provides a declarative way to map workflow errors to HTTP responses.\n *\n * @param mappings - Array of error mappings\n * @param defaultStatus - Default status for unmapped errors (default: 500)\n * @returns A mapResult function for use in handler config\n *\n * @example\n * ```typescript\n * const mapResult = createResultMapper<CheckoutOutput, CheckoutError>([\n * { error: 'NOT_FOUND', status: 404, message: 'Resource not found' },\n * { error: 'CARD_DECLINED', status: 402, message: 'Payment failed' },\n * { error: 'RATE_LIMITED', status: 429, message: 'Too many requests' },\n * ]);\n *\n * const handler = createWebhookHandler(workflow, workflowFn, {\n * validateInput,\n * mapResult,\n * });\n * ```\n */\nexport function createResultMapper<TOutput, TError>(\n mappings: ErrorMapping<TError>[],\n options: {\n defaultStatus?: number;\n successStatus?: number;\n } = {}\n): (result: Result<TOutput, TError | UnexpectedError>) => WebhookResponse {\n const { defaultStatus = 500, successStatus = 200 } = options;\n\n const errorMap = new Map<TError, ErrorMapping<TError>>();\n for (const mapping of mappings) {\n errorMap.set(mapping.error, mapping);\n }\n\n return (result: Result<TOutput, TError | UnexpectedError>): WebhookResponse => {\n if (result.ok) {\n return {\n status: successStatus,\n body: result.value,\n };\n }\n\n // Check if it's an UnexpectedError\n if (\n typeof result.error === \"object\" &&\n result.error !== null &&\n (result.error as { type?: string }).type === \"UNEXPECTED_ERROR\"\n ) {\n return {\n status: 500,\n body: {\n error: {\n type: \"INTERNAL_ERROR\",\n message: \"An unexpected error occurred\",\n },\n },\n };\n }\n\n // Check error mappings\n const mapping = errorMap.get(result.error as TError);\n if (mapping) {\n return {\n status: mapping.status,\n body: {\n error: {\n type: String(mapping.error),\n message: mapping.message,\n },\n },\n };\n }\n\n // Default error response\n return {\n status: defaultStatus,\n body: {\n error: {\n type: String(result.error),\n },\n },\n };\n };\n}\n\n// =============================================================================\n// Framework Adapters\n// =============================================================================\n\n/**\n * Express-style request object (minimal interface).\n */\nexport interface ExpressLikeRequest {\n method: string;\n path: string;\n headers: Record<string, string | string[] | undefined>;\n body: unknown;\n query: Record<string, string | string[] | undefined>;\n params: Record<string, string>;\n}\n\n/**\n * Express-style response object (minimal interface).\n */\nexport interface ExpressLikeResponse {\n status(code: number): ExpressLikeResponse;\n set(headers: Record<string, string>): ExpressLikeResponse;\n json(body: unknown): void;\n}\n\n/**\n * Convert an Express-like request to WebhookRequest.\n *\n * @param req - Express-like request object\n * @returns WebhookRequest\n *\n * @example\n * ```typescript\n * app.post('/checkout', async (req, res) => {\n * const webhookReq = toWebhookRequest(req);\n * const response = await handler(webhookReq);\n * res.status(response.status).json(response.body);\n * });\n * ```\n */\nexport function toWebhookRequest<TBody = unknown>(\n req: ExpressLikeRequest\n): WebhookRequest<TBody> {\n return {\n method: req.method,\n path: req.path,\n headers: req.headers,\n body: req.body as TBody,\n query: req.query,\n params: req.params,\n raw: req,\n };\n}\n\n/**\n * Send a WebhookResponse using an Express-like response object.\n *\n * @param res - Express-like response object\n * @param response - WebhookResponse to send\n *\n * @example\n * ```typescript\n * app.post('/checkout', async (req, res) => {\n * const response = await handler(toWebhookRequest(req));\n * sendWebhookResponse(res, response);\n * });\n * ```\n */\nexport function sendWebhookResponse(\n res: ExpressLikeResponse,\n response: WebhookResponse\n): void {\n if (response.headers) {\n res.set(response.headers);\n }\n res.status(response.status).json(response.body);\n}\n\n/**\n * Create an Express-compatible middleware from a webhook handler.\n *\n * @param handler - Webhook handler function\n * @returns Express middleware function\n *\n * @example\n * ```typescript\n * const handler = createWebhookHandler(workflow, workflowFn, config);\n * const middleware = createExpressHandler(handler);\n * app.post('/checkout', middleware);\n * ```\n */\nexport function createExpressHandler<TBody = unknown>(\n handler: WebhookHandler<TBody>\n): (req: ExpressLikeRequest, res: ExpressLikeResponse) => Promise<void> {\n return async (req: ExpressLikeRequest, res: ExpressLikeResponse) => {\n const webhookReq = toWebhookRequest<TBody>(req);\n const response = await handler(webhookReq);\n sendWebhookResponse(res, response);\n };\n}\n\n// =============================================================================\n// Validation Helpers\n// =============================================================================\n\n/**\n * Create a validation error.\n *\n * @param message - Error message\n * @param field - Optional field name\n * @param details - Optional additional details\n * @returns ValidationError\n */\nexport function validationError(\n message: string,\n field?: string,\n details?: unknown\n): ValidationError {\n return {\n type: \"VALIDATION_ERROR\",\n message,\n field,\n details,\n };\n}\n\n/**\n * Create a required field validator.\n *\n * @param fields - Field names to validate\n * @returns Validation function\n *\n * @example\n * ```typescript\n * const validateRequired = requireFields(['email', 'password']);\n *\n * const validateInput = (req) => {\n * const result = validateRequired(req.body);\n * if (!result.ok) return result;\n * return ok(req.body as LoginInput);\n * };\n * ```\n */\nexport function requireFields(\n fields: string[]\n): (body: Record<string, unknown>) => ValidationResult<void, ValidationError> {\n return (body: Record<string, unknown>) => {\n for (const field of fields) {\n if (body[field] === undefined || body[field] === null || body[field] === \"\") {\n return err(validationError(`Missing required field: ${field}`, field));\n }\n }\n return ok(undefined);\n };\n}\n\n/**\n * Compose multiple validators into a single validator.\n *\n * @param validators - Validators to compose\n * @returns Combined validator function\n *\n * @example\n * ```typescript\n * const validate = composeValidators(\n * requireFields(['email', 'password']),\n * validateEmailFormat,\n * validatePasswordStrength\n * );\n * ```\n */\nexport function composeValidators<T>(\n ...validators: Array<(input: T) => ValidationResult<void, ValidationError>>\n): (input: T) => ValidationResult<void, ValidationError> {\n return (input: T) => {\n for (const validator of validators) {\n const result = validator(input);\n if (!result.ok) return result;\n }\n return ok(undefined);\n };\n}\n\n// =============================================================================\n// Event Trigger Types (for message queues, etc.)\n// =============================================================================\n\n/**\n * Generic event message for queue-based triggers.\n */\nexport interface EventMessage<T = unknown> {\n /** Unique message ID */\n id: string;\n /** Event type/name */\n type: string;\n /** Event payload */\n payload: T;\n /** Event metadata */\n metadata?: {\n timestamp?: number;\n source?: string;\n correlationId?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Result of processing an event.\n */\nexport interface EventProcessingResult {\n /** Whether the event was processed successfully */\n success: boolean;\n /** Should the message be acknowledged (removed from queue)? */\n ack: boolean;\n /** Optional error details */\n error?: {\n type: string;\n message?: string;\n retryable?: boolean;\n };\n}\n\n/**\n * Configuration for event trigger handlers.\n */\nexport interface EventTriggerConfig<TPayload, TOutput, TError> {\n /** Validate the event payload */\n validatePayload: (\n event: EventMessage<TPayload>\n ) => ValidationResult<TPayload, ValidationError>;\n\n /** Map workflow result to processing result */\n mapResult: (\n result: Result<TOutput, TError | UnexpectedError>,\n event: EventMessage<TPayload>\n ) => EventProcessingResult;\n\n /** Optional: Determine if error is retryable */\n isRetryable?: (error: TError | UnexpectedError) => boolean;\n}\n\n/**\n * Event handler function type.\n */\nexport type EventHandler<TPayload = unknown> = (\n event: EventMessage<TPayload>\n) => Promise<EventProcessingResult>;\n\n/**\n * Create an event handler for queue-based triggers.\n *\n * @example\n * ```typescript\n * const handler = createEventHandler(\n * checkoutWorkflow,\n * async (step, deps, payload: CheckoutPayload) => {\n * const charge = await step(() => deps.chargeCard(payload.amount));\n * return { chargeId: charge.id };\n * },\n * {\n * validatePayload: (event) => {\n * if (!event.payload.amount) {\n * return err({ type: 'VALIDATION_ERROR', message: 'Missing amount' });\n * }\n * return ok(event.payload);\n * },\n * mapResult: (result) => ({\n * success: result.ok,\n * ack: result.ok || !isRetryableError(result.error),\n * error: result.ok ? undefined : { type: String(result.error) },\n * }),\n * }\n * );\n *\n * // Use with SQS, RabbitMQ, etc.\n * queue.consume(async (message) => {\n * const result = await handler(message);\n * if (result.ack) await message.ack();\n * else await message.nack();\n * });\n * ```\n */\nexport function createEventHandler<\n TPayload,\n TOutput,\n TError,\n TDeps = unknown\n>(\n workflow: Workflow<TError, TDeps> | WorkflowStrict<TError, unknown, TDeps>,\n workflowFn: (\n step: Parameters<Parameters<Workflow<TError, TDeps>>[1]>[0],\n deps: TDeps,\n payload: TPayload\n ) => TOutput | Promise<TOutput>,\n config: EventTriggerConfig<TPayload, TOutput, TError>\n): EventHandler<TPayload> {\n const { validatePayload, mapResult } = config;\n\n return async (event: EventMessage<TPayload>): Promise<EventProcessingResult> => {\n try {\n const validationResult = validatePayload(event);\n\n if (!isOk(validationResult)) {\n return {\n success: false,\n ack: true, // Don't retry validation errors\n error: {\n type: validationResult.error.type,\n message: validationResult.error.message,\n retryable: false,\n },\n };\n }\n\n const workflowResult = await (workflow as Workflow<TError, TDeps>)(\n validationResult.value,\n (step, deps) => workflowFn(step, deps, validationResult.value)\n );\n\n return mapResult(workflowResult, event);\n } catch (error) {\n return {\n success: false,\n ack: false, // Retry unexpected errors\n error: {\n type: \"UNEXPECTED_ERROR\",\n message: error instanceof Error ? error.message : String(error),\n retryable: true,\n },\n };\n }\n };\n}\n","/**\n * @jagreehal/workflow/policies\n *\n * Policy-Driven Step Middleware - Reusable bundles of StepOptions\n * that can be composed and applied per-workflow or per-step.\n */\n\nimport type { StepOptions, RetryOptions, TimeoutOptions } from \"./core\";\n\n// =============================================================================\n// Policy Types\n// =============================================================================\n\n/**\n * A policy is a partial StepOptions that can be merged with other policies.\n */\nexport type Policy = Partial<StepOptions>;\n\n/**\n * A policy factory that creates policies based on context.\n */\nexport type PolicyFactory<T = void> = T extends void\n ? () => Policy\n : (context: T) => Policy;\n\n/**\n * Named policy with metadata.\n */\nexport interface NamedPolicy {\n name: string;\n policy: Policy;\n description?: string;\n}\n\n// =============================================================================\n// Policy Composition\n// =============================================================================\n\n/**\n * Merge multiple policies into a single StepOptions object.\n * Later policies override earlier ones for conflicting properties.\n * Retry and timeout options are deep-merged.\n *\n * @param policies - Policies to merge (in order of precedence)\n * @returns Merged StepOptions\n *\n * @example\n * ```typescript\n * const merged = mergePolicies(\n * timeoutPolicies.api, // timeout: 5000ms\n * retryPolicies.transient, // retry: 3 attempts\n * { name: 'fetch-user' } // name override\n * );\n * ```\n */\nexport function mergePolicies(...policies: Policy[]): StepOptions {\n const result: StepOptions = {};\n\n for (const policy of policies) {\n if (policy.name !== undefined) result.name = policy.name;\n if (policy.key !== undefined) result.key = policy.key;\n\n // Deep merge retry options\n if (policy.retry !== undefined) {\n result.retry = result.retry\n ? { ...result.retry, ...policy.retry }\n : { ...policy.retry };\n }\n\n // Deep merge timeout options\n if (policy.timeout !== undefined) {\n result.timeout = result.timeout\n ? { ...result.timeout, ...policy.timeout }\n : { ...policy.timeout };\n }\n }\n\n return result;\n}\n\n/**\n * Create a policy applier that merges base policies with step-specific options.\n *\n * @param basePolicies - Base policies to apply to all steps\n * @returns A function that applies policies to step options\n *\n * @example\n * ```typescript\n * const applyPolicy = createPolicyApplier(\n * timeoutPolicies.api,\n * retryPolicies.transient\n * );\n *\n * // In workflow\n * const user = await step(\n * () => fetchUser(id),\n * applyPolicy({ name: 'fetch-user', key: 'user:' + id })\n * );\n * ```\n */\nexport function createPolicyApplier(\n ...basePolicies: Policy[]\n): (stepOptions?: StepOptions | string) => StepOptions {\n const basePolicy = mergePolicies(...basePolicies);\n\n return (stepOptions?: StepOptions | string): StepOptions => {\n const opts = typeof stepOptions === \"string\" ? { name: stepOptions } : (stepOptions ?? {});\n return mergePolicies(basePolicy, opts);\n };\n}\n\n/**\n * Create a named policy bundle for reuse across workflows.\n *\n * @param name - Policy bundle name\n * @param policies - Policies to include in the bundle\n * @returns Named policy object\n */\nexport function createPolicyBundle(\n name: string,\n ...policies: Policy[]\n): NamedPolicy {\n return {\n name,\n policy: mergePolicies(...policies),\n };\n}\n\n// =============================================================================\n// Retry Policies\n// =============================================================================\n\n/**\n * Create a retry policy with the given options.\n */\nexport function retryPolicy(options: RetryOptions): Policy {\n return { retry: options };\n}\n\n/**\n * Pre-built retry policies for common scenarios.\n */\nexport const retryPolicies = {\n /**\n * No retry - fail immediately on error.\n */\n none: retryPolicy({ attempts: 1 }),\n\n /**\n * Quick retry for transient errors (3 attempts, fast backoff).\n */\n transient: retryPolicy({\n attempts: 3,\n backoff: \"exponential\",\n initialDelay: 100,\n maxDelay: 1000,\n jitter: true,\n }),\n\n /**\n * Standard retry for API calls (3 attempts, moderate backoff).\n */\n standard: retryPolicy({\n attempts: 3,\n backoff: \"exponential\",\n initialDelay: 200,\n maxDelay: 5000,\n jitter: true,\n }),\n\n /**\n * Aggressive retry for critical operations (5 attempts, longer backoff).\n */\n aggressive: retryPolicy({\n attempts: 5,\n backoff: \"exponential\",\n initialDelay: 500,\n maxDelay: 30000,\n jitter: true,\n }),\n\n /**\n * Fixed interval retry (useful for polling).\n */\n fixed: (attempts: number, delayMs: number): Policy =>\n retryPolicy({\n attempts,\n backoff: \"fixed\",\n initialDelay: delayMs,\n jitter: false,\n }),\n\n /**\n * Linear backoff retry.\n */\n linear: (attempts: number, initialDelay: number): Policy =>\n retryPolicy({\n attempts,\n backoff: \"linear\",\n initialDelay,\n jitter: true,\n }),\n\n /**\n * Custom retry policy builder.\n */\n custom: (options: Partial<RetryOptions> & { attempts: number }): Policy =>\n retryPolicy({\n backoff: \"exponential\",\n initialDelay: 100,\n maxDelay: 30000,\n jitter: true,\n ...options,\n }),\n} as const;\n\n// =============================================================================\n// Timeout Policies\n// =============================================================================\n\n/**\n * Create a timeout policy with the given options.\n */\nexport function timeoutPolicy(options: TimeoutOptions): Policy {\n return { timeout: options };\n}\n\n/**\n * Pre-built timeout policies for common scenarios.\n */\nexport const timeoutPolicies = {\n /**\n * No timeout.\n */\n none: {} as Policy,\n\n /**\n * Fast timeout for quick operations (1 second).\n */\n fast: timeoutPolicy({ ms: 1000 }),\n\n /**\n * Standard API timeout (5 seconds).\n */\n api: timeoutPolicy({ ms: 5000 }),\n\n /**\n * Extended timeout for slower operations (30 seconds).\n */\n extended: timeoutPolicy({ ms: 30000 }),\n\n /**\n * Long timeout for batch operations (2 minutes).\n */\n long: timeoutPolicy({ ms: 120000 }),\n\n /**\n * Custom timeout in milliseconds.\n */\n ms: (ms: number): Policy => timeoutPolicy({ ms }),\n\n /**\n * Custom timeout in seconds.\n */\n seconds: (seconds: number): Policy => timeoutPolicy({ ms: seconds * 1000 }),\n\n /**\n * Timeout with custom error.\n */\n withError: <E>(ms: number, error: E): Policy =>\n timeoutPolicy({ ms, error }),\n\n /**\n * Timeout with AbortSignal support.\n */\n withSignal: (ms: number): Policy =>\n timeoutPolicy({ ms, signal: true }),\n} as const;\n\n// =============================================================================\n// Combined Policies\n// =============================================================================\n\n/**\n * Pre-built combined policies for common service patterns.\n */\nexport const servicePolicies = {\n /**\n * Policy for external HTTP APIs.\n * - 5 second timeout\n * - 3 retries with exponential backoff\n */\n httpApi: mergePolicies(\n timeoutPolicies.api,\n retryPolicies.standard\n ),\n\n /**\n * Policy for database operations.\n * - 30 second timeout\n * - 2 retries for transient errors\n */\n database: mergePolicies(\n timeoutPolicies.extended,\n retryPolicy({\n attempts: 2,\n backoff: \"exponential\",\n initialDelay: 100,\n maxDelay: 2000,\n jitter: true,\n })\n ),\n\n /**\n * Policy for cache operations.\n * - 1 second timeout\n * - No retry (cache misses are not errors)\n */\n cache: mergePolicies(\n timeoutPolicies.fast,\n retryPolicies.none\n ),\n\n /**\n * Policy for message queue operations.\n * - 30 second timeout\n * - 5 retries with longer backoff\n */\n messageQueue: mergePolicies(\n timeoutPolicies.extended,\n retryPolicies.aggressive\n ),\n\n /**\n * Policy for file operations.\n * - 2 minute timeout\n * - 3 retries\n */\n fileSystem: mergePolicies(\n timeoutPolicies.long,\n retryPolicies.standard\n ),\n\n /**\n * Policy for third-party services with rate limits.\n * - 10 second timeout\n * - 5 retries with linear backoff\n */\n rateLimited: mergePolicies(\n timeoutPolicy({ ms: 10000 }),\n retryPolicy({\n attempts: 5,\n backoff: \"linear\",\n initialDelay: 1000,\n maxDelay: 10000,\n jitter: true,\n })\n ),\n} as const;\n\n// =============================================================================\n// Policy Decorators\n// =============================================================================\n\n/**\n * Options for withPolicies workflow wrapper.\n */\nexport interface WithPoliciesOptions {\n /**\n * Base policies applied to all steps.\n */\n policies: Policy[];\n\n /**\n * Step-specific policy overrides by name or key pattern.\n */\n overrides?: Record<string, Policy>;\n}\n\n/**\n * Create step options with policies applied.\n * This is a helper for applying policies inline.\n *\n * @param policies - Policies to apply\n * @param stepOptions - Step-specific options\n * @returns Merged StepOptions\n *\n * @example\n * ```typescript\n * const user = await step(\n * () => fetchUser(id),\n * withPolicy(servicePolicies.httpApi, { name: 'fetch-user' })\n * );\n * ```\n */\nexport function withPolicy(\n policy: Policy,\n stepOptions?: StepOptions | string\n): StepOptions {\n const opts = typeof stepOptions === \"string\" ? { name: stepOptions } : (stepOptions ?? {});\n return mergePolicies(policy, opts);\n}\n\n/**\n * Create step options with multiple policies applied.\n *\n * @param policies - Policies to apply (in order)\n * @param stepOptions - Step-specific options\n * @returns Merged StepOptions\n *\n * @example\n * ```typescript\n * const user = await step(\n * () => fetchUser(id),\n * withPolicies([timeoutPolicies.api, retryPolicies.standard], { name: 'fetch-user' })\n * );\n * ```\n */\nexport function withPolicies(\n policies: Policy[],\n stepOptions?: StepOptions | string\n): StepOptions {\n const opts = typeof stepOptions === \"string\" ? { name: stepOptions } : (stepOptions ?? {});\n return mergePolicies(...policies, opts);\n}\n\n// =============================================================================\n// Conditional Policies\n// =============================================================================\n\n/**\n * Create a policy that applies conditionally.\n *\n * @param condition - Condition to check\n * @param policy - Policy to apply if condition is true\n * @param elsePolicy - Policy to apply if condition is false (optional)\n * @returns The selected policy\n *\n * @example\n * ```typescript\n * const policy = conditionalPolicy(\n * isProduction,\n * servicePolicies.httpApi, // Use in production\n * retryPolicies.none // Skip in development\n * );\n * ```\n */\nexport function conditionalPolicy(\n condition: boolean,\n policy: Policy,\n elsePolicy: Policy = {}\n): Policy {\n return condition ? policy : elsePolicy;\n}\n\n/**\n * Create a policy based on environment.\n *\n * @param envPolicies - Map of environment names to policies\n * @param currentEnv - Current environment (defaults to NODE_ENV)\n * @param defaultPolicy - Default policy if environment not found\n * @returns The selected policy\n *\n * @example\n * ```typescript\n * const policy = envPolicy({\n * production: servicePolicies.httpApi,\n * development: retryPolicies.none,\n * test: retryPolicies.none,\n * });\n * ```\n */\nexport function envPolicy(\n envPolicies: Record<string, Policy>,\n currentEnv: string = process.env.NODE_ENV ?? \"development\",\n defaultPolicy: Policy = {}\n): Policy {\n return envPolicies[currentEnv] ?? defaultPolicy;\n}\n\n// =============================================================================\n// Policy Registry\n// =============================================================================\n\n/**\n * A registry for managing named policies.\n */\nexport interface PolicyRegistry {\n /**\n * Register a named policy.\n */\n register(name: string, policy: Policy): void;\n\n /**\n * Get a policy by name.\n */\n get(name: string): Policy | undefined;\n\n /**\n * Check if a policy exists.\n */\n has(name: string): boolean;\n\n /**\n * Get all registered policy names.\n */\n names(): string[];\n\n /**\n * Create step options using a registered policy.\n */\n apply(policyName: string, stepOptions?: StepOptions | string): StepOptions;\n}\n\n/**\n * Create a policy registry for managing named policies.\n *\n * @returns PolicyRegistry instance\n *\n * @example\n * ```typescript\n * const registry = createPolicyRegistry();\n *\n * // Register policies\n * registry.register('api', servicePolicies.httpApi);\n * registry.register('db', servicePolicies.database);\n *\n * // Use in workflow\n * const user = await step(\n * () => fetchUser(id),\n * registry.apply('api', { name: 'fetch-user' })\n * );\n * ```\n */\nexport function createPolicyRegistry(): PolicyRegistry {\n const policies = new Map<string, Policy>();\n\n return {\n register(name: string, policy: Policy): void {\n policies.set(name, policy);\n },\n\n get(name: string): Policy | undefined {\n return policies.get(name);\n },\n\n has(name: string): boolean {\n return policies.has(name);\n },\n\n names(): string[] {\n return Array.from(policies.keys());\n },\n\n apply(policyName: string, stepOptions?: StepOptions | string): StepOptions {\n const policy = policies.get(policyName);\n if (!policy) {\n throw new Error(`Policy not found: ${policyName}`);\n }\n return withPolicy(policy, stepOptions);\n },\n };\n}\n\n// =============================================================================\n// Step Options Builder (Fluent API)\n// =============================================================================\n\n/**\n * Fluent builder for constructing step options.\n */\nexport interface StepOptionsBuilder {\n /**\n * Set step name.\n */\n name(name: string): StepOptionsBuilder;\n\n /**\n * Set step key for caching.\n */\n key(key: string): StepOptionsBuilder;\n\n /**\n * Apply a policy.\n */\n policy(policy: Policy): StepOptionsBuilder;\n\n /**\n * Set timeout in milliseconds.\n */\n timeout(ms: number): StepOptionsBuilder;\n\n /**\n * Set retry options.\n */\n retry(options: RetryOptions): StepOptionsBuilder;\n\n /**\n * Set retry attempts (with default exponential backoff).\n */\n retries(attempts: number): StepOptionsBuilder;\n\n /**\n * Build the final StepOptions.\n */\n build(): StepOptions;\n}\n\n/**\n * Create a fluent builder for step options.\n *\n * @returns StepOptionsBuilder instance\n *\n * @example\n * ```typescript\n * const options = stepOptions()\n * .name('fetch-user')\n * .key('user:123')\n * .timeout(5000)\n * .retries(3)\n * .build();\n *\n * const user = await step(() => fetchUser(id), options);\n * ```\n */\nexport function stepOptions(): StepOptionsBuilder {\n const policies: Policy[] = [];\n\n const builder: StepOptionsBuilder = {\n name(name: string) {\n policies.push({ name });\n return builder;\n },\n\n key(key: string) {\n policies.push({ key });\n return builder;\n },\n\n policy(policy: Policy) {\n policies.push(policy);\n return builder;\n },\n\n timeout(ms: number) {\n policies.push(timeoutPolicy({ ms }));\n return builder;\n },\n\n retry(options: RetryOptions) {\n policies.push(retryPolicy(options));\n return builder;\n },\n\n retries(attempts: number) {\n policies.push(retryPolicies.custom({ attempts }));\n return builder;\n },\n\n build(): StepOptions {\n return mergePolicies(...policies);\n },\n };\n\n return builder;\n}\n","/**\n * @jagreehal/workflow/persistence\n *\n * Pluggable Persistence Adapters for StepCache and ResumeState.\n * Provides adapters for Redis, file system, and in-memory storage,\n * plus helpers for JSON-safe serialization of causes.\n */\n\nimport type { Result, StepFailureMeta } from \"./core\";\nimport { ok, err } from \"./core\";\nimport type { StepCache, ResumeState, ResumeStateEntry } from \"./workflow\";\n\n// =============================================================================\n// Serialization Types\n// =============================================================================\n\n/**\n * JSON-safe representation of a Result.\n */\nexport interface SerializedResult {\n ok: boolean;\n value?: unknown;\n error?: unknown;\n cause?: SerializedCause;\n}\n\n/**\n * JSON-safe representation of a cause value.\n * Handles Error objects and other non-JSON-safe types.\n */\nexport interface SerializedCause {\n type: \"error\" | \"value\" | \"undefined\";\n errorName?: string;\n errorMessage?: string;\n errorStack?: string;\n value?: unknown;\n}\n\n/**\n * JSON-safe representation of StepFailureMeta.\n */\nexport interface SerializedMeta {\n origin: \"result\" | \"throw\";\n resultCause?: SerializedCause;\n thrown?: SerializedCause;\n}\n\n/**\n * JSON-safe representation of a ResumeStateEntry.\n */\nexport interface SerializedEntry {\n result: SerializedResult;\n meta?: SerializedMeta;\n}\n\n/**\n * JSON-safe representation of ResumeState.\n */\nexport interface SerializedState {\n version: number;\n entries: Record<string, SerializedEntry>;\n metadata?: Record<string, unknown>;\n}\n\n// =============================================================================\n// Serialization Helpers\n// =============================================================================\n\n/**\n * Serialize a cause value to a JSON-safe format.\n */\nexport function serializeCause(cause: unknown): SerializedCause {\n if (cause === undefined) {\n return { type: \"undefined\" };\n }\n\n if (cause instanceof Error) {\n return {\n type: \"error\",\n errorName: cause.name,\n errorMessage: cause.message,\n errorStack: cause.stack,\n };\n }\n\n // Try to serialize as JSON\n try {\n // Test if it's JSON-serializable\n JSON.stringify(cause);\n return { type: \"value\", value: cause };\n } catch {\n // Fall back to string representation\n return { type: \"value\", value: String(cause) };\n }\n}\n\n/**\n * Deserialize a cause value from JSON-safe format.\n */\nexport function deserializeCause(serialized: SerializedCause): unknown {\n if (serialized.type === \"undefined\") {\n return undefined;\n }\n\n if (serialized.type === \"error\") {\n const error = new Error(serialized.errorMessage ?? \"Unknown error\");\n error.name = serialized.errorName ?? \"Error\";\n if (serialized.errorStack) {\n error.stack = serialized.errorStack;\n }\n return error;\n }\n\n return serialized.value;\n}\n\n/**\n * Serialize a Result to a JSON-safe format.\n */\nexport function serializeResult(result: Result<unknown, unknown, unknown>): SerializedResult {\n if (result.ok) {\n return { ok: true, value: result.value };\n }\n\n return {\n ok: false,\n error: result.error,\n cause: result.cause !== undefined ? serializeCause(result.cause) : undefined,\n };\n}\n\n/**\n * Deserialize a Result from JSON-safe format.\n */\nexport function deserializeResult(serialized: SerializedResult): Result<unknown, unknown, unknown> {\n if (serialized.ok) {\n return ok(serialized.value);\n }\n\n const cause = serialized.cause ? deserializeCause(serialized.cause) : undefined;\n return err(serialized.error, cause !== undefined ? { cause } : undefined);\n}\n\n/**\n * Serialize StepFailureMeta to a JSON-safe format.\n */\nexport function serializeMeta(meta: StepFailureMeta): SerializedMeta {\n if (meta.origin === \"result\") {\n return {\n origin: \"result\",\n resultCause: meta.resultCause !== undefined ? serializeCause(meta.resultCause) : undefined,\n };\n }\n\n return {\n origin: \"throw\",\n thrown: serializeCause(meta.thrown),\n };\n}\n\n/**\n * Deserialize StepFailureMeta from JSON-safe format.\n */\nexport function deserializeMeta(serialized: SerializedMeta): StepFailureMeta {\n if (serialized.origin === \"result\") {\n return {\n origin: \"result\",\n resultCause: serialized.resultCause ? deserializeCause(serialized.resultCause) : undefined,\n };\n }\n\n return {\n origin: \"throw\",\n thrown: serialized.thrown ? deserializeCause(serialized.thrown) : undefined,\n };\n}\n\n/**\n * Serialize a ResumeStateEntry to a JSON-safe format.\n */\nexport function serializeEntry(entry: ResumeStateEntry): SerializedEntry {\n return {\n result: serializeResult(entry.result),\n meta: entry.meta ? serializeMeta(entry.meta) : undefined,\n };\n}\n\n/**\n * Deserialize a ResumeStateEntry from JSON-safe format.\n */\nexport function deserializeEntry(serialized: SerializedEntry): ResumeStateEntry {\n return {\n result: deserializeResult(serialized.result),\n meta: serialized.meta ? deserializeMeta(serialized.meta) : undefined,\n };\n}\n\n/**\n * Serialize ResumeState to a JSON-safe format.\n */\nexport function serializeState(state: ResumeState, metadata?: Record<string, unknown>): SerializedState {\n const entries: Record<string, SerializedEntry> = {};\n\n for (const [key, entry] of state.steps) {\n entries[key] = serializeEntry(entry);\n }\n\n return {\n version: 1,\n entries,\n metadata,\n };\n}\n\n/**\n * Deserialize ResumeState from JSON-safe format.\n */\nexport function deserializeState(serialized: SerializedState): ResumeState {\n const steps = new Map<string, ResumeStateEntry>();\n\n for (const [key, entry] of Object.entries(serialized.entries)) {\n steps.set(key, deserializeEntry(entry));\n }\n\n return { steps };\n}\n\n/**\n * Convert ResumeState to a JSON string.\n */\nexport function stringifyState(state: ResumeState, metadata?: Record<string, unknown>): string {\n return JSON.stringify(serializeState(state, metadata));\n}\n\n/**\n * Parse ResumeState from a JSON string.\n */\nexport function parseState(json: string): ResumeState {\n const serialized = JSON.parse(json) as SerializedState;\n return deserializeState(serialized);\n}\n\n// =============================================================================\n// In-Memory Adapter\n// =============================================================================\n\n/**\n * Options for the in-memory cache adapter.\n */\nexport interface MemoryCacheOptions {\n /**\n * Maximum number of entries to store.\n * Oldest entries are evicted when limit is reached.\n */\n maxSize?: number;\n\n /**\n * Time-to-live in milliseconds.\n * Entries are automatically removed after this duration.\n */\n ttl?: number;\n}\n\n/**\n * Create an in-memory StepCache with optional LRU eviction and TTL.\n *\n * @param options - Cache options\n * @returns StepCache implementation\n *\n * @example\n * ```typescript\n * const cache = createMemoryCache({ maxSize: 1000, ttl: 60000 });\n * const workflow = createWorkflow(deps, { cache });\n * ```\n */\nexport function createMemoryCache(options: MemoryCacheOptions = {}): StepCache {\n const { maxSize, ttl } = options;\n const cache = new Map<string, { result: Result<unknown, unknown, unknown>; timestamp: number }>();\n\n const isExpired = (timestamp: number): boolean => {\n if (!ttl) return false;\n return Date.now() - timestamp > ttl;\n };\n\n const evictExpired = (): void => {\n if (!ttl) return;\n for (const [key, entry] of cache) {\n if (isExpired(entry.timestamp)) {\n cache.delete(key);\n }\n }\n };\n\n const evictOldest = (): void => {\n if (!maxSize || cache.size < maxSize) return;\n\n // Find oldest entry\n let oldestKey: string | undefined;\n let oldestTime = Infinity;\n\n for (const [key, entry] of cache) {\n if (entry.timestamp < oldestTime) {\n oldestTime = entry.timestamp;\n oldestKey = key;\n }\n }\n\n if (oldestKey) {\n cache.delete(oldestKey);\n }\n };\n\n return {\n get(key: string): Result<unknown, unknown, unknown> | undefined {\n evictExpired();\n const entry = cache.get(key);\n if (!entry) return undefined;\n if (isExpired(entry.timestamp)) {\n cache.delete(key);\n return undefined;\n }\n return entry.result;\n },\n\n set(key: string, result: Result<unknown, unknown, unknown>): void {\n evictExpired();\n evictOldest();\n cache.set(key, { result, timestamp: Date.now() });\n },\n\n has(key: string): boolean {\n evictExpired();\n const entry = cache.get(key);\n if (!entry) return false;\n if (isExpired(entry.timestamp)) {\n cache.delete(key);\n return false;\n }\n return true;\n },\n\n delete(key: string): boolean {\n return cache.delete(key);\n },\n\n clear(): void {\n cache.clear();\n },\n };\n}\n\n// =============================================================================\n// File System Adapter\n// =============================================================================\n\n/**\n * Options for the file system cache adapter.\n */\nexport interface FileCacheOptions {\n /**\n * Directory to store cache files.\n */\n directory: string;\n\n /**\n * File extension for cache files.\n * @default '.json'\n */\n extension?: string;\n\n /**\n * Custom file system interface (for testing or custom implementations).\n */\n fs?: FileSystemInterface;\n}\n\n/**\n * Minimal file system interface for cache operations.\n */\nexport interface FileSystemInterface {\n readFile(path: string): Promise<string>;\n writeFile(path: string, data: string): Promise<void>;\n unlink(path: string): Promise<void>;\n exists(path: string): Promise<boolean>;\n readdir(path: string): Promise<string[]>;\n mkdir(path: string, options?: { recursive?: boolean }): Promise<void>;\n}\n\n/**\n * Create a file system-based StepCache.\n * Each step result is stored as a separate JSON file.\n *\n * @param options - Cache options\n * @returns StepCache implementation (async operations wrapped in sync interface)\n *\n * @example\n * ```typescript\n * import * as fs from 'fs/promises';\n *\n * const cache = createFileCache({\n * directory: './workflow-cache',\n * fs: {\n * readFile: (path) => fs.readFile(path, 'utf-8'),\n * writeFile: (path, data) => fs.writeFile(path, data, 'utf-8'),\n * unlink: fs.unlink,\n * exists: async (path) => fs.access(path).then(() => true).catch(() => false),\n * readdir: fs.readdir,\n * mkdir: fs.mkdir,\n * },\n * });\n * ```\n */\nexport function createFileCache(options: FileCacheOptions): StepCache & {\n /** Initialize the cache directory. Call before using the cache. */\n init(): Promise<void>;\n /** Get a result asynchronously. */\n getAsync(key: string): Promise<Result<unknown, unknown, unknown> | undefined>;\n /** Set a result asynchronously. */\n setAsync(key: string, result: Result<unknown, unknown, unknown>): Promise<void>;\n /** Delete a result asynchronously. */\n deleteAsync(key: string): Promise<boolean>;\n /** Clear all results asynchronously. */\n clearAsync(): Promise<void>;\n} {\n const { directory, extension = \".json\", fs } = options;\n\n if (!fs) {\n throw new Error(\"File system interface is required. Pass fs option with readFile, writeFile, etc.\");\n }\n\n const keyToPath = (key: string): string => {\n // Sanitize key for file system\n const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n return `${directory}/${safeKey}${extension}`;\n };\n\n // In-memory fallback for sync operations\n const memoryCache = new Map<string, Result<unknown, unknown, unknown>>();\n\n return {\n async init(): Promise<void> {\n await fs.mkdir(directory, { recursive: true });\n },\n\n get(key: string): Result<unknown, unknown, unknown> | undefined {\n // Sync operation uses memory cache\n return memoryCache.get(key);\n },\n\n async getAsync(key: string): Promise<Result<unknown, unknown, unknown> | undefined> {\n const path = keyToPath(key);\n try {\n if (!(await fs.exists(path))) return undefined;\n const data = await fs.readFile(path);\n const serialized = JSON.parse(data) as SerializedResult;\n const result = deserializeResult(serialized);\n memoryCache.set(key, result);\n return result;\n } catch {\n return undefined;\n }\n },\n\n set(key: string, result: Result<unknown, unknown, unknown>): void {\n // Sync operation updates memory cache\n memoryCache.set(key, result);\n },\n\n async setAsync(key: string, result: Result<unknown, unknown, unknown>): Promise<void> {\n const path = keyToPath(key);\n const serialized = serializeResult(result);\n await fs.writeFile(path, JSON.stringify(serialized, null, 2));\n memoryCache.set(key, result);\n },\n\n has(key: string): boolean {\n return memoryCache.has(key);\n },\n\n delete(key: string): boolean {\n return memoryCache.delete(key);\n },\n\n async deleteAsync(key: string): Promise<boolean> {\n const path = keyToPath(key);\n try {\n await fs.unlink(path);\n memoryCache.delete(key);\n return true;\n } catch {\n return false;\n }\n },\n\n clear(): void {\n memoryCache.clear();\n },\n\n async clearAsync(): Promise<void> {\n try {\n const files = await fs.readdir(directory);\n for (const file of files) {\n if (file.endsWith(extension)) {\n await fs.unlink(`${directory}/${file}`);\n }\n }\n memoryCache.clear();\n } catch {\n // Directory may not exist\n }\n },\n };\n}\n\n// =============================================================================\n// Key-Value Store Adapter\n// =============================================================================\n\n/**\n * Generic key-value store interface.\n * Implement this for Redis, DynamoDB, etc.\n */\nexport interface KeyValueStore {\n get(key: string): Promise<string | null>;\n set(key: string, value: string, options?: { ttl?: number }): Promise<void>;\n delete(key: string): Promise<boolean>;\n exists(key: string): Promise<boolean>;\n keys(pattern: string): Promise<string[]>;\n}\n\n/**\n * Options for key-value store cache adapter.\n */\nexport interface KVCacheOptions {\n /**\n * Key-value store implementation.\n */\n store: KeyValueStore;\n\n /**\n * Key prefix for all cache entries.\n * @default 'workflow:'\n */\n prefix?: string;\n\n /**\n * Time-to-live in seconds for cache entries.\n */\n ttl?: number;\n}\n\n/**\n * Create a StepCache backed by a key-value store (Redis, DynamoDB, etc.).\n *\n * @param options - Cache options\n * @returns StepCache implementation with async methods\n *\n * @example\n * ```typescript\n * // With Redis\n * import { createClient } from 'redis';\n *\n * const redis = createClient();\n * await redis.connect();\n *\n * const cache = createKVCache({\n * store: {\n * get: (key) => redis.get(key),\n * set: (key, value, opts) => redis.set(key, value, { EX: opts?.ttl }),\n * delete: (key) => redis.del(key).then(n => n > 0),\n * exists: (key) => redis.exists(key).then(n => n > 0),\n * keys: (pattern) => redis.keys(pattern),\n * },\n * prefix: 'myapp:workflow:',\n * ttl: 3600, // 1 hour\n * });\n * ```\n */\nexport function createKVCache(options: KVCacheOptions): StepCache & {\n /** Get a result asynchronously. */\n getAsync(key: string): Promise<Result<unknown, unknown, unknown> | undefined>;\n /** Set a result asynchronously. */\n setAsync(key: string, result: Result<unknown, unknown, unknown>): Promise<void>;\n /** Check if key exists asynchronously. */\n hasAsync(key: string): Promise<boolean>;\n /** Delete a result asynchronously. */\n deleteAsync(key: string): Promise<boolean>;\n /** Clear all results asynchronously. */\n clearAsync(): Promise<void>;\n} {\n const { store, prefix = \"workflow:\", ttl } = options;\n\n const prefixKey = (key: string): string => `${prefix}${key}`;\n\n // In-memory fallback for sync operations\n const memoryCache = new Map<string, Result<unknown, unknown, unknown>>();\n\n return {\n get(key: string): Result<unknown, unknown, unknown> | undefined {\n return memoryCache.get(key);\n },\n\n async getAsync(key: string): Promise<Result<unknown, unknown, unknown> | undefined> {\n const data = await store.get(prefixKey(key));\n if (!data) return undefined;\n\n try {\n const serialized = JSON.parse(data) as SerializedResult;\n const result = deserializeResult(serialized);\n memoryCache.set(key, result);\n return result;\n } catch {\n return undefined;\n }\n },\n\n set(key: string, result: Result<unknown, unknown, unknown>): void {\n memoryCache.set(key, result);\n },\n\n async setAsync(key: string, result: Result<unknown, unknown, unknown>): Promise<void> {\n const serialized = serializeResult(result);\n await store.set(prefixKey(key), JSON.stringify(serialized), ttl ? { ttl } : undefined);\n memoryCache.set(key, result);\n },\n\n has(key: string): boolean {\n return memoryCache.has(key);\n },\n\n async hasAsync(key: string): Promise<boolean> {\n return store.exists(prefixKey(key));\n },\n\n delete(key: string): boolean {\n return memoryCache.delete(key);\n },\n\n async deleteAsync(key: string): Promise<boolean> {\n memoryCache.delete(key);\n return store.delete(prefixKey(key));\n },\n\n clear(): void {\n memoryCache.clear();\n },\n\n async clearAsync(): Promise<void> {\n const keys = await store.keys(`${prefix}*`);\n for (const key of keys) {\n await store.delete(key);\n }\n memoryCache.clear();\n },\n };\n}\n\n// =============================================================================\n// State Persistence\n// =============================================================================\n\n/**\n * Interface for persisting workflow state.\n */\nexport interface StatePersistence {\n /**\n * Save workflow state.\n */\n save(runId: string, state: ResumeState, metadata?: Record<string, unknown>): Promise<void>;\n\n /**\n * Load workflow state.\n */\n load(runId: string): Promise<ResumeState | undefined>;\n\n /**\n * Delete workflow state.\n */\n delete(runId: string): Promise<boolean>;\n\n /**\n * List all saved workflow IDs.\n */\n list(): Promise<string[]>;\n}\n\n/**\n * Create a state persistence adapter using a key-value store.\n *\n * @param store - Key-value store implementation\n * @param prefix - Key prefix for state entries\n * @returns StatePersistence implementation\n */\nexport function createStatePersistence(\n store: KeyValueStore,\n prefix = \"workflow:state:\"\n): StatePersistence {\n const prefixKey = (runId: string): string => `${prefix}${runId}`;\n\n return {\n async save(runId: string, state: ResumeState, metadata?: Record<string, unknown>): Promise<void> {\n const serialized = serializeState(state, metadata);\n await store.set(prefixKey(runId), JSON.stringify(serialized));\n },\n\n async load(runId: string): Promise<ResumeState | undefined> {\n const data = await store.get(prefixKey(runId));\n if (!data) return undefined;\n\n try {\n const serialized = JSON.parse(data) as SerializedState;\n return deserializeState(serialized);\n } catch {\n return undefined;\n }\n },\n\n async delete(runId: string): Promise<boolean> {\n return store.delete(prefixKey(runId));\n },\n\n async list(): Promise<string[]> {\n const keys = await store.keys(`${prefix}*`);\n return keys.map((key) => key.slice(prefix.length));\n },\n };\n}\n\n// =============================================================================\n// Cache Wrapper with Async Hydration\n// =============================================================================\n\n/**\n * Create a cache that hydrates from persistent storage on first access.\n *\n * @param memoryCache - In-memory cache for fast access\n * @param persistence - Persistent storage for durability\n * @returns Hydrating cache implementation\n */\nexport function createHydratingCache(\n memoryCache: StepCache,\n persistence: StatePersistence,\n runId: string\n): StepCache & { hydrate(): Promise<void> } {\n let hydrated = false;\n\n return {\n async hydrate(): Promise<void> {\n if (hydrated) return;\n\n const state = await persistence.load(runId);\n if (state) {\n for (const [key, entry] of state.steps) {\n memoryCache.set(key, entry.result);\n }\n }\n hydrated = true;\n },\n\n get(key: string): Result<unknown, unknown, unknown> | undefined {\n return memoryCache.get(key);\n },\n\n set(key: string, result: Result<unknown, unknown, unknown>): void {\n memoryCache.set(key, result);\n },\n\n has(key: string): boolean {\n return memoryCache.has(key);\n },\n\n delete(key: string): boolean {\n return memoryCache.delete(key);\n },\n\n clear(): void {\n memoryCache.clear();\n },\n };\n}\n","/**\n * Timing utilities for workflow visualization.\n */\n\n/**\n * Format duration in milliseconds to a human-readable string.\n *\n * @example\n * formatDuration(23) // \"23ms\"\n * formatDuration(1500) // \"1.5s\"\n * formatDuration(65000) // \"1m 5s\"\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)}ms`;\n }\n\n if (ms < 60000) {\n const seconds = ms / 1000;\n // Show one decimal for seconds\n return `${seconds.toFixed(1).replace(/\\.0$/, \"\")}s`;\n }\n\n const minutes = Math.floor(ms / 60000);\n const seconds = Math.round((ms % 60000) / 1000);\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 (\"decisionId\" in node) {\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 \"../core\";\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} 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// =============================================================================\n// Builder State\n// =============================================================================\n\ninterface ActiveStep {\n id: string;\n name?: string;\n key?: string;\n startTs: number;\n retryCount: number;\n timedOut: boolean;\n timeoutMs?: number;\n}\n\ninterface ActiveScope {\n id: string;\n name?: string;\n type: ScopeType;\n startTs: number;\n children: FlowNode[];\n}\n\ninterface ActiveDecision {\n id: string;\n name?: string;\n condition?: string;\n decisionValue?: unknown;\n startTs: number;\n branches: Map<string, DecisionBranch>;\n branchTaken?: string | boolean;\n}\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 { detectParallel = true, parallelDetection } = options;\n\n // Current workflow state\n let workflowId: string | undefined;\n let workflowStartTs: 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 // 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 /**\n * Get the step ID from an event.\n * Uses stepId if available (new events), then falls back to stepKey or name,\n * and finally generates a random ID for backwards compatibility.\n */\n function getStepId(event: { stepId?: string; stepKey?: string; name?: string }): string {\n return event.stepId ?? event.stepKey ?? event.name ?? generateId();\n }\n\n /**\n * Add a completed node to the current scope or decision branch.\n */\n function addNode(node: FlowNode): void {\n // If we're in a decision, 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 // If no branch is marked as taken yet, add to the first branch\n // (this handles cases where steps execute before branch is marked)\n const firstBranch = Array.from(decision.branches.values())[0];\n if (firstBranch) {\n firstBranch.children.push(node);\n lastUpdatedAt = Date.now();\n return;\n }\n }\n\n // If we're in a scope, add to the scope\n if (scopeStack.length > 0) {\n // Add to the innermost scope\n scopeStack[scopeStack.length - 1].children.push(node);\n } else {\n // Add to the root level\n currentNodes.push(node);\n }\n lastUpdatedAt = Date.now();\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 workflowId = event.workflowId;\n workflowStartTs = event.ts;\n workflowState = \"running\";\n createdAt = Date.now();\n lastUpdatedAt = createdAt;\n break;\n\n case \"workflow_success\":\n workflowState = \"success\";\n workflowDurationMs = event.durationMs;\n lastUpdatedAt = Date.now();\n break;\n\n case \"workflow_error\":\n workflowState = \"error\";\n workflowError = event.error;\n workflowDurationMs = event.durationMs;\n lastUpdatedAt = Date.now();\n break;\n\n case \"step_start\": {\n const id = getStepId(event);\n activeSteps.set(id, {\n id,\n name: event.name,\n key: event.stepKey,\n startTs: event.ts,\n retryCount: 0,\n timedOut: false,\n });\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"step_success\": {\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n const node: StepNode = {\n type: \"step\",\n id: active.id,\n name: active.name,\n key: active.key,\n state: \"success\",\n startTs: active.startTs,\n endTs: event.ts,\n durationMs: event.durationMs,\n ...(active.retryCount > 0 && { retryCount: active.retryCount }),\n ...(active.timedOut && { timedOut: true, timeoutMs: active.timeoutMs }),\n };\n addNode(node);\n activeSteps.delete(id);\n }\n break;\n }\n\n case \"step_error\": {\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n const node: StepNode = {\n type: \"step\",\n id: active.id,\n name: active.name,\n key: active.key,\n state: \"error\",\n startTs: active.startTs,\n endTs: event.ts,\n durationMs: event.durationMs,\n error: event.error,\n ...(active.retryCount > 0 && { retryCount: active.retryCount }),\n ...(active.timedOut && { timedOut: true, timeoutMs: active.timeoutMs }),\n };\n addNode(node);\n activeSteps.delete(id);\n }\n break;\n }\n\n case \"step_aborted\": {\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n const node: StepNode = {\n type: \"step\",\n id: active.id,\n name: active.name,\n key: active.key,\n state: \"aborted\",\n startTs: active.startTs,\n endTs: event.ts,\n durationMs: event.durationMs,\n ...(active.retryCount > 0 && { retryCount: active.retryCount }),\n ...(active.timedOut && { timedOut: true, timeoutMs: active.timeoutMs }),\n };\n addNode(node);\n activeSteps.delete(id);\n }\n break;\n }\n\n case \"step_cache_hit\": {\n const id = getStepId(event);\n const node: StepNode = {\n type: \"step\",\n id,\n name: event.name,\n key: event.stepKey,\n state: \"cached\",\n startTs: event.ts,\n endTs: event.ts,\n durationMs: 0,\n };\n addNode(node);\n break;\n }\n\n case \"step_cache_miss\":\n // Cache miss just means the step will execute normally\n // We'll get a step_start event next\n break;\n\n case \"step_complete\":\n // step_complete is for state persistence, not visualization\n // We already handled the step via step_success/step_error\n break;\n\n case \"step_timeout\": {\n // Timeout is an intermediate event - step may retry or will get step_error\n // Track timeout info on the active step\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n active.timedOut = true;\n active.timeoutMs = event.timeoutMs;\n }\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"step_retry\": {\n // Retry is an intermediate event - increment retry counter\n const id = getStepId(event);\n const active = activeSteps.get(id);\n if (active) {\n active.retryCount = (event.attempt ?? 1) - 1; // attempt is 1-indexed, retryCount is 0-indexed\n }\n lastUpdatedAt = Date.now();\n break;\n }\n\n case \"step_retries_exhausted\":\n // All retries exhausted - step_error will follow\n // The error state will be set by step_error handler\n lastUpdatedAt = Date.now();\n break;\n\n case \"step_skipped\": {\n const id = getStepId(event);\n const node: StepNode = {\n type: \"step\",\n id,\n name: event.name,\n key: event.stepKey,\n state: \"skipped\",\n startTs: event.ts,\n endTs: event.ts,\n durationMs: 0,\n };\n addNode(node);\n break;\n }\n }\n }\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 const scope = scopeStack.pop();\n if (scope) {\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 /**\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 });\n lastUpdatedAt = Date.now();\n } else if (event.type === \"decision_branch\") {\n const decision = decisionStack[decisionStack.length - 1];\n if (decision && decision.id === event.decisionId) {\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 } else {\n // Create new branch\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 const decision = decisionStack.pop();\n if (decision && decision.id === event.decisionId) {\n // Convert branches map to array\n const branches: DecisionBranch[] = Array.from(decision.branches.values());\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 ?? decision.branchTaken,\n branches,\n };\n addNode(node);\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.\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 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 ?? generateId(),\n workflowId: workflowId ?? \"unknown\",\n state: workflowState,\n startTs: workflowStartTs,\n durationMs: workflowDurationMs,\n children,\n error: workflowError,\n };\n\n return {\n root,\n metadata: {\n createdAt,\n lastUpdatedAt,\n },\n };\n }\n\n /**\n * Reset the builder state.\n */\n function reset(): void {\n workflowId = undefined;\n workflowStartTs = undefined;\n workflowState = \"pending\";\n workflowError = undefined;\n workflowDurationMs = undefined;\n activeSteps.clear();\n scopeStack.length = 0;\n decisionStack.length = 0;\n currentNodes = [];\n createdAt = Date.now();\n lastUpdatedAt = createdAt;\n }\n\n return {\n handleEvent,\n handleScopeEvent,\n handleDecisionEvent,\n getIR,\n reset,\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 };\n}\n\n/**\n * Type for the IR builder instance.\n */\nexport type IRBuilder = ReturnType<typeof createIRBuilder>;\n","/**\n * Workflow Visualization - Intermediate Representation Types\n *\n * The IR (Intermediate Representation) is a DSL that represents workflow\n * execution structure. Events are converted to IR, which can then be\n * rendered to various output formats (ASCII, Mermaid, JSON, etc.).\n */\n\n// =============================================================================\n// Step States\n// =============================================================================\n\n/**\n * Execution state of a step with semantic meaning for visualization.\n *\n * Color mapping:\n * - pending → white/clear (not started)\n * - running → yellow (currently executing)\n * - success → green (completed successfully)\n * - error → red (failed with error)\n * - aborted → gray (cancelled, e.g., in race)\n * - cached → blue (served from cache)\n * - skipped → dim gray (not executed due to conditional logic)\n */\nexport type StepState =\n | \"pending\"\n | \"running\"\n | \"success\"\n | \"error\"\n | \"aborted\"\n | \"cached\"\n | \"skipped\";\n\n// =============================================================================\n// Node Types\n// =============================================================================\n\n/**\n * Base properties shared by all IR nodes.\n */\nexport interface BaseNode {\n /** Unique identifier for this node */\n id: string;\n /** Human-readable name (from step options or inferred) */\n name?: string;\n /** Cache key if this is a keyed step */\n key?: string;\n /** Current execution state */\n state: StepState;\n /** Timestamp when execution started */\n startTs?: number;\n /** Timestamp when execution ended */\n endTs?: number;\n /** Duration in milliseconds */\n durationMs?: number;\n /** Error value if state is 'error' */\n error?: unknown;\n /** Input value that triggered this step (for decision understanding) */\n input?: unknown;\n /** Output value from this step (for decision understanding) */\n output?: unknown;\n /** Number of retry attempts made (0 = no retries, 1 = one retry, etc.) */\n retryCount?: number;\n /** Whether this step experienced a timeout (may have retried after) */\n timedOut?: boolean;\n /** Timeout duration in ms (if timed out) */\n timeoutMs?: number;\n}\n\n/**\n * A single step execution node.\n */\nexport interface StepNode extends BaseNode {\n type: \"step\";\n}\n\n/**\n * Sequential execution - steps run one after another.\n * This is the implicit structure when steps are awaited in sequence.\n */\nexport interface SequenceNode extends BaseNode {\n type: \"sequence\";\n children: FlowNode[];\n}\n\n/**\n * Parallel execution - all branches run simultaneously.\n * Created by allAsync() or allSettledAsync().\n */\nexport interface ParallelNode extends BaseNode {\n type: \"parallel\";\n children: FlowNode[];\n /**\n * Execution mode:\n * - 'all': Fails on first error (allAsync)\n * - 'allSettled': Collects all results (allSettledAsync)\n */\n mode: \"all\" | \"allSettled\";\n}\n\n/**\n * Race execution - first to complete wins.\n * Created by anyAsync().\n */\nexport interface RaceNode extends BaseNode {\n type: \"race\";\n children: FlowNode[];\n /** ID of the winning branch (first to succeed) */\n winnerId?: string;\n}\n\n/**\n * 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;\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}\n\n// =============================================================================\n// Scope Events (for parallel/race detection)\n// =============================================================================\n\n// Re-export ScopeType from core for consistency\nexport type { ScopeType } from \"../core\";\nimport type { ScopeType } from \"../core\";\n\n/**\n * Event emitted when entering a parallel/race scope.\n * This matches the scope_start event in WorkflowEvent.\n */\nexport interface ScopeStartEvent {\n type: \"scope_start\";\n workflowId: string;\n scopeId: string;\n scopeType: ScopeType;\n name?: string;\n ts: number;\n}\n\n/**\n * Event emitted when exiting a parallel/race scope.\n */\nexport interface ScopeEndEvent {\n type: \"scope_end\";\n workflowId: string;\n scopeId: string;\n ts: number;\n durationMs: number;\n /** For race scopes, the ID of the winning branch */\n winnerId?: string;\n}\n\n/**\n * Event emitted when a decision point is encountered.\n * Use this to track conditional logic (if/switch).\n */\nexport interface DecisionStartEvent {\n type: \"decision_start\";\n workflowId: string;\n decisionId: string;\n /** Condition being evaluated (e.g., \"user.role === 'admin'\") */\n condition?: string;\n /** Value being evaluated */\n decisionValue?: unknown;\n /** Name/label for this decision point */\n name?: string;\n ts: number;\n}\n\n/**\n * Event emitted when a decision branch is taken.\n */\nexport interface DecisionBranchEvent {\n type: \"decision_branch\";\n workflowId: string;\n decisionId: string;\n /** Label for this branch (e.g., \"if\", \"else\", \"case 'admin'\") */\n branchLabel: string;\n /** Condition for this branch */\n condition?: string;\n /** Whether this branch was taken */\n taken: boolean;\n ts: number;\n}\n\n/**\n * Event emitted when a decision point completes.\n */\nexport interface DecisionEndEvent {\n type: \"decision_end\";\n workflowId: string;\n decisionId: string;\n /** Which branch was taken */\n branchTaken?: string | boolean;\n ts: number;\n durationMs: number;\n}\n\n/**\n * Event emitted when a step is skipped due to conditional logic.\n */\nexport interface StepSkippedEvent {\n type: \"step_skipped\";\n workflowId: string;\n stepKey?: string;\n name?: string;\n /** Reason why this step was skipped (e.g., \"condition was false\") */\n reason?: string;\n /** The decision that caused this skip */\n decisionId?: string;\n ts: number;\n}\n\n/**\n * Union of scope-related events.\n */\nexport type ScopeEvent = ScopeStartEvent | ScopeEndEvent;\n\n/**\n * Union of decision-related events.\n */\nexport type DecisionEvent = DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent;\n\n// =============================================================================\n// Renderer Types\n// =============================================================================\n\n/**\n * Color scheme for rendering step states.\n */\nexport interface ColorScheme {\n pending: string;\n running: string;\n success: string;\n error: string;\n aborted: string;\n cached: string;\n skipped: string;\n}\n\n/**\n * Options passed to renderers.\n */\nexport interface RenderOptions {\n /** Show timing information (duration) */\n showTimings: boolean;\n /** Show step cache keys */\n showKeys: boolean;\n /** Terminal width for ASCII renderer */\n terminalWidth?: number;\n /** Color scheme */\n colors: ColorScheme;\n}\n\n/**\n * 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\";\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\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 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 * 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 * 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 type {\n FlowNode,\n ParallelNode,\n RaceNode,\n DecisionNode,\n Renderer,\n RenderOptions,\n StepNode,\n WorkflowIR,\n} from \"../types\";\nimport { isParallelNode, isRaceNode, isStepNode, isDecisionNode } 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// 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// 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 const remainingWidth = width - titleText.length;\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// 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 const width = options.terminalWidth ?? 60;\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 children\n const childLines = renderNodes(ir.root.children, options, colors, 0);\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 = ir.root.state === \"success\" ? \"Completed\" : \"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): string[] {\n const lines: string[] = [];\n\n for (const node of nodes) {\n if (isStepNode(node)) {\n lines.push(renderStepNode(node, options, colors));\n } else if (isParallelNode(node)) {\n lines.push(...renderParallelNode(node, options, colors, depth));\n } else if (isRaceNode(node)) {\n lines.push(...renderRaceNode(node, options, colors, depth));\n } else if (isDecisionNode(node)) {\n lines.push(...renderDecisionNode(node, options, colors, depth));\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): string {\n const symbol = getColoredSymbol(node.state, colors);\n const name = node.name ?? node.key ?? \"step\";\n const nameColored = colorByState(name, node.state, colors);\n\n let line = `${symbol} ${nameColored}`;\n\n // Add key if requested\n if (options.showKeys && node.key) {\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 : JSON.stringify(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 : JSON.stringify(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 line += dim(` [${formatDuration(node.durationMs)}]`);\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 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): 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)}`);\n } else {\n // Nested structure - recurse\n const nestedLines = renderNodes([child], options, colors, depth + 1);\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): 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)}${winnerSuffix}`);\n } else {\n const nestedLines = renderNodes([child], options, colors, depth + 1);\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): 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);\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 * 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 type {\n FlowNode,\n ParallelNode,\n RaceNode,\n DecisionNode,\n Renderer,\n RenderOptions,\n StepNode,\n StepState,\n WorkflowIR,\n} from \"../types\";\nimport { isParallelNode, isRaceNode, isStepNode, isDecisionNode } from \"../types\";\nimport { formatDuration } from \"../utils/timing\";\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 ];\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// Node ID Generation\n// =============================================================================\n\nlet nodeCounter = 0;\n\nfunction generateNodeId(prefix: string = \"node\"): string {\n return `${prefix}_${++nodeCounter}`;\n}\n\nfunction resetNodeCounter(): void {\n nodeCounter = 0;\n}\n\n// =============================================================================\n// Mermaid Text Escaping\n// =============================================================================\n\n/**\n * Escape text for use in Mermaid diagrams.\n * Removes characters that break Mermaid parsing.\n * \n * Characters removed:\n * - {}[]() - Brackets and parentheses break parsing in labels\n * - <> - Angle brackets can cause issues\n * - \" - Double quotes replaced with single quotes\n * \n * @param text - Text to escape\n * @returns Escaped text safe for Mermaid\n */\nfunction escapeMermaidText(text: string): string {\n return text\n .replace(/[{}[\\]()]/g, \"\") // Remove brackets and parentheses (they break parsing)\n .replace(/[<>]/g, \"\") // Remove angle brackets\n .replace(/\"/g, \"'\") // Replace double quotes with single\n .trim();\n}\n\n/**\n * Escape text for use in Mermaid subgraph names.\n * Subgraph names in brackets need special handling.\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, \"\"); // Also remove brackets 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 // Diagram header\n lines.push(\"flowchart TD\");\n\n // Start node - more visually distinctive\n const startId = \"start\";\n lines.push(` ${startId}((\"▶ Start\"))`);\n\n // Track the last node for connections\n let prevNodeId = startId;\n\n // Render children\n for (const child of ir.root.children) {\n const result = renderNode(child, options, lines);\n lines.push(` ${prevNodeId} --> ${result.entryId}`);\n prevNodeId = result.exitId;\n }\n\n // End node (if workflow completed) - more visually distinctive\n if (ir.root.state === \"success\" || ir.root.state === \"error\") {\n const endId = \"finish\";\n const endIcon = ir.root.state === \"success\" ? \"✓\" : \"✗\";\n const endLabel = ir.root.state === \"success\" ? \"Done\" : \"Failed\";\n const endShape = `((\"${endIcon} ${endLabel}\"))`;\n const endClass =\n ir.root.state === \"success\" ? \":::success\" : \":::error\";\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 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): RenderResult {\n if (isStepNode(node)) {\n return renderStepNode(node, options, lines);\n } else if (isParallelNode(node)) {\n return renderParallelNode(node, options, lines);\n } else if (isRaceNode(node)) {\n return renderRaceNode(node, options, lines);\n } else if (isDecisionNode(node)) {\n return renderDecisionNode(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): RenderResult {\n const id = node.key\n ? `step_${node.key.replace(/[^a-zA-Z0-9]/g, \"_\")}`\n : generateNodeId(\"step\");\n\n const label = escapeMermaidText(node.name ?? node.key ?? \"Step\");\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(JSON.stringify(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(JSON.stringify(node.output).slice(0, 20));\n ioInfo += `\\\\nout: ${outputStr}`;\n }\n\n // Add retry/timeout indicators with icons\n let retryInfo = \"\";\n if (node.retryCount !== undefined && node.retryCount > 0) {\n retryInfo += `\\\\n↻ ${node.retryCount} retr${node.retryCount === 1 ? \"y\" : \"ies\"}`;\n }\n if (node.timedOut) {\n const timeoutStr = node.timeoutMs !== undefined ? `${node.timeoutMs}ms` : \"\";\n retryInfo += `\\\\n⏱ timeout ${timeoutStr}`;\n }\n\n // Combine all label parts with icon\n const escapedLabel = (stateIcon + label + ioInfo + retryInfo + timing).trim();\n\n const stateClass = getStateClass(node.state);\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 for skipped\n shape = `[${escapedLabel}]:::skipped`;\n break;\n default:\n // Standard rectangle for normal steps\n shape = `[${escapedLabel}]`;\n }\n\n lines.push(` ${id}${shape}:::${stateClass}`);\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): 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);\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): 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);\n const isWinner = isStepNode(child) && 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): RenderResult {\n const decisionId = node.key\n ? `decision_${node.key.replace(/[^a-zA-Z0-9]/g, \"_\")}`\n : generateNodeId(\"decision\");\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\n for (const branch of node.branches) {\n const branchId = `${decisionId}_${branch.label.replace(/[^a-zA-Z0-9]/g, \"_\")}`;\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);\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\nexport { mermaidRenderer as default };\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 '@jagreehal/workflow/visualize';\n *\n * const viz = createVisualizer({ workflowName: 'checkout' });\n * const workflow = createWorkflow(deps, { onEvent: viz.handleEvent });\n *\n * await workflow(async (step) => {\n * await step(() => validateCart(cart), 'Validate cart');\n * await step(() => processPayment(payment), 'Process payment');\n * });\n *\n * console.log(viz.render());\n * ```\n */\n\nimport type { WorkflowEvent } from \"../core\";\nimport type {\n OutputFormat,\n RenderOptions,\n ScopeEndEvent,\n ScopeStartEvent,\n DecisionStartEvent,\n DecisionBranchEvent,\n DecisionEndEvent,\n VisualizerOptions,\n WorkflowIR,\n} from \"./types\";\nimport { createIRBuilder } from \"./ir-builder\";\nimport { asciiRenderer, mermaidRenderer, defaultColorScheme } from \"./renderers\";\n\n// =============================================================================\n// Re-exports\n// =============================================================================\n\nexport * from \"./types\";\nexport { createIRBuilder, type IRBuilderOptions } from \"./ir-builder\";\nexport { asciiRenderer, mermaidRenderer, defaultColorScheme } from \"./renderers\";\nexport { detectParallelGroups, createParallelDetector, type ParallelDetectorOptions } from \"./parallel-detector\";\nexport { createLiveVisualizer, type LiveVisualizer } from \"./live-visualizer\";\nexport { trackDecision, trackIf, trackSwitch, type DecisionTracker, type IfTracker, type SwitchTracker } from \"./decision-tracker\";\n\n// =============================================================================\n// Visualizer Interface\n// =============================================================================\n\n/**\n * Workflow visualizer that processes events and renders output.\n */\nexport interface WorkflowVisualizer {\n /** Process a workflow event */\n handleEvent: (event: WorkflowEvent<unknown>) => void;\n\n /** Process a scope event (parallel/race) */\n handleScopeEvent: (event: ScopeStartEvent | ScopeEndEvent) => void;\n\n /** Process a decision event (conditional branches) */\n handleDecisionEvent: (event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent) => void;\n\n /** Get current IR state */\n getIR: () => WorkflowIR;\n\n /** Render current state using the default renderer */\n render: () => string;\n\n /** Render to a specific format */\n renderAs: (format: OutputFormat) => string;\n\n /** Reset state for a new workflow */\n reset: () => void;\n\n /** Subscribe to IR updates (for live visualization) */\n onUpdate: (callback: (ir: WorkflowIR) => void) => () => void;\n}\n\n// =============================================================================\n// Create Visualizer\n// =============================================================================\n\n/**\n * Create a workflow visualizer.\n *\n * @example\n * ```typescript\n * const viz = createVisualizer({ workflowName: 'my-workflow' });\n *\n * const workflow = createWorkflow(deps, {\n * onEvent: viz.handleEvent,\n * });\n *\n * await workflow(async (step) => { ... });\n *\n * console.log(viz.render());\n * ```\n */\nexport function createVisualizer(\n options: VisualizerOptions = {}\n): WorkflowVisualizer {\n const {\n workflowName,\n detectParallel = true,\n showTimings = true,\n showKeys = false,\n colors: customColors,\n } = options;\n\n const builder = createIRBuilder({ detectParallel });\n const updateCallbacks: Set<(ir: WorkflowIR) => void> = new Set();\n\n // Renderers\n const ascii = asciiRenderer();\n const mermaid = mermaidRenderer();\n\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 = builder.getIR();\n for (const callback of updateCallbacks) {\n callback(ir);\n }\n }\n }\n\n function handleEvent(event: WorkflowEvent<unknown>): void {\n // Route scope events to handleScopeEvent for proper IR building\n if (event.type === \"scope_start\" || event.type === \"scope_end\") {\n handleScopeEvent(event as ScopeStartEvent | ScopeEndEvent);\n return;\n }\n\n builder.handleEvent(event);\n\n // Set workflow name if provided\n if (event.type === \"workflow_start\" && workflowName) {\n // Note: We'd need to extend the builder to support setting name\n // For now, the name is passed in render options\n }\n\n notifyUpdate();\n }\n\n function handleScopeEvent(event: ScopeStartEvent | ScopeEndEvent): void {\n builder.handleScopeEvent(event);\n notifyUpdate();\n }\n\n function handleDecisionEvent(\n event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent\n ): void {\n builder.handleDecisionEvent(event);\n notifyUpdate();\n }\n\n function getIR(): WorkflowIR {\n const ir = builder.getIR();\n // Apply workflow name if provided\n if (workflowName && !ir.root.name) {\n ir.root.name = workflowName;\n }\n return ir;\n }\n\n function render(): string {\n const ir = getIR();\n return ascii.render(ir, renderOptions);\n }\n\n function renderAs(format: OutputFormat): string {\n const ir = getIR();\n\n switch (format) {\n case \"ascii\":\n return ascii.render(ir, renderOptions);\n\n case \"mermaid\":\n return mermaid.render(ir, renderOptions);\n\n case \"json\":\n return JSON.stringify(ir, null, 2);\n\n default:\n throw new Error(`Unknown format: ${format}`);\n }\n }\n\n function reset(): void {\n builder.reset();\n notifyUpdate();\n }\n\n function onUpdate(callback: (ir: WorkflowIR) => void): () => void {\n updateCallbacks.add(callback);\n return () => updateCallbacks.delete(callback);\n }\n\n return {\n handleEvent,\n handleScopeEvent,\n handleDecisionEvent,\n getIR,\n render,\n renderAs,\n reset,\n onUpdate,\n };\n}\n\n// =============================================================================\n// Convenience Functions\n// =============================================================================\n\n/**\n * Union type for all collectable/visualizable events (workflow + decision).\n */\nexport type CollectableEvent =\n | WorkflowEvent<unknown>\n | DecisionStartEvent\n | DecisionBranchEvent\n | DecisionEndEvent;\n\n/**\n * Visualize collected events (post-execution).\n *\n * Supports both workflow events (from onEvent) and decision events\n * (from trackDecision/trackIf/trackSwitch).\n *\n * @example\n * ```typescript\n * const events: CollectableEvent[] = [];\n * const workflow = createWorkflow(deps, {\n * onEvent: (e) => events.push(e),\n * });\n *\n * await workflow(async (step) => {\n * const decision = trackIf('check', condition, {\n * emit: (e) => events.push(e),\n * });\n * // ...\n * });\n *\n * console.log(visualizeEvents(events));\n * ```\n */\nexport function visualizeEvents(\n events: CollectableEvent[],\n options: VisualizerOptions = {}\n): string {\n const viz = createVisualizer(options);\n\n for (const event of events) {\n if (event.type.startsWith(\"decision_\")) {\n viz.handleDecisionEvent(event as DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent);\n } else {\n viz.handleEvent(event as WorkflowEvent<unknown>);\n }\n }\n\n return viz.render();\n}\n\n/**\n * Create an event collector for later visualization.\n *\n * Supports both workflow events (from onEvent) and decision events\n * (from trackDecision/trackIf/trackSwitch).\n *\n * @example\n * ```typescript\n * const collector = createEventCollector();\n *\n * const workflow = createWorkflow(deps, {\n * onEvent: collector.handleEvent,\n * });\n *\n * await workflow(async (step) => {\n * // Decision events can also be collected\n * const decision = trackIf('check', condition, {\n * emit: collector.handleDecisionEvent,\n * });\n * // ...\n * });\n *\n * console.log(collector.visualize());\n * ```\n */\nexport function createEventCollector(options: VisualizerOptions = {}) {\n const events: CollectableEvent[] = [];\n\n return {\n /** Handle a workflow event */\n handleEvent: (event: WorkflowEvent<unknown>) => {\n events.push(event);\n },\n\n /** Handle a decision event */\n handleDecisionEvent: (event: DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent) => {\n events.push(event);\n },\n\n /** Get all collected events */\n getEvents: () => [...events],\n\n /** Get workflow events only */\n getWorkflowEvents: () => events.filter((e): e is WorkflowEvent<unknown> =>\n !e.type.startsWith(\"decision_\")\n ),\n\n /** Get decision events only */\n getDecisionEvents: () => events.filter((e): e is DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent =>\n e.type.startsWith(\"decision_\")\n ),\n\n /** Clear collected events */\n clear: () => {\n events.length = 0;\n },\n\n /** Visualize collected events */\n visualize: () => {\n const viz = createVisualizer(options);\n for (const event of events) {\n if (event.type.startsWith(\"decision_\")) {\n viz.handleDecisionEvent(event as DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent);\n } else {\n viz.handleEvent(event as WorkflowEvent<unknown>);\n }\n }\n return viz.render();\n },\n\n /** Visualize in a specific format */\n visualizeAs: (format: OutputFormat) => {\n const viz = createVisualizer(options);\n for (const event of events) {\n if (event.type.startsWith(\"decision_\")) {\n viz.handleDecisionEvent(event as DecisionStartEvent | DecisionBranchEvent | DecisionEndEvent);\n } else {\n viz.handleEvent(event as WorkflowEvent<unknown>);\n }\n }\n return viz.renderAs(format);\n },\n };\n}\n\n","/**\n * @jagreehal/workflow/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 \"./core\";\nimport type {\n OutputFormat,\n VisualizerOptions,\n DecisionStartEvent,\n DecisionBranchEvent,\n DecisionEndEvent,\n CollectableEvent,\n} from \"./visualize\";\nimport { createVisualizer } from \"./visualize\";\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(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 = \"\";\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}\n","/**\n * @jagreehal/workflow/hitl\n *\n * Human-in-the-Loop Orchestration Helpers.\n * Provides pollers, webhook handlers, and resume injectors\n * for production-ready approval workflows.\n */\n\nimport type { Result, WorkflowEvent } from \"./core\";\nimport type { ResumeState, Workflow } from \"./workflow\";\nimport { createHITLCollector, isPendingApproval, injectApproval } from \"./workflow\";\n\n// =============================================================================\n// Workflow Factory Types\n// =============================================================================\n\n/**\n * Options passed to the workflow factory by the HITL orchestrator.\n */\nexport interface HITLWorkflowFactoryOptions {\n /** Resume state for replaying completed steps */\n resumeState?: ResumeState;\n /** Event handler for tracking workflow events (required for HITL) */\n onEvent: (event: WorkflowEvent<unknown>) => void;\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Approval status returned from the approval store.\n */\nexport type ApprovalStatus<T = unknown> =\n | { status: \"pending\" }\n | { status: \"approved\"; value: T; approvedBy?: string; approvedAt?: number }\n | { status: \"rejected\"; reason: string; rejectedBy?: string; rejectedAt?: number }\n | { status: \"expired\"; expiredAt: number };\n\n/**\n * Interface for approval storage backends.\n */\nexport interface ApprovalStore {\n /**\n * Get the status of an approval.\n */\n getApproval(key: string): Promise<ApprovalStatus>;\n\n /**\n * Create or update a pending approval request.\n */\n createApproval(\n key: string,\n options?: {\n metadata?: Record<string, unknown>;\n expiresAt?: number;\n requestedBy?: string;\n }\n ): Promise<void>;\n\n /**\n * Grant an approval.\n */\n grantApproval<T>(\n key: string,\n value: T,\n options?: { approvedBy?: string }\n ): Promise<void>;\n\n /**\n * Reject an approval.\n */\n rejectApproval(\n key: string,\n reason: string,\n options?: { rejectedBy?: string }\n ): Promise<void>;\n\n /**\n * Cancel a pending approval.\n */\n cancelApproval(key: string): Promise<void>;\n\n /**\n * List all pending approvals.\n */\n listPending(options?: { prefix?: string }): Promise<string[]>;\n}\n\n/**\n * Saved workflow state for resumption.\n */\nexport interface SavedWorkflowState {\n /** Unique identifier for this workflow run */\n runId: string;\n /** Workflow name/type */\n workflowName: string;\n /** Resume state with step results */\n resumeState: ResumeState;\n /** Pending approval keys */\n pendingApprovals: string[];\n /** Input that was passed to the workflow */\n input?: unknown;\n /** Custom metadata */\n metadata?: Record<string, unknown>;\n /** When the workflow was started */\n startedAt: number;\n /** When the state was last updated */\n updatedAt: number;\n}\n\n/**\n * Interface for workflow state storage.\n */\nexport interface WorkflowStateStore {\n /**\n * Save workflow state.\n */\n save(state: SavedWorkflowState): Promise<void>;\n\n /**\n * Load workflow state by run ID.\n */\n load(runId: string): Promise<SavedWorkflowState | undefined>;\n\n /**\n * Delete workflow state.\n */\n delete(runId: string): Promise<void>;\n\n /**\n * List all saved workflow states.\n */\n list(options?: { workflowName?: string; hasPendingApprovals?: boolean }): Promise<string[]>;\n\n /**\n * Find workflows waiting for a specific approval.\n */\n findByPendingApproval(approvalKey: string): Promise<string[]>;\n}\n\n/**\n * Options for the HITL orchestrator.\n */\nexport interface HITLOrchestratorOptions {\n /** Approval store for managing approval states */\n approvalStore: ApprovalStore;\n /** Workflow state store for persisting workflow state */\n workflowStateStore: WorkflowStateStore;\n /** Default expiration time for approvals (in milliseconds) */\n defaultExpirationMs?: number;\n /** Logger function */\n logger?: (message: string) => void;\n}\n\n/**\n * Result of executing a workflow that may pause for approval.\n * Uses unknown for error type since workflows add UnexpectedError to the union.\n */\nexport type HITLExecutionResult<T, E> =\n | { status: \"completed\"; result: Result<T, E | unknown> }\n | { status: \"paused\"; runId: string; pendingApprovals: string[]; reason?: string }\n | { status: \"resumed\"; runId: string; result: Result<T, E | unknown> };\n\n/**\n * Poller configuration.\n */\nexport interface PollerOptions {\n /** Polling interval in milliseconds */\n intervalMs: number;\n /** Maximum number of polls (undefined = unlimited) */\n maxPolls?: number;\n /** Timeout for the entire polling operation */\n timeoutMs?: number;\n /** Callback when polling starts */\n onPollStart?: () => void;\n /** Callback when a poll completes */\n onPollComplete?: (result: ApprovalStatus) => void;\n}\n\n// =============================================================================\n// In-Memory Stores (for development/testing)\n// =============================================================================\n\n/**\n * Create an in-memory approval store for development/testing.\n */\nexport function createMemoryApprovalStore(): ApprovalStore {\n const approvals = new Map<string, ApprovalStatus & { metadata?: Record<string, unknown>; expiresAt?: number }>();\n\n return {\n async getApproval(key: string): Promise<ApprovalStatus> {\n const approval = approvals.get(key);\n if (!approval) {\n return { status: \"pending\" };\n }\n\n // Check expiration\n if (approval.expiresAt && Date.now() > approval.expiresAt) {\n return { status: \"expired\", expiredAt: approval.expiresAt };\n }\n\n return approval;\n },\n\n async createApproval(\n key: string,\n options?: { metadata?: Record<string, unknown>; expiresAt?: number }\n ): Promise<void> {\n approvals.set(key, {\n status: \"pending\",\n metadata: options?.metadata,\n expiresAt: options?.expiresAt,\n });\n },\n\n async grantApproval<T>(\n key: string,\n value: T,\n options?: { approvedBy?: string }\n ): Promise<void> {\n approvals.set(key, {\n status: \"approved\",\n value,\n approvedBy: options?.approvedBy,\n approvedAt: Date.now(),\n });\n },\n\n async rejectApproval(\n key: string,\n reason: string,\n options?: { rejectedBy?: string }\n ): Promise<void> {\n approvals.set(key, {\n status: \"rejected\",\n reason,\n rejectedBy: options?.rejectedBy,\n rejectedAt: Date.now(),\n });\n },\n\n async cancelApproval(key: string): Promise<void> {\n approvals.delete(key);\n },\n\n async listPending(options?: { prefix?: string }): Promise<string[]> {\n const pending: string[] = [];\n for (const [key, approval] of approvals) {\n if (approval.status === \"pending\") {\n if (!options?.prefix || key.startsWith(options.prefix)) {\n pending.push(key);\n }\n }\n }\n return pending;\n },\n };\n}\n\n/**\n * Create an in-memory workflow state store for development/testing.\n */\nexport function createMemoryWorkflowStateStore(): WorkflowStateStore {\n const states = new Map<string, SavedWorkflowState>();\n\n return {\n async save(state: SavedWorkflowState): Promise<void> {\n states.set(state.runId, { ...state, updatedAt: Date.now() });\n },\n\n async load(runId: string): Promise<SavedWorkflowState | undefined> {\n return states.get(runId);\n },\n\n async delete(runId: string): Promise<void> {\n states.delete(runId);\n },\n\n async list(options?: { workflowName?: string; hasPendingApprovals?: boolean }): Promise<string[]> {\n const results: string[] = [];\n for (const [runId, state] of states) {\n if (options?.workflowName && state.workflowName !== options.workflowName) {\n continue;\n }\n if (options?.hasPendingApprovals !== undefined) {\n const hasPending = state.pendingApprovals.length > 0;\n if (options.hasPendingApprovals !== hasPending) {\n continue;\n }\n }\n results.push(runId);\n }\n return results;\n },\n\n async findByPendingApproval(approvalKey: string): Promise<string[]> {\n const results: string[] = [];\n for (const [runId, state] of states) {\n if (state.pendingApprovals.includes(approvalKey)) {\n results.push(runId);\n }\n }\n return results;\n },\n };\n}\n\n// =============================================================================\n// HITL Orchestrator\n// =============================================================================\n\n/**\n * HITL orchestrator interface.\n */\nexport interface HITLOrchestrator {\n /**\n * Execute a workflow that may pause for approvals.\n * If the workflow pauses, state is automatically saved.\n * \n * The workflowFactory receives options including onEvent handler which MUST be\n * passed to createWorkflow for HITL tracking to work.\n */\n execute<T, E, TInput>(\n workflowName: string,\n workflowFactory: (options: HITLWorkflowFactoryOptions) => Workflow<E, unknown>,\n workflowFn: (step: unknown, deps: unknown, input: TInput) => Promise<T>,\n input: TInput,\n options?: { runId?: string; metadata?: Record<string, unknown> }\n ): Promise<HITLExecutionResult<T, E>>;\n\n /**\n * Resume a paused workflow after approvals have been granted.\n */\n resume<T, E, TInput>(\n runId: string,\n workflowFactory: (options: HITLWorkflowFactoryOptions) => Workflow<E, unknown>,\n workflowFn: (step: unknown, deps: unknown, input: TInput) => Promise<T>\n ): Promise<HITLExecutionResult<T, E>>;\n\n /**\n * Grant an approval and automatically resume any waiting workflows.\n */\n grantApproval<T>(\n approvalKey: string,\n value: T,\n options?: { approvedBy?: string; autoResume?: boolean }\n ): Promise<{ grantedAt: number; resumedWorkflows: string[] }>;\n\n /**\n * Reject an approval.\n */\n rejectApproval(\n approvalKey: string,\n reason: string,\n options?: { rejectedBy?: string }\n ): Promise<void>;\n\n /**\n * Poll for an approval to be granted.\n */\n pollApproval<T>(\n approvalKey: string,\n options?: PollerOptions\n ): Promise<ApprovalStatus<T>>;\n\n /**\n * Get the status of a workflow run.\n */\n getWorkflowStatus(runId: string): Promise<SavedWorkflowState | undefined>;\n\n /**\n * List all pending workflows.\n */\n listPendingWorkflows(workflowName?: string): Promise<string[]>;\n\n /**\n * Clean up completed workflows older than the specified age.\n */\n cleanup(maxAgeMs: number): Promise<number>;\n}\n\n/**\n * Create a HITL orchestrator for managing approval workflows.\n *\n * @example\n * ```typescript\n * const orchestrator = createHITLOrchestrator({\n * approvalStore: createMemoryApprovalStore(),\n * workflowStateStore: createMemoryWorkflowStateStore(),\n * });\n *\n * // Execute workflow - IMPORTANT: pass onEvent to createWorkflow!\n * const result = await orchestrator.execute(\n * 'order-approval',\n * ({ resumeState, onEvent }) => createWorkflow(deps, { resumeState, onEvent }),\n * async (step, deps, input) => {\n * const order = await step(() => deps.createOrder(input));\n * const approval = await step(() => deps.requireApproval(order.id), { key: `approval:${order.id}` });\n * await step(() => deps.processOrder(order.id));\n * return { orderId: order.id, approvedBy: approval.approvedBy };\n * },\n * { items: [...], total: 100 }\n * );\n *\n * if (result.status === 'paused') {\n * console.log(`Workflow paused, waiting for: ${result.pendingApprovals}`);\n * }\n *\n * // Later, grant approval\n * await orchestrator.grantApproval(\n * `approval:${orderId}`,\n * { approvedBy: 'manager@example.com' },\n * { autoResume: true }\n * );\n * ```\n */\nexport function createHITLOrchestrator(options: HITLOrchestratorOptions): HITLOrchestrator {\n const {\n approvalStore,\n workflowStateStore,\n defaultExpirationMs = 7 * 24 * 60 * 60 * 1000, // 7 days\n logger = () => {},\n } = options;\n\n async function execute<T, E, TInput>(\n workflowName: string,\n workflowFactory: (options: HITLWorkflowFactoryOptions) => Workflow<E, unknown>,\n workflowFn: (step: unknown, deps: unknown, input: TInput) => Promise<T>,\n input: TInput,\n opts?: { runId?: string; metadata?: Record<string, unknown> }\n ): Promise<HITLExecutionResult<T, E>> {\n const runId = opts?.runId ?? crypto.randomUUID();\n const collector = createHITLCollector();\n\n // Create workflow with collector's event handler wired in\n const workflow = workflowFactory({\n onEvent: collector.handleEvent,\n });\n\n // Execute workflow - collector tracks pending approvals via events\n const result = await (workflow as Workflow<E, unknown>)(\n input,\n workflowFn as (step: unknown, deps: unknown, args: TInput) => Promise<T>\n );\n\n // Check for pending approvals\n const pendingApprovals = collector.getPendingApprovals().map((p) => p.stepKey);\n\n if (pendingApprovals.length > 0) {\n // Save state for later resumption\n const state: SavedWorkflowState = {\n runId,\n workflowName,\n resumeState: collector.getState(),\n pendingApprovals,\n input,\n metadata: opts?.metadata,\n startedAt: Date.now(),\n updatedAt: Date.now(),\n };\n\n await workflowStateStore.save(state);\n\n // Create approval requests in the store\n for (const key of pendingApprovals) {\n await approvalStore.createApproval(key, {\n metadata: { runId, workflowName },\n expiresAt: Date.now() + defaultExpirationMs,\n });\n }\n\n logger(`Workflow ${runId} paused, waiting for: ${pendingApprovals.join(\", \")}`);\n\n // Find the reason from the error if available\n let reason: string | undefined;\n if (!result.ok && isPendingApproval(result.error)) {\n reason = result.error.reason;\n }\n\n return {\n status: \"paused\",\n runId,\n pendingApprovals,\n reason,\n };\n }\n\n // Workflow completed\n logger(`Workflow ${runId} completed`);\n return { status: \"completed\", result };\n }\n\n async function resume<T, E, TInput>(\n runId: string,\n workflowFactory: (options: HITLWorkflowFactoryOptions) => Workflow<E, unknown>,\n workflowFn: (step: unknown, deps: unknown, input: TInput) => Promise<T>\n ): Promise<HITLExecutionResult<T, E>> {\n const savedState = await workflowStateStore.load(runId);\n if (!savedState) {\n throw new Error(`Workflow run not found: ${runId}`);\n }\n\n // Check if all approvals have been granted\n for (const key of savedState.pendingApprovals) {\n const status = await approvalStore.getApproval(key);\n if (status.status === \"pending\") {\n return {\n status: \"paused\",\n runId,\n pendingApprovals: savedState.pendingApprovals,\n };\n }\n if (status.status === \"rejected\") {\n // Inject rejection as error\n // This would need custom handling\n }\n if (status.status === \"expired\") {\n throw new Error(`Approval ${key} has expired`);\n }\n }\n\n // Inject approved values into resume state\n let resumeState = savedState.resumeState;\n for (const key of savedState.pendingApprovals) {\n const status = await approvalStore.getApproval(key);\n if (status.status === \"approved\") {\n resumeState = injectApproval(resumeState, {\n stepKey: key,\n value: status.value,\n });\n }\n }\n\n // Create collector for tracking any new pending approvals\n const collector = createHITLCollector();\n\n // Create workflow with resume state and event handler\n const workflow = workflowFactory({\n resumeState,\n onEvent: collector.handleEvent,\n });\n\n // Execute workflow\n const result = await (workflow as Workflow<E, unknown>)(\n savedState.input as TInput,\n workflowFn as (step: unknown, deps: unknown, args: TInput) => Promise<T>\n );\n\n // Check for NEW pending approvals (workflow paused again at a different step)\n const newPendingApprovals = collector.getPendingApprovals().map((p) => p.stepKey);\n\n if (newPendingApprovals.length > 0) {\n // Update saved state with new pending approvals\n const updatedState: SavedWorkflowState = {\n ...savedState,\n resumeState: collector.getState(),\n pendingApprovals: newPendingApprovals,\n updatedAt: Date.now(),\n };\n\n await workflowStateStore.save(updatedState);\n\n // Create approval requests for new approvals\n for (const key of newPendingApprovals) {\n await approvalStore.createApproval(key, {\n metadata: { runId, workflowName: savedState.workflowName },\n expiresAt: Date.now() + defaultExpirationMs,\n });\n }\n\n logger(`Workflow ${runId} paused again, waiting for: ${newPendingApprovals.join(\", \")}`);\n\n // Find the reason from the error if available\n let reason: string | undefined;\n if (!result.ok && isPendingApproval(result.error)) {\n reason = result.error.reason;\n }\n\n return {\n status: \"paused\",\n runId,\n pendingApprovals: newPendingApprovals,\n reason,\n };\n }\n\n // Workflow completed - clean up saved state\n await workflowStateStore.delete(runId);\n\n logger(`Workflow ${runId} resumed and completed`);\n return { status: \"resumed\", runId, result };\n }\n\n async function grantApprovalFn<T>(\n approvalKey: string,\n value: T,\n opts?: { approvedBy?: string; autoResume?: boolean }\n ): Promise<{ grantedAt: number; resumedWorkflows: string[] }> {\n await approvalStore.grantApproval(approvalKey, value, {\n approvedBy: opts?.approvedBy,\n });\n\n const grantedAt = Date.now();\n const resumedWorkflows: string[] = [];\n\n if (opts?.autoResume !== false) {\n // Find workflows waiting for this approval\n const waitingWorkflows = await workflowStateStore.findByPendingApproval(approvalKey);\n\n for (const runId of waitingWorkflows) {\n // Note: Auto-resume would need the workflow factory, which we don't have here\n // This is a simplified implementation\n resumedWorkflows.push(runId);\n }\n }\n\n logger(`Approval ${approvalKey} granted by ${opts?.approvedBy ?? \"unknown\"}`);\n return { grantedAt, resumedWorkflows };\n }\n\n async function rejectApprovalFn(\n approvalKey: string,\n reason: string,\n opts?: { rejectedBy?: string }\n ): Promise<void> {\n await approvalStore.rejectApproval(approvalKey, reason, {\n rejectedBy: opts?.rejectedBy,\n });\n logger(`Approval ${approvalKey} rejected: ${reason}`);\n }\n\n async function pollApproval<T>(\n approvalKey: string,\n opts?: PollerOptions\n ): Promise<ApprovalStatus<T>> {\n const {\n intervalMs = 1000,\n maxPolls,\n timeoutMs,\n onPollStart,\n onPollComplete,\n } = opts ?? {};\n\n const startTime = Date.now();\n let pollCount = 0;\n\n while (true) {\n onPollStart?.();\n const status = (await approvalStore.getApproval(approvalKey)) as ApprovalStatus<T>;\n onPollComplete?.(status);\n\n if (status.status !== \"pending\") {\n return status;\n }\n\n pollCount++;\n if (maxPolls !== undefined && pollCount >= maxPolls) {\n return { status: \"pending\" };\n }\n\n if (timeoutMs !== undefined && Date.now() - startTime >= timeoutMs) {\n return { status: \"pending\" };\n }\n\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n }\n\n async function getWorkflowStatus(runId: string): Promise<SavedWorkflowState | undefined> {\n return workflowStateStore.load(runId);\n }\n\n async function listPendingWorkflows(workflowName?: string): Promise<string[]> {\n return workflowStateStore.list({\n workflowName,\n hasPendingApprovals: true,\n });\n }\n\n async function cleanup(maxAgeMs: number): Promise<number> {\n const allWorkflows = await workflowStateStore.list();\n let cleaned = 0;\n const cutoff = Date.now() - maxAgeMs;\n\n for (const runId of allWorkflows) {\n const state = await workflowStateStore.load(runId);\n if (state && state.updatedAt < cutoff && state.pendingApprovals.length === 0) {\n await workflowStateStore.delete(runId);\n cleaned++;\n }\n }\n\n logger(`Cleaned up ${cleaned} old workflow states`);\n return cleaned;\n }\n\n return {\n execute,\n resume,\n grantApproval: grantApprovalFn,\n rejectApproval: rejectApprovalFn,\n pollApproval,\n getWorkflowStatus,\n listPendingWorkflows,\n cleanup,\n };\n}\n\n// =============================================================================\n// Webhook Handlers for Approvals\n// =============================================================================\n\n/**\n * Approval webhook request body.\n */\nexport interface ApprovalWebhookRequest {\n /** Approval key */\n key: string;\n /** Action: approve, reject, or cancel */\n action: \"approve\" | \"reject\" | \"cancel\";\n /** Value to inject (for approve) */\n value?: unknown;\n /** Reason (for reject) */\n reason?: string;\n /** Who performed this action */\n actorId?: string;\n}\n\n/**\n * Approval webhook response.\n */\nexport interface ApprovalWebhookResponse {\n success: boolean;\n message: string;\n data?: {\n key: string;\n action: string;\n timestamp: number;\n };\n}\n\n/**\n * Create a webhook handler for approval actions.\n *\n * @example\n * ```typescript\n * const handleApproval = createApprovalWebhookHandler(approvalStore);\n *\n * // Express\n * app.post('/api/approvals', async (req, res) => {\n * const result = await handleApproval(req.body);\n * res.json(result);\n * });\n * ```\n */\nexport function createApprovalWebhookHandler(\n store: ApprovalStore\n): (request: ApprovalWebhookRequest) => Promise<ApprovalWebhookResponse> {\n return async (request: ApprovalWebhookRequest): Promise<ApprovalWebhookResponse> => {\n const { key, action, value, reason, actorId } = request;\n\n try {\n switch (action) {\n case \"approve\":\n await store.grantApproval(key, value, { approvedBy: actorId });\n return {\n success: true,\n message: `Approval ${key} granted`,\n data: { key, action, timestamp: Date.now() },\n };\n\n case \"reject\":\n if (!reason) {\n return { success: false, message: \"Reason is required for rejection\" };\n }\n await store.rejectApproval(key, reason, { rejectedBy: actorId });\n return {\n success: true,\n message: `Approval ${key} rejected`,\n data: { key, action, timestamp: Date.now() },\n };\n\n case \"cancel\":\n await store.cancelApproval(key);\n return {\n success: true,\n message: `Approval ${key} cancelled`,\n data: { key, action, timestamp: Date.now() },\n };\n\n default:\n return { success: false, message: `Unknown action: ${action}` };\n }\n } catch (error) {\n return {\n success: false,\n message: error instanceof Error ? error.message : String(error),\n };\n }\n };\n}\n\n// =============================================================================\n// Approval Status Checker\n// =============================================================================\n\n/**\n * Create an approval checker function for use in approval steps.\n * This wraps the approval store with the standard checkApproval interface.\n *\n * @example\n * ```typescript\n * const checkApproval = createApprovalChecker(approvalStore);\n *\n * const requireManagerApproval = createApprovalStep<{ approvedBy: string }>({\n * key: 'manager-approval',\n * checkApproval: checkApproval('manager-approval'),\n * pendingReason: 'Waiting for manager approval',\n * });\n * ```\n */\nexport function createApprovalChecker<T>(store: ApprovalStore) {\n return (key: string) => async (): Promise<\n | { status: \"pending\" }\n | { status: \"approved\"; value: T }\n | { status: \"rejected\"; reason: string }\n > => {\n const status = await store.getApproval(key);\n\n switch (status.status) {\n case \"pending\":\n return { status: \"pending\" };\n case \"approved\":\n return { status: \"approved\", value: status.value as T };\n case \"rejected\":\n return { status: \"rejected\", reason: status.reason };\n case \"expired\":\n return { status: \"rejected\", reason: \"Approval request expired\" };\n default:\n return { status: \"pending\" };\n }\n };\n}\n","/**\n * @jagreehal/workflow/testing\n *\n * Deterministic Workflow Testing Harness.\n * Provides tools for scripting step outcomes and asserting workflow behavior.\n */\n\nimport type { Result, AsyncResult, StepOptions, WorkflowEvent } from \"./core\";\nimport { ok, err } from \"./core\";\nimport type { AnyResultFn, ErrorsOfDeps } from \"./workflow\";\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\n/**\n * Internal early exit marker used by the testing harness.\n * @internal\n */\ninterface TestEarlyExit<E = unknown> {\n __earlyExit: true;\n error: E;\n}\n\n/**\n * Type guard for test early exit objects.\n * @internal\n */\nfunction isTestEarlyExit(e: unknown): e is TestEarlyExit {\n return (\n typeof e === \"object\" &&\n e !== null &&\n \"__earlyExit\" in e &&\n (e as TestEarlyExit).__earlyExit === true\n );\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * A scripted outcome for a step.\n */\nexport type ScriptedOutcome<T = unknown, E = unknown> =\n | { type: \"ok\"; value: T }\n | { type: \"err\"; error: E }\n | { type: \"throw\"; error: unknown };\n\n/**\n * Step invocation record.\n */\nexport interface StepInvocation {\n /** Step name */\n name?: string;\n /** Step key */\n key?: string;\n /** Invocation order (0-indexed) */\n order: number;\n /** Timestamp when step was invoked */\n timestamp: number;\n /** Duration in milliseconds */\n durationMs?: number;\n /** Result of the step */\n result?: Result<unknown, unknown>;\n /** Whether the step was from cache */\n cached?: boolean;\n}\n\n/**\n * Assertion result.\n */\nexport interface AssertionResult {\n passed: boolean;\n message: string;\n expected?: unknown;\n actual?: unknown;\n}\n\n/**\n * Test harness options.\n */\nexport interface TestHarnessOptions {\n /** Whether to record step invocations */\n recordInvocations?: boolean;\n /** Custom clock for deterministic timing */\n clock?: () => number;\n}\n\n/**\n * Mock step function that returns scripted outcomes.\n */\nexport type MockStep<E> = {\n /** Execute with a Result-returning operation */\n <T, StepE extends E>(\n operation: () => Result<T, StepE> | AsyncResult<T, StepE>,\n options?: StepOptions | string\n ): Promise<T>;\n\n /** Execute with a direct Result */\n <T, StepE extends E>(\n result: Result<T, StepE> | AsyncResult<T, StepE>,\n options?: StepOptions | string\n ): Promise<T>;\n\n /** step.try for catching throws */\n try: <T, Err extends E>(\n operation: () => T | Promise<T>,\n options: { error: Err; name?: string; key?: string } | { onError: (cause: unknown) => Err; name?: string; key?: string }\n ) => Promise<T>;\n};\n\n// =============================================================================\n// Test Harness\n// =============================================================================\n\n/**\n * Workflow test harness interface.\n */\nexport interface WorkflowHarness<E, Deps> {\n /**\n * Script step outcomes in order.\n * Each outcome will be returned for the corresponding step invocation.\n */\n script(outcomes: ScriptedOutcome[]): void;\n\n /**\n * Script a specific step outcome by name or key.\n */\n scriptStep(nameOrKey: string, outcome: ScriptedOutcome): void;\n\n /**\n * Run the workflow with scripted outcomes.\n */\n run<T>(\n fn: (step: MockStep<E>, deps: Deps) => Promise<T>\n ): Promise<Result<T, E | unknown>>;\n\n /**\n * Run the workflow with input.\n */\n runWithInput<T, TInput>(\n input: TInput,\n fn: (step: MockStep<E>, deps: Deps, input: TInput) => Promise<T>\n ): Promise<Result<T, E | unknown>>;\n\n /**\n * Get recorded step invocations.\n */\n getInvocations(): StepInvocation[];\n\n /**\n * Assert that steps were invoked in order.\n */\n assertSteps(expectedNames: string[]): AssertionResult;\n\n /**\n * Assert that a step was invoked with specific options.\n */\n assertStepCalled(nameOrKey: string): AssertionResult;\n\n /**\n * Assert that a step was NOT invoked.\n */\n assertStepNotCalled(nameOrKey: string): AssertionResult;\n\n /**\n * Assert the workflow result.\n */\n assertResult<T>(result: Result<T, unknown>, expected: Result<T, unknown>): AssertionResult;\n\n /**\n * Clear all state for a new test.\n */\n reset(): void;\n}\n\n/**\n * Create a test harness for a workflow.\n *\n * @example\n * ```typescript\n * const harness = createWorkflowHarness({ fetchUser, chargeCard });\n *\n * // Script step outcomes\n * harness.script([\n * { type: 'ok', value: { id: '1', name: 'Alice' } },\n * { type: 'ok', value: { transactionId: 'tx_123' } },\n * ]);\n *\n * // Run the workflow\n * const result = await harness.run(async (step, { fetchUser, chargeCard }) => {\n * const user = await step(() => fetchUser('1'), 'fetch-user');\n * const charge = await step(() => chargeCard(100), 'charge-card');\n * return { user, charge };\n * });\n *\n * // Assert\n * expect(result.ok).toBe(true);\n * harness.assertSteps(['fetch-user', 'charge-card']);\n * ```\n */\nexport function createWorkflowHarness<\n Deps extends Record<string, AnyResultFn>\n>(\n deps: Deps,\n options: TestHarnessOptions = {}\n): WorkflowHarness<ErrorsOfDeps<Deps>, Deps> {\n type E = ErrorsOfDeps<Deps>;\n\n const { recordInvocations = true, clock = Date.now } = options;\n\n let scriptedOutcomes: ScriptedOutcome[] = [];\n const namedOutcomes = new Map<string, ScriptedOutcome>();\n let invocationIndex = 0;\n let invocations: StepInvocation[] = [];\n\n function script(outcomes: ScriptedOutcome[]): void {\n scriptedOutcomes = [...outcomes];\n invocationIndex = 0; // Reset index when script is called\n namedOutcomes.clear(); // Clear named overrides for deterministic behavior\n }\n\n function scriptStep(nameOrKey: string, outcome: ScriptedOutcome): void {\n namedOutcomes.set(nameOrKey, outcome);\n }\n\n function getNextOutcome(nameOrKey?: string): ScriptedOutcome | undefined {\n // Check named outcomes first\n if (nameOrKey && namedOutcomes.has(nameOrKey)) {\n return namedOutcomes.get(nameOrKey);\n }\n\n // Fall back to sequential outcomes\n if (invocationIndex < scriptedOutcomes.length) {\n return scriptedOutcomes[invocationIndex++];\n }\n\n return undefined;\n }\n\n function createMockStep(): MockStep<E> {\n const mockStep = async <T, StepE extends E>(\n operationOrResult:\n | (() => Result<T, StepE> | AsyncResult<T, StepE>)\n | Result<T, StepE>\n | AsyncResult<T, StepE>,\n stepOptions?: StepOptions | string\n ): Promise<T> => {\n const opts = typeof stepOptions === \"string\" ? { name: stepOptions } : (stepOptions ?? {});\n const nameOrKey = opts.name ?? opts.key;\n const startTime = clock();\n\n // Record invocation\n const invocation: StepInvocation = {\n name: opts.name,\n key: opts.key,\n order: invocations.length,\n timestamp: startTime,\n };\n\n if (recordInvocations) {\n invocations.push(invocation);\n }\n\n // Get scripted outcome\n const outcome = getNextOutcome(nameOrKey);\n\n if (outcome) {\n invocation.durationMs = clock() - startTime;\n\n switch (outcome.type) {\n case \"ok\":\n invocation.result = ok(outcome.value);\n return outcome.value as T;\n\n case \"err\":\n invocation.result = err(outcome.error);\n throw { __earlyExit: true, error: outcome.error };\n\n case \"throw\":\n throw outcome.error;\n }\n }\n\n // No scripted outcome - execute the real operation\n const result =\n typeof operationOrResult === \"function\"\n ? await operationOrResult()\n : await operationOrResult;\n\n invocation.durationMs = clock() - startTime;\n invocation.result = result;\n\n if (!result.ok) {\n throw { __earlyExit: true, error: result.error };\n }\n\n return result.value;\n };\n\n mockStep.try = async <T, Err extends E>(\n operation: () => T | Promise<T>,\n opts:\n | { error: Err; name?: string; key?: string }\n | { onError: (cause: unknown) => Err; name?: string; key?: string }\n ): Promise<T> => {\n const nameOrKey = opts.name ?? opts.key;\n const startTime = clock();\n\n const invocation: StepInvocation = {\n name: opts.name,\n key: opts.key,\n order: invocations.length,\n timestamp: startTime,\n };\n\n if (recordInvocations) {\n invocations.push(invocation);\n }\n\n // Get scripted outcome\n const outcome = getNextOutcome(nameOrKey);\n\n if (outcome) {\n invocation.durationMs = clock() - startTime;\n\n switch (outcome.type) {\n case \"ok\":\n invocation.result = ok(outcome.value);\n return outcome.value as T;\n\n case \"err\":\n invocation.result = err(outcome.error);\n throw { __earlyExit: true, error: outcome.error };\n\n case \"throw\":\n throw outcome.error;\n }\n }\n\n // No scripted outcome - execute the real operation\n try {\n const value = await operation();\n invocation.durationMs = clock() - startTime;\n invocation.result = ok(value);\n return value;\n } catch (error) {\n invocation.durationMs = clock() - startTime;\n const mappedError = \"error\" in opts ? opts.error : opts.onError(error);\n invocation.result = err(mappedError);\n throw { __earlyExit: true, error: mappedError };\n }\n };\n\n return mockStep as MockStep<E>;\n }\n\n async function run<T>(\n fn: (step: MockStep<E>, deps: Deps) => Promise<T>\n ): Promise<Result<T, E | unknown>> {\n const mockStep = createMockStep();\n\n try {\n const value = await fn(mockStep, deps);\n return ok(value);\n } catch (error) {\n if (isTestEarlyExit(error)) {\n return err(error.error);\n }\n return err({ type: \"UNEXPECTED_ERROR\", cause: error });\n }\n }\n\n async function runWithInput<T, TInput>(\n input: TInput,\n fn: (step: MockStep<E>, deps: Deps, input: TInput) => Promise<T>\n ): Promise<Result<T, E | unknown>> {\n const mockStep = createMockStep();\n\n try {\n const value = await fn(mockStep, deps, input);\n return ok(value);\n } catch (error) {\n if (isTestEarlyExit(error)) {\n return err(error.error);\n }\n return err({ type: \"UNEXPECTED_ERROR\", cause: error });\n }\n }\n\n function getInvocations(): StepInvocation[] {\n return [...invocations];\n }\n\n function assertSteps(expectedNames: string[]): AssertionResult {\n const actualNames = invocations\n .map((inv) => inv.name ?? inv.key ?? \"unnamed\")\n .filter((n) => n !== \"unnamed\");\n\n const passed = JSON.stringify(actualNames) === JSON.stringify(expectedNames);\n\n return {\n passed,\n message: passed\n ? `Steps invoked in order: ${expectedNames.join(\", \")}`\n : `Expected steps [${expectedNames.join(\", \")}] but got [${actualNames.join(\", \")}]`,\n expected: expectedNames,\n actual: actualNames,\n };\n }\n\n function assertStepCalled(nameOrKey: string): AssertionResult {\n const found = invocations.some(\n (inv) => inv.name === nameOrKey || inv.key === nameOrKey\n );\n\n return {\n passed: found,\n message: found\n ? `Step \"${nameOrKey}\" was invoked`\n : `Step \"${nameOrKey}\" was NOT invoked`,\n expected: nameOrKey,\n actual: found,\n };\n }\n\n function assertStepNotCalled(nameOrKey: string): AssertionResult {\n const found = invocations.some(\n (inv) => inv.name === nameOrKey || inv.key === nameOrKey\n );\n\n return {\n passed: !found,\n message: !found\n ? `Step \"${nameOrKey}\" was correctly NOT invoked`\n : `Step \"${nameOrKey}\" was invoked but should not have been`,\n expected: \"not called\",\n actual: found ? \"called\" : \"not called\",\n };\n }\n\n function assertResult<T>(\n result: Result<T, unknown>,\n expected: Result<T, unknown>\n ): AssertionResult {\n const passed =\n result.ok === expected.ok &&\n (result.ok\n ? JSON.stringify(result.value) === JSON.stringify((expected as { ok: true; value: T }).value)\n : JSON.stringify(result.error) === JSON.stringify((expected as { ok: false; error: unknown }).error));\n\n return {\n passed,\n message: passed\n ? `Result matches expected`\n : `Result does not match expected`,\n expected,\n actual: result,\n };\n }\n\n function reset(): void {\n scriptedOutcomes = [];\n namedOutcomes.clear();\n invocationIndex = 0;\n invocations = [];\n }\n\n return {\n script,\n scriptStep,\n run,\n runWithInput,\n getInvocations,\n assertSteps,\n assertStepCalled,\n assertStepNotCalled,\n assertResult,\n reset,\n };\n}\n\n// =============================================================================\n// Mock Factories\n// =============================================================================\n\n/**\n * Create a mock Result-returning function.\n *\n * @example\n * ```typescript\n * const fetchUser = createMockFn<User, 'NOT_FOUND'>();\n *\n * fetchUser.returns(ok({ id: '1', name: 'Alice' }));\n * // or\n * fetchUser.returnsOnce(ok({ id: '1', name: 'Alice' }));\n * fetchUser.returnsOnce(err('NOT_FOUND'));\n * ```\n */\nexport function createMockFn<T, E>(): MockFunction<T, E> {\n let defaultReturn: Result<T, E> | undefined;\n const returnQueue: Result<T, E>[] = [];\n const calls: unknown[][] = [];\n\n const fn = ((...args: unknown[]) => {\n calls.push(args);\n\n if (returnQueue.length > 0) {\n return Promise.resolve(returnQueue.shift()!);\n }\n\n if (defaultReturn) {\n return Promise.resolve(defaultReturn);\n }\n\n throw new Error(\"Mock function called without configured return value\");\n }) as MockFunction<T, E>;\n\n fn.returns = (result: Result<T, E>) => {\n defaultReturn = result;\n return fn;\n };\n\n fn.returnsOnce = (result: Result<T, E>) => {\n returnQueue.push(result);\n return fn;\n };\n\n fn.getCalls = () => [...calls];\n\n fn.getCallCount = () => calls.length;\n\n fn.reset = () => {\n defaultReturn = undefined;\n returnQueue.length = 0;\n calls.length = 0;\n };\n\n return fn;\n}\n\n/**\n * Mock function interface.\n */\nexport interface MockFunction<T, E> {\n (...args: unknown[]): AsyncResult<T, E>;\n\n /** Set the default return value */\n returns(result: Result<T, E>): MockFunction<T, E>;\n\n /** Queue a return value for the next call */\n returnsOnce(result: Result<T, E>): MockFunction<T, E>;\n\n /** Get all call arguments */\n getCalls(): unknown[][];\n\n /** Get the number of times the function was called */\n getCallCount(): number;\n\n /** Reset the mock */\n reset(): void;\n}\n\n// =============================================================================\n// Snapshot Testing\n// =============================================================================\n\n/**\n * Workflow snapshot for comparison.\n */\nexport interface WorkflowSnapshot {\n /** Step invocations */\n invocations: StepInvocation[];\n /** Final result */\n result: Result<unknown, unknown>;\n /** Events emitted */\n events?: WorkflowEvent<unknown>[];\n /** Total duration */\n durationMs?: number;\n}\n\n/**\n * Create a snapshot of a workflow execution.\n */\nexport function createSnapshot(\n invocations: StepInvocation[],\n result: Result<unknown, unknown>,\n events?: WorkflowEvent<unknown>[]\n): WorkflowSnapshot {\n const totalDuration = invocations.reduce(\n (sum, inv) => sum + (inv.durationMs ?? 0),\n 0\n );\n\n return {\n invocations: invocations.map((inv) => ({\n ...inv,\n // Normalize timestamps for comparison\n timestamp: 0,\n })),\n result,\n events: events?.map((e) => ({\n ...e,\n ts: 0, // Normalize timestamps\n })),\n durationMs: totalDuration,\n };\n}\n\n/**\n * Compare two workflow snapshots.\n */\nexport function compareSnapshots(\n snapshot1: WorkflowSnapshot,\n snapshot2: WorkflowSnapshot\n): {\n equal: boolean;\n differences: string[];\n} {\n const differences: string[] = [];\n\n // Compare invocations count\n if (snapshot1.invocations.length !== snapshot2.invocations.length) {\n differences.push(\n `Invocation count: ${snapshot1.invocations.length} vs ${snapshot2.invocations.length}`\n );\n }\n\n // Compare each invocation\n const maxLen = Math.max(\n snapshot1.invocations.length,\n snapshot2.invocations.length\n );\n\n for (let i = 0; i < maxLen; i++) {\n const inv1 = snapshot1.invocations[i];\n const inv2 = snapshot2.invocations[i];\n\n if (!inv1) {\n differences.push(`Step ${i}: missing in first snapshot`);\n continue;\n }\n\n if (!inv2) {\n differences.push(`Step ${i}: missing in second snapshot`);\n continue;\n }\n\n if (inv1.name !== inv2.name) {\n differences.push(`Step ${i} name: \"${inv1.name}\" vs \"${inv2.name}\"`);\n }\n\n if (inv1.key !== inv2.key) {\n differences.push(`Step ${i} key: \"${inv1.key}\" vs \"${inv2.key}\"`);\n }\n\n // Compare results\n if (inv1.result?.ok !== inv2.result?.ok) {\n differences.push(\n `Step ${i} result: ${inv1.result?.ok ? \"ok\" : \"err\"} vs ${inv2.result?.ok ? \"ok\" : \"err\"}`\n );\n }\n }\n\n // Compare final result\n if (snapshot1.result.ok !== snapshot2.result.ok) {\n differences.push(\n `Final result: ${snapshot1.result.ok ? \"ok\" : \"err\"} vs ${snapshot2.result.ok ? \"ok\" : \"err\"}`\n );\n }\n\n return {\n equal: differences.length === 0,\n differences,\n };\n}\n\n// =============================================================================\n// Test Utilities\n// =============================================================================\n\n/**\n * Create a deterministic clock for testing.\n */\nexport function createTestClock(startTime = 0): {\n now: () => number;\n advance: (ms: number) => void;\n set: (time: number) => void;\n reset: () => void;\n} {\n let currentTime = startTime;\n\n return {\n now: () => currentTime,\n advance: (ms: number) => {\n currentTime += ms;\n },\n set: (time: number) => {\n currentTime = time;\n },\n reset: () => {\n currentTime = startTime;\n },\n };\n}\n\n/**\n * Helper to create ok outcomes.\n */\nexport function okOutcome<T>(value: T): ScriptedOutcome<T, never> {\n return { type: \"ok\", value };\n}\n\n/**\n * Helper to create err outcomes.\n */\nexport function errOutcome<E>(error: E): ScriptedOutcome<never, E> {\n return { type: \"err\", error };\n}\n\n/**\n * Helper to create throw outcomes.\n */\nexport function throwOutcome(error: unknown): ScriptedOutcome<never, never> {\n return { type: \"throw\", error };\n}\n"],"mappings":"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","names":["ok","value","err","error","options","isOk","r","isErr","isUnexpectedError","STEP_TIMEOUT_MARKER","isStepTimeoutError","getStepTimeoutMeta","EARLY_EXIT_SYMBOL","createEarlyExit","meta","isEarlyExit","MAPPER_EXCEPTION_SYMBOL","createMapperException","thrown","isMapperException","parseStepOptions","calculateRetryDelay","attempt","backoff","initialDelay","maxDelay","jitter","delay","jitterAmount","sleep","ms","resolve","TIMEOUT_SYMBOL","executeWithTimeout","operation","stepInfo","controller","timeoutError","timeoutId","timeoutPromise","_","reject","operationPromise","errorToThrow","DEFAULT_RETRY_CONFIG","run","fn","onError","onEvent","catchUnexpected","providedWorkflowId","context","workflowId","wrapMode","activeScopeStack","stepIdCounter","generateStepId","stepKey","emitEvent","event","stepId","i","scope","earlyExit","isEarlyExitE","e","wrapForStep","causeFromMeta","unexpectedFromFailure","failure","stepFn","operationOrResult","stepOptions","parsedOptions","stepName","retryConfig","timeoutConfig","hasEventListeners","overallStartTime","effectiveRetry","lastResult","attemptStartTime","result","durationMs","timeoutMeta","timeoutMs","totalDurationMs","mappedError","mapperError","unexpectedError","errorResult","wrappedError","opts","mapToError","startTime","mapped","name","scopeId","scopeEnded","emitScopeEnd","idx","s","scopeEntry","failureCause","UnwrapError","cause","unwrap","unwrapOr","defaultValue","unwrapOrElse","from","fromPromise","promise","tryAsync","fromNullable","onNull","map","mapError","match","handlers","andThen","tap","tapError","mapTry","transform","mapErrorTry","bimap","onOk","onErr","orElse","orElseAsync","resolved","recover","recoverAsync","all","results","values","allAsync","settled","pendingCount","index","reason","allSettled","errors","partition","any","firstError","anyAsync","item","allSettledAsync","createStepCollector","steps","event","isStepComplete","isCachedErrorCause","cause","encodeCachedError","error","meta","originalCause","err","decodeCachedMeta","createWorkflow","deps","options","workflowExecutor","fnOrArgs","maybeFn","hasArgs","args","userFn","workflowId","context","emitEvent","startTs","startTime","resumeStateOption","cache","resumeState","key","entry","result","effectiveMeta","parseStepOptions","opts","createCachedStep","realStep","cachedStepFn","operationOrResult","stepOptions","name","cached","createEarlyExit","wrappedOp","value","ok","thrown","isEarlyExit","exit","operation","wrappedFn","step","strictOptions","run","normalOptions","durationMs","isPendingApproval","isApprovalRejected","pendingApproval","stepKey","createApprovalStep","injectApproval","state","newSteps","clearStep","hasPendingApproval","getPendingApprovals","pending","createHITLCollector","generateDecisionId","emitSkipped","ctx","options","decisionId","when","condition","operation","unless","whenOr","defaultValue","unlessOr","createConditionalHelpers","CircuitOpenError","options","isCircuitOpenError","error","DEFAULT_CONFIG","createCircuitBreaker","name","config","effectiveConfig","state","failures","lastFailureTime","lastSuccessTime","successCount","halfOpenSuccesses","cleanupFailures","now","f","transitionTo","newState","oldState","checkOpenToHalfOpen","handleSuccess","handleFailure","canExecute","elapsed","operation","_options","waitTime","result","err","circuitBreakerPresets","isSagaCompensationError","error","createSagaWorkflow","deps","options","fn","sagaId","startTime","compensations","emitEvent","event","runCompensations","_originalError","errors","compensationStartTime","i","comp","sagaContext","operation","stepOptions","result","createEarlyExit","opts","mapToError","value","thrown","mapped","c","durationMs","ok","originalError","isEarlyExit","compensationErrors","sagaError","err","runSaga","isRateLimitExceededError","error","isQueueFullError","createRateLimiter","name","config","maxPerSecond","strategy","maxTokens","tokens","lastRefill","refillRate","waitQueue","refill","now","tokensToAdd","tryConsume","tokensNeeded","waitForToken","resolve","check","waitTime","idx","operation","err","createConcurrencyLimiter","maxConcurrent","maxQueueSize","activeCount","queue","acquire","reject","release","operations","results","executing","i","index","promise","result","createCombinedLimiter","rate","concurrency","op","originalOp","rateLimiterPresets","isMigrationError","error","isVersionIncompatibleError","migrateState","state","targetVersion","migrations","currentState","currentVersion","err","ok","migration","cause","createVersionedStateLoader","config","version","strictVersioning","versionedState","result","createVersionedState","parseVersionedState","json","parsed","typedParsed","steps","stringifyVersionedState","createKeyRenameMigration","renames","newSteps","key","entry","newKey","createKeyRemoveMigration","keysToRemove","keysSet","createValueTransformMigration","transforms","transform","composeMigrations","createAutotelAdapter","config","serviceName","createStepSpans","recordMetrics","defaultAttributes","recordRetryEvents","markErrorsOnSpan","activeSpans","metrics","getSpanName","name","stepKey","handleEvent","event","stepId","span","createAutotelEventHandler","options","includeStepDetails","prefix","withAutotelTracing","traceFn","fn","attributes","spanName","ctx","key","value","isValidationError","defaultValidationErrorMapper","error","defaultUnexpectedErrorMapper","_error","createWebhookHandler","workflow","workflowFn","config","validateInput","mapResult","mapValidationError","mapUnexpectedError","beforeValidation","afterResponse","req","processedReq","validationResult","isOk","response","workflowResult","step","deps","createSimpleHandler","handler","result","createResultMapper","mappings","options","defaultStatus","successStatus","errorMap","mapping","toWebhookRequest","sendWebhookResponse","res","createExpressHandler","webhookReq","validationError","message","field","details","requireFields","fields","body","err","ok","composeValidators","validators","input","validator","createEventHandler","validatePayload","event","mergePolicies","policies","result","policy","createPolicyApplier","basePolicies","basePolicy","stepOptions","createPolicyBundle","name","retryPolicy","options","retryPolicies","attempts","delayMs","initialDelay","timeoutPolicy","timeoutPolicies","ms","seconds","error","servicePolicies","withPolicy","withPolicies","opts","conditionalPolicy","condition","elsePolicy","envPolicy","envPolicies","currentEnv","defaultPolicy","createPolicyRegistry","policyName","builder","key","serializeCause","cause","deserializeCause","serialized","error","serializeResult","result","deserializeResult","ok","err","serializeMeta","meta","deserializeMeta","serializeEntry","entry","deserializeEntry","serializeState","state","metadata","entries","key","deserializeState","steps","stringifyState","parseState","json","createMemoryCache","options","maxSize","ttl","cache","isExpired","timestamp","evictExpired","evictOldest","oldestKey","oldestTime","createFileCache","directory","extension","fs","keyToPath","safeKey","memoryCache","path","data","files","file","createKVCache","store","prefix","prefixKey","keys","createStatePersistence","runId","createHydratingCache","persistence","hydrated","formatDuration","ms","minutes","seconds","generateId","hasRealScopeNodes","nodes","node","detectParallelGroups","options","minOverlapMs","maxGapMs","stepsWithTiming","nonStepNodes","i","a","b","groups","currentGroup","step","groupStart","s","groupEnd","startedTogether","hasTrueOverlap","overlapDuration","groupedNodes","group","position","children","startTs","endTs","parallelNode","deriveGroupState","originalIndex","g","c","createIRBuilder","options","detectParallel","parallelDetection","workflowId","workflowStartTs","workflowState","workflowError","workflowDurationMs","activeSteps","scopeStack","decisionStack","currentNodes","createdAt","lastUpdatedAt","getStepId","event","generateId","addNode","node","decision","branch","firstBranch","handleEvent","id","active","handleScopeEvent","scope","deriveState","handleDecisionEvent","branchKey","existing","branches","b","children","c","getCurrentNodes","nodes","getIR","detectParallelGroups","reset","isStepNode","node","isParallelNode","node","isRaceNode","isDecisionNode","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","padEnd","str","width","visibleLen","stripAnsi","padding","horizontalLine","title","titleText","remainingWidth","leftPad","rightPad","asciiRenderer","ir","options","colors","defaultColorScheme","innerWidth","lines","workflowName","headerTitle","bold","childLines","renderNodes","line","status","footer","colorByState","formatDuration","nodes","depth","node","isStepNode","renderStepNode","isParallelNode","renderParallelNode","isRaceNode","renderRaceNode","isDecisionNode","renderDecisionNode","symbol","getColoredSymbol","name","nameColored","dim","inputStr","outputStr","timeoutInfo","indent","mode","i","child","prefix","nestedLines","winnerSuffix","condition","decisionValue","branchTaken","branch","branchSymbol","branchColor","branchLabel","colorize","branchCondition","getStyleDefinitions","nodeCounter","generateNodeId","prefix","resetNodeCounter","escapeMermaidText","text","escapeSubgraphName","mermaidRenderer","ir","options","lines","startId","prevNodeId","child","result","renderNode","endId","endIcon","endLabel","endShape","endClass","getStyleDefinitions","node","isStepNode","renderStepNode","isParallelNode","renderParallelNode","isRaceNode","renderRaceNode","isDecisionNode","renderDecisionNode","id","label","timing","formatDuration","stateIcon","ioInfo","inputStr","outputStr","retryInfo","timeoutStr","escapedLabel","stateClass","shape","subgraphId","forkId","joinId","name","modeLabel","note","childExitIds","exitId","winnerExitId","isWinner","decisionId","condition","decisionValue","decisionLabel","branchExitIds","takenBranchExitId","branch","branchId","branchLabelText","branchLabel","branchClass","edgeLabel","prevId","createVisualizer","options","workflowName","detectParallel","showTimings","showKeys","customColors","builder","createIRBuilder","updateCallbacks","ascii","asciiRenderer","mermaid","mermaidRenderer","renderOptions","defaultColorScheme","notifyUpdate","ir","callback","handleEvent","event","handleScopeEvent","handleDecisionEvent","getIR","render","renderAs","format","reset","onUpdate","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","createMemoryApprovalStore","approvals","key","approval","options","value","reason","pending","createMemoryWorkflowStateStore","states","state","runId","results","hasPending","approvalKey","createHITLOrchestrator","approvalStore","workflowStateStore","defaultExpirationMs","logger","execute","workflowName","workflowFactory","workflowFn","input","opts","collector","createHITLCollector","result","pendingApprovals","p","isPendingApproval","resume","savedState","status","resumeState","injectApproval","newPendingApprovals","updatedState","grantApprovalFn","grantedAt","resumedWorkflows","waitingWorkflows","rejectApprovalFn","pollApproval","intervalMs","maxPolls","timeoutMs","onPollStart","onPollComplete","startTime","pollCount","resolve","getWorkflowStatus","listPendingWorkflows","cleanup","maxAgeMs","allWorkflows","cleaned","cutoff","createApprovalWebhookHandler","store","request","action","actorId","error","createApprovalChecker","isTestEarlyExit","createWorkflowHarness","deps","options","recordInvocations","clock","scriptedOutcomes","namedOutcomes","invocationIndex","invocations","script","outcomes","scriptStep","nameOrKey","outcome","getNextOutcome","createMockStep","mockStep","operationOrResult","stepOptions","opts","startTime","invocation","ok","err","result","operation","value","error","mappedError","run","fn","runWithInput","input","getInvocations","assertSteps","expectedNames","actualNames","inv","n","passed","assertStepCalled","found","assertStepNotCalled","assertResult","expected","reset","createMockFn","defaultReturn","returnQueue","calls","args","createSnapshot","events","totalDuration","sum","e","compareSnapshots","snapshot1","snapshot2","differences","maxLen","i","inv1","inv2","createTestClock","currentTime","ms","time","okOutcome","errOutcome","throwOutcome"]}