runsheet 0.4.0 → 0.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/define-step.ts","../src/errors.ts","../src/middleware.ts","../src/when.ts","../src/pipeline.ts","../src/parallel.ts","../src/builder.ts"],"sourcesContent":["export { defineStep } from './define-step.js';\nexport { RunsheetError } from './errors.js';\nexport type { RunsheetErrorCode } from './errors.js';\nexport { buildPipeline } from './pipeline.js';\nexport type { Pipeline, PipelineConfig } from './pipeline.js';\nexport { when } from './when.js';\nexport type { ConditionalStep } from './when.js';\nexport { parallel } from './parallel.js';\nexport type { StepMiddleware, StepInfo, StepExecutor } from './middleware.js';\nexport { createPipeline } from './builder.js';\nexport type { PipelineBuilder } from './builder.js';\n\nexport type {\n Step,\n TypedStep,\n StepConfig,\n StepContext,\n StepOutput,\n ExtractRequires,\n ExtractProvides,\n RetryPolicy,\n PipelineResult,\n PipelineSuccess,\n PipelineFailure,\n PipelineExecutionMeta,\n RollbackReport,\n RollbackFailure,\n} from './types.js';\n\n// Re-export Result types so consumers never need to import composable-functions\nexport type { Result, Success, Failure } from 'composable-functions';\n","import { composable } from 'composable-functions';\nimport type { Result } from 'composable-functions';\nimport type { RetryPolicy, StepConfig, StepContext, StepOutput, Step, TypedStep } from './types.js';\nimport { RunsheetError } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// Timeout and retry wrappers\n// ---------------------------------------------------------------------------\n\nfunction withTimeout(\n run: (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>>,\n stepName: string,\n ms: number,\n): (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>> {\n return async (ctx) => {\n let timer: ReturnType<typeof setTimeout>;\n const timeout = new Promise<Result<StepOutput>>((resolve) => {\n timer = setTimeout(() => {\n const error = new RunsheetError('TIMEOUT', `${stepName} timed out after ${ms}ms`);\n resolve({ success: false as const, errors: [error] });\n }, ms);\n });\n try {\n return await Promise.race([run(ctx), timeout]);\n } finally {\n clearTimeout(timer!);\n }\n };\n}\n\nfunction computeDelay(policy: RetryPolicy, attempt: number): number {\n const base = policy.delay ?? 0;\n if (base === 0) return 0;\n const strategy = policy.backoff ?? 'linear';\n return strategy === 'exponential' ? base * 2 ** (attempt - 1) : base * attempt;\n}\n\nfunction withRetry(\n run: (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>>,\n stepName: string,\n policy: RetryPolicy,\n): (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>> {\n return async (ctx) => {\n let lastResult = await run(ctx);\n if (lastResult.success) return lastResult;\n\n for (let attempt = 1; attempt <= policy.count; attempt++) {\n // Check if the failure is retryable\n if (policy.retryIf && !policy.retryIf(lastResult.errors)) return lastResult;\n\n const delay = computeDelay(policy, attempt);\n if (delay > 0) await new Promise((r) => setTimeout(r, delay));\n lastResult = await run(ctx);\n if (lastResult.success) return lastResult;\n }\n\n // Wrap the last failure with RETRY_EXHAUSTED\n const error = new RunsheetError(\n 'RETRY_EXHAUSTED',\n `${stepName} failed after ${policy.count} retries`,\n );\n return { success: false as const, errors: [...lastResult.errors, error] };\n };\n}\n\nfunction wrapWithTimeoutAndRetry(\n run: (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>>,\n stepName: string,\n timeout: number | undefined,\n retry: RetryPolicy | undefined,\n): (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>> {\n // Timeout wraps the individual run call, retry wraps the timeout+run combo\n let wrapped = run;\n if (timeout !== undefined) wrapped = withTimeout(wrapped, stepName, timeout);\n if (retry !== undefined) wrapped = withRetry(wrapped, stepName, retry);\n return wrapped;\n}\n\n/**\n * Define a pipeline step.\n *\n * Returns a frozen {@link TypedStep} with concrete types for `run`,\n * `rollback`, `requires`, and `provides`. The `run` function can be\n * sync or async — both are supported.\n *\n * **With schemas** (runtime validation + type inference):\n * ```ts\n * const charge = defineStep({\n * name: 'charge',\n * requires: z.object({ amount: z.number() }),\n * provides: z.object({ chargeId: z.string() }),\n * run: async (ctx) => ({ chargeId: 'ch_123' }),\n * });\n * ```\n *\n * **With generics only** (no runtime validation):\n * ```ts\n * const log = defineStep<{ order: Order }, { loggedAt: Date }>({\n * name: 'log',\n * run: async (ctx) => ({ loggedAt: new Date() }),\n * });\n * ```\n *\n * **Invariants:**\n * - The returned step object is always frozen (immutable).\n * - The `run` function is wrapped with `composable()` from\n * composable-functions, which catches thrown errors and produces\n * `Result` values. Step authors should throw to signal failure.\n * - This is the single type-erasure cast point in the library.\n *\n * @typeParam Requires - The context shape this step reads from.\n * @typeParam Provides - The output shape this step produces.\n * @param config - The step configuration. See {@link StepConfig}.\n * @returns A frozen {@link TypedStep} ready for use in pipelines.\n */\nexport function defineStep<Requires extends StepContext, Provides extends StepContext>(\n config: StepConfig<Requires, Provides>,\n): TypedStep<Requires, Provides> {\n const baseRun = composable(config.run) as unknown as (\n ctx: Readonly<StepContext>,\n ) => Promise<Result<StepOutput>>;\n const wrappedRun = wrapWithTimeoutAndRetry(baseRun, config.name, config.timeout, config.retry);\n\n // The cast below is the single point where typed step functions are erased\n // to the runtime Step representation. This is safe because:\n // 1. Schema validation at step boundaries (requires/provides) enforces\n // correct types at runtime before and after each step executes.\n // 2. The pipeline accumulates context immutably, so the runtime object\n // structurally matches what the typed function expects.\n // 3. The phantom brands on TypedStep preserve compile-time type tracking\n // through the builder API without affecting runtime behavior.\n return Object.freeze({\n name: config.name,\n requires: config.requires ?? undefined,\n provides: config.provides ?? undefined,\n run: wrappedRun as unknown as Step['run'],\n rollback: config.rollback\n ? async (ctx: Readonly<StepContext>, output: Readonly<StepContext>) => {\n await (config.rollback as NonNullable<typeof config.rollback>)(\n ctx as Readonly<Requires>,\n output as Readonly<Provides>,\n );\n }\n : undefined,\n retry: config.retry ?? undefined,\n timeout: config.timeout ?? undefined,\n }) as TypedStep<Requires, Provides>;\n}\n","/**\n * Error codes for errors produced by the runsheet library itself.\n *\n * Use these to distinguish library errors from application errors\n * in a pipeline's `errors` array:\n *\n * ```ts\n * if (!result.success) {\n * for (const error of result.errors) {\n * if (error instanceof RunsheetError) {\n * console.log(error.code); // 'REQUIRES_VALIDATION', etc.\n * }\n * }\n * }\n * ```\n */\nexport type RunsheetErrorCode =\n | 'REQUIRES_VALIDATION'\n | 'PROVIDES_VALIDATION'\n | 'ARGS_VALIDATION'\n | 'PREDICATE'\n | 'TIMEOUT'\n | 'RETRY_EXHAUSTED'\n | 'STRICT_OVERLAP';\n\n/**\n * Base error class for all errors produced by the runsheet library.\n *\n * Application errors (thrown by step `run` or `rollback` functions)\n * are never wrapped in `RunsheetError` — they pass through as-is.\n * If you see a `RunsheetError` in a result's `errors` array, the\n * library itself produced it.\n *\n * Use `instanceof RunsheetError` to distinguish library errors from\n * application errors, and the `code` property to identify the\n * specific failure.\n */\nexport class RunsheetError extends Error {\n /** Discriminant code identifying the type of library error. */\n readonly code: RunsheetErrorCode;\n\n /**\n * @param code - The error code.\n * @param message - A human-readable description of the failure.\n */\n constructor(code: RunsheetErrorCode, message: string) {\n super(message);\n this.name = 'RunsheetError';\n this.code = code;\n }\n}\n","import type { Result } from 'composable-functions';\nimport type { Step, StepContext, StepOutput } from './types.js';\n\n/**\n * Metadata about the step being executed, passed to middleware.\n *\n * This is a read-only view of the step's public configuration.\n * Middleware can use it for logging, metrics, or conditional behavior.\n */\nexport type StepInfo = {\n /** The step's name. */\n readonly name: string;\n /** The step's requires schema, or `undefined` if not provided. */\n readonly requires: Step['requires'];\n /** The step's provides schema, or `undefined` if not provided. */\n readonly provides: Step['provides'];\n};\n\n/**\n * A function that executes a step (or the next middleware in the chain).\n *\n * Receives the frozen accumulated context and returns a `Result` — either\n * `{ success: true, data }` or `{ success: false, errors }`.\n */\nexport type StepExecutor = (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>>;\n\n/**\n * Middleware that wraps the entire step lifecycle, including schema\n * validation.\n *\n * A middleware receives the step metadata and a `next` function, and\n * returns a new executor. Call `next(ctx)` to proceed to the next\n * middleware (or the actual step execution). You can:\n *\n * - **Observe**: read the context or result for logging/metrics.\n * - **Transform**: modify the result before returning it.\n * - **Short-circuit**: return a `Result` without calling `next`.\n *\n * If a middleware throws, the pipeline catches it and treats it as a\n * step failure (triggering rollback for previously completed steps).\n *\n * @example\n * ```ts\n * const timing: StepMiddleware = (step, next) => async (ctx) => {\n * const start = performance.now();\n * const result = await next(ctx);\n * console.log(`${step.name}: ${performance.now() - start}ms`);\n * return result;\n * };\n * ```\n *\n * @param step - Metadata about the step being wrapped.\n * @param next - The next executor in the chain. Call it to continue.\n * @returns A new executor that wraps `next`.\n */\nexport type StepMiddleware = (step: StepInfo, next: StepExecutor) => StepExecutor;\n\n/**\n * Compose an array of middlewares around a step executor.\n *\n * First middleware in the array is the outermost wrapper (executes\n * first on the way in, last on the way out).\n *\n * @param middlewares - Middleware functions, in declaration order.\n * @param step - Metadata about the step being wrapped.\n * @param executor - The base step executor to wrap.\n * @returns A composed executor with all middleware applied.\n */\nexport function applyMiddleware(\n middlewares: readonly StepMiddleware[],\n step: StepInfo,\n executor: StepExecutor,\n): StepExecutor {\n return middlewares.reduceRight<StepExecutor>((next, mw) => mw(step, next), executor);\n}\n","import type { Step, StepContext, TypedStep } from './types.js';\n\n/**\n * A step with a conditional predicate attached.\n *\n * The pipeline engine checks for the `predicate` property to decide\n * whether to execute or skip the step. Use the {@link when} function\n * to create conditional steps — don't construct this type directly.\n */\nexport type ConditionalStep = Step & {\n /** Returns `true` to execute the step, `false` to skip it. */\n readonly predicate: (ctx: Readonly<StepContext>) => boolean;\n};\n\n/**\n * Wrap a step with a guard predicate.\n *\n * The step only executes when the predicate returns `true`. When\n * skipped:\n * - No context snapshot is taken.\n * - No rollback entry is created.\n * - The step name is recorded in `result.meta.stepsSkipped`.\n *\n * If the predicate throws, the pipeline treats it as a step failure\n * and triggers rollback for any previously completed steps.\n *\n * @example\n * ```ts\n * const steps = [\n * validateOrder,\n * when((ctx) => ctx.order.amount > 100, notifyManager),\n * sendConfirmation,\n * ];\n * ```\n *\n * @typeParam Requires - Inferred from the step's requires type.\n * @typeParam Provides - Inferred from the step's provides type.\n * @param predicate - Guard function. Receives the current accumulated\n * context (frozen). Return `true` to execute, `false` to skip.\n * @param step - The step to conditionally execute.\n * @returns A frozen {@link TypedStep} with the predicate attached.\n */\nexport function when<Requires extends StepContext, Provides extends StepContext>(\n predicate: (ctx: Readonly<Requires>) => boolean,\n step: TypedStep<Requires, Provides>,\n): TypedStep<Requires, Provides> {\n return Object.freeze({\n ...step,\n predicate: predicate as ConditionalStep['predicate'],\n }) as TypedStep<Requires, Provides>;\n}\n\n/** Type guard for conditional steps. */\nexport function isConditionalStep(step: Step): step is ConditionalStep {\n return 'predicate' in step && typeof (step as StepContext)['predicate'] === 'function';\n}\n","import type { ParserSchema, Result } from 'composable-functions';\nimport type {\n ExtractProvides,\n PipelineFailure,\n PipelineResult,\n PipelineSuccess,\n RollbackFailure,\n RollbackReport,\n Step,\n StepContext,\n StepOutput,\n UnionToIntersection,\n} from './types.js';\nimport type { StepMiddleware } from './middleware.js';\nimport type { RunsheetErrorCode } from './errors.js';\nimport { RunsheetError } from './errors.js';\nimport { applyMiddleware } from './middleware.js';\nimport { isConditionalStep } from './when.js';\n\n// ---------------------------------------------------------------------------\n// Pipeline configuration\n// ---------------------------------------------------------------------------\n\n/**\n * Internal configuration shape for the pipeline engine.\n *\n * Users typically don't construct this directly — use `buildPipeline()`\n * or `createPipeline()` instead.\n */\nexport type PipelineConfig = {\n /** Pipeline name, used in execution metadata and error messages. */\n readonly name: string;\n /** Steps to execute in order. */\n readonly steps: readonly Step[];\n /** Optional middleware applied to every step. First in array = outermost. */\n readonly middleware?: readonly StepMiddleware[];\n /** Optional schema that validates the pipeline's input arguments. */\n readonly argsSchema?: ParserSchema<StepContext>;\n /**\n * When `true`, throws at build time if two or more steps provide the\n * same key. Only checks steps that have a `provides` schema with an\n * inspectable `.shape` property (e.g., Zod objects). Steps without\n * provides schemas are not checked.\n */\n readonly strict?: boolean;\n};\n\n// ---------------------------------------------------------------------------\n// Strict mode — detect provides key collisions at build time\n// ---------------------------------------------------------------------------\n\nfunction checkStrictOverlap(steps: readonly Step[]): void {\n const seen = new Map<string, string>(); // key → step name\n\n for (const step of steps) {\n if (!step.provides) continue;\n\n // Extract keys from schemas that expose .shape (e.g., Zod objects)\n const shape = (step.provides as Record<string, unknown>).shape;\n if (!shape || typeof shape !== 'object') continue;\n\n for (const key of Object.keys(shape)) {\n const existing = seen.get(key);\n if (existing) {\n throw new RunsheetError(\n 'STRICT_OVERLAP',\n `strict mode: key \"${key}\" is provided by both \"${existing}\" and \"${step.name}\"`,\n );\n }\n seen.set(key, step.name);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline\n// ---------------------------------------------------------------------------\n\n/**\n * A built pipeline, ready to execute.\n *\n * Call `run(args)` to execute the pipeline. The result is a\n * {@link PipelineResult} — either a success with the fully accumulated\n * context, or a failure with error details and a rollback report.\n *\n * Pipeline objects are frozen (immutable) and can be called repeatedly.\n *\n * @typeParam Args - The input type accepted by `run()`.\n * @typeParam Ctx - The accumulated output type on success.\n */\nexport type Pipeline<Args extends StepContext, Ctx> = {\n /** The pipeline's name, as provided at build time. */\n readonly name: string;\n /**\n * Execute the pipeline.\n *\n * @param args - The initial arguments. Merged into the context before\n * the first step runs. Validated against `argsSchema` if one was\n * provided.\n * @returns A {@link PipelineResult} — discriminate on `success` to\n * access `data` (on success) or `errors`/`rollback` (on failure).\n */\n readonly run: (args: Args) => Promise<PipelineResult<Ctx>>;\n};\n\n// ---------------------------------------------------------------------------\n// Schema validation\n// ---------------------------------------------------------------------------\n\nfunction validateSchema<T>(\n schema: ParserSchema<T> | undefined,\n data: unknown,\n label: string,\n code: RunsheetErrorCode,\n): { success: true; data: T } | { success: false; errors: RunsheetError[] } {\n if (!schema) return { success: true, data: data as T };\n\n const parsed = schema.safeParse(data);\n if (parsed.success) return { success: true, data: parsed.data };\n\n const errors = parsed.error.issues.map(\n (issue) => new RunsheetError(code, `${label}: ${issue.path.join('.')}: ${issue.message}`),\n );\n return { success: false, errors };\n}\n\n// ---------------------------------------------------------------------------\n// Rollback\n// ---------------------------------------------------------------------------\n\nasync function executeRollback(\n executedSteps: readonly Step[],\n snapshots: readonly StepContext[],\n outputs: readonly StepOutput[],\n): Promise<RollbackReport> {\n const completed: string[] = [];\n const failed: RollbackFailure[] = [];\n\n for (let i = executedSteps.length - 1; i >= 0; i--) {\n const step = executedSteps[i];\n if (!step.rollback) continue;\n\n try {\n await step.rollback(snapshots[i], outputs[i]);\n completed.push(step.name);\n } catch (err) {\n failed.push({\n step: step.name,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n\n return Object.freeze({ completed, failed });\n}\n\n// ---------------------------------------------------------------------------\n// Execution state — accumulated during pipeline run\n// ---------------------------------------------------------------------------\n\ntype ExecutionState = {\n context: StepContext;\n readonly snapshots: StepContext[];\n readonly outputs: StepOutput[];\n readonly executedSteps: Step[];\n readonly stepsExecuted: string[];\n readonly stepsSkipped: string[];\n};\n\nfunction createExecutionState(args: StepContext): ExecutionState {\n return {\n context: Object.freeze({ ...args }),\n snapshots: [],\n outputs: [],\n executedSteps: [],\n stepsExecuted: [],\n stepsSkipped: [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Result constructors\n// ---------------------------------------------------------------------------\n\nfunction pipelineFailure(\n pipelineName: string,\n args: StepContext,\n state: ExecutionState,\n failedStep: string,\n errors: Error[],\n rollback: RollbackReport,\n): PipelineFailure {\n return Object.freeze({\n success: false as const,\n errors,\n meta: Object.freeze({\n pipeline: pipelineName,\n args,\n stepsExecuted: state.stepsExecuted,\n stepsSkipped: state.stepsSkipped,\n }),\n failedStep,\n rollback,\n });\n}\n\nfunction pipelineSuccess(\n pipelineName: string,\n args: StepContext,\n state: ExecutionState,\n): PipelineSuccess<StepContext> {\n return Object.freeze({\n success: true as const,\n data: state.context,\n errors: [] as [],\n meta: Object.freeze({\n pipeline: pipelineName,\n args,\n stepsExecuted: state.stepsExecuted,\n stepsSkipped: state.stepsSkipped,\n }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Step executor — the full lifecycle (validate requires → run → validate provides)\n// ---------------------------------------------------------------------------\n\nfunction createStepExecutor(\n step: Step,\n): (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>> {\n return async (ctx) => {\n // Validate requires\n const requiresCheck = validateSchema(\n step.requires,\n ctx,\n `${step.name} requires`,\n 'REQUIRES_VALIDATION',\n );\n if (!requiresCheck.success) {\n return { success: false as const, errors: requiresCheck.errors };\n }\n\n // Execute step run\n const result = await step.run(ctx);\n if (!result.success) return result;\n\n // Validate provides\n const providesCheck = validateSchema(\n step.provides,\n result.data,\n `${step.name} provides`,\n 'PROVIDES_VALIDATION',\n );\n if (!providesCheck.success) {\n return { success: false as const, errors: providesCheck.errors };\n }\n\n return {\n success: true as const,\n data: providesCheck.data as StepOutput,\n errors: [] as [],\n };\n };\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline execution\n// ---------------------------------------------------------------------------\n\nasync function executePipeline(\n config: PipelineConfig,\n args: StepContext,\n): Promise<PipelineResult<StepContext>> {\n // Validate pipeline args if schema provided\n if (config.argsSchema) {\n const argsCheck = validateSchema(\n config.argsSchema,\n args,\n `${config.name} args`,\n 'ARGS_VALIDATION',\n );\n if (!argsCheck.success) {\n const state = createExecutionState(args);\n return pipelineFailure(\n config.name,\n args,\n state,\n config.name,\n argsCheck.errors,\n Object.freeze({ completed: [], failed: [] }),\n );\n }\n }\n\n const state = createExecutionState(args);\n const middlewares = config.middleware ?? [];\n\n for (const step of config.steps) {\n // Evaluate conditional predicate\n try {\n if (isConditionalStep(step) && !step.predicate(state.context)) {\n state.stepsSkipped.push(step.name);\n continue;\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const error = new RunsheetError('PREDICATE', `${step.name} predicate: ${message}`);\n if (err instanceof Error) error.cause = err;\n const rollback = await executeRollback(state.executedSteps, state.snapshots, state.outputs);\n return pipelineFailure(config.name, args, state, step.name, [error], rollback);\n }\n\n // Snapshot pre-step context\n state.snapshots.push(state.context);\n\n // Build executor with middleware wrapping the full lifecycle\n const baseExecutor = createStepExecutor(step);\n const executor = applyMiddleware(\n middlewares,\n { name: step.name, requires: step.requires, provides: step.provides },\n baseExecutor,\n );\n\n // Execute (try/catch handles middleware throws outside the Result boundary)\n let result: Result<StepOutput>;\n try {\n result = await executor(state.context);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n state.snapshots.pop();\n const rollback = await executeRollback(state.executedSteps, state.snapshots, state.outputs);\n return pipelineFailure(config.name, args, state, step.name, [error], rollback);\n }\n\n if (!result.success) {\n // Remove the snapshot we just pushed — the step didn't complete\n state.snapshots.pop();\n const rollback = await executeRollback(state.executedSteps, state.snapshots, state.outputs);\n return pipelineFailure(config.name, args, state, step.name, result.errors, rollback);\n }\n\n // Track step output and accumulate context\n const output = result.data;\n state.outputs.push(output);\n state.executedSteps.push(step);\n state.stepsExecuted.push(step.name);\n state.context = Object.freeze({ ...state.context, ...output });\n }\n\n return pipelineSuccess(config.name, args, state);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a pipeline from an array of steps.\n *\n * The result type is inferred from the steps — `pipeline.run()` returns\n * a {@link PipelineResult} whose `data` is the intersection of the\n * initial `Args` and all step output types.\n *\n * @example\n * ```ts\n * const pipeline = buildPipeline({\n * name: 'placeOrder',\n * steps: [validateOrder, chargePayment, sendConfirmation],\n * middleware: [logging, timing],\n * argsSchema: z.object({ orderId: z.string() }),\n * });\n *\n * const result = await pipeline.run({ orderId: '123' });\n * if (result.success) {\n * result.data.chargeId; // string — fully typed\n * }\n * ```\n *\n * **Execution semantics:**\n * - Steps execute sequentially in array order.\n * - Context is frozen (`Object.freeze`) at every step boundary.\n * - Conditional steps (wrapped with `when()`) are skipped when their\n * predicate returns false — no snapshot, no rollback entry.\n * - On step failure, rollback handlers for all previously completed\n * steps execute in reverse order (best-effort).\n * - Middleware wraps the full step lifecycle including schema validation.\n *\n * **Invariants:**\n * - The returned pipeline object is frozen (immutable).\n * - Errors thrown by steps, predicates, or middleware are caught and\n * returned as `PipelineFailure` — `run()` never throws.\n *\n * @typeParam Args - The pipeline's input type. Inferred from `argsSchema`\n * if provided, otherwise defaults to `StepContext`.\n * @typeParam S - The step types in the array. Inferred automatically —\n * do not specify manually.\n * @param config - Pipeline configuration.\n * @param config.name - Pipeline name, used in metadata and error messages.\n * @param config.steps - Steps to execute in order.\n * @param config.middleware - Optional middleware applied to every step.\n * First in array = outermost wrapper.\n * @param config.argsSchema - Optional schema that validates `args` before\n * any steps run. Validation failure produces a `PipelineFailure` with\n * `failedStep` set to the pipeline name.\n * @returns A frozen {@link Pipeline} whose `run()` method executes the\n * steps and returns a {@link PipelineResult}.\n */\nexport function buildPipeline<\n Args extends StepContext = StepContext,\n S extends Step = Step,\n>(config: {\n readonly name: string;\n readonly steps: readonly S[];\n readonly middleware?: readonly StepMiddleware[];\n readonly argsSchema?: ParserSchema<Args>;\n readonly strict?: boolean;\n}): Pipeline<Args, Args & UnionToIntersection<ExtractProvides<S>>> {\n if (config.strict) checkStrictOverlap(config.steps);\n\n return Object.freeze({\n name: config.name,\n run: (args: Args) => executePipeline(config as PipelineConfig, args),\n }) as Pipeline<Args, Args & UnionToIntersection<ExtractProvides<S>>>;\n}\n","import type {\n ExtractProvides,\n ExtractRequires,\n Step,\n StepContext,\n StepOutput,\n TypedStep,\n UnionToIntersection,\n} from './types.js';\n\n/** Ensure a type satisfies StepContext, falling back to StepContext. */\ntype AsContext<T> = T extends StepContext ? T : StepContext;\nimport { RunsheetError } from './errors.js';\nimport { isConditionalStep } from './when.js';\n\n// ---------------------------------------------------------------------------\n// Inner step execution result\n// ---------------------------------------------------------------------------\n\ntype InnerResult = {\n step: Step;\n skipped: boolean;\n output?: StepOutput;\n errors?: Error[];\n};\n\n// ---------------------------------------------------------------------------\n// Schema validation (mirrors pipeline.ts — kept local to avoid coupling)\n// ---------------------------------------------------------------------------\n\nfunction validateInnerSchema(\n schema: Step['requires'] | Step['provides'],\n data: unknown,\n label: string,\n code: 'REQUIRES_VALIDATION' | 'PROVIDES_VALIDATION',\n): RunsheetError[] | null {\n if (!schema) return null;\n const parsed = schema.safeParse(data);\n if (parsed.success) return null;\n return parsed.error.issues.map(\n (issue) => new RunsheetError(code, `${label}: ${issue.path.join('.')}: ${issue.message}`),\n );\n}\n\n// ---------------------------------------------------------------------------\n// Execute a single inner step\n// ---------------------------------------------------------------------------\n\nasync function executeInner(step: Step, ctx: Readonly<StepContext>): Promise<InnerResult> {\n // Conditional check\n try {\n if (isConditionalStep(step) && !step.predicate(ctx)) {\n return { step, skipped: true };\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const error = new RunsheetError('PREDICATE', `${step.name} predicate: ${message}`);\n if (err instanceof Error) error.cause = err;\n return { step, skipped: false, errors: [error] };\n }\n\n // Validate requires\n const requiresErrors = validateInnerSchema(\n step.requires,\n ctx,\n `${step.name} requires`,\n 'REQUIRES_VALIDATION',\n );\n if (requiresErrors) return { step, skipped: false, errors: requiresErrors };\n\n // Run step (composable() wrapper inside defineStep catches throws)\n const result = await step.run(ctx);\n if (!result.success) return { step, skipped: false, errors: [...result.errors] };\n\n // Validate provides\n const providesErrors = validateInnerSchema(\n step.provides,\n result.data,\n `${step.name} provides`,\n 'PROVIDES_VALIDATION',\n );\n if (providesErrors) return { step, skipped: false, errors: providesErrors };\n\n return { step, skipped: false, output: result.data };\n}\n\n// ---------------------------------------------------------------------------\n// parallel()\n// ---------------------------------------------------------------------------\n\n/**\n * Run multiple steps concurrently and merge their outputs.\n *\n * All inner steps receive the same pre-parallel context snapshot and\n * execute via `Promise.allSettled`. On success, outputs are merged in\n * array order (deterministic). On partial failure, succeeded inner\n * steps are rolled back in reverse array order before the failure\n * propagates.\n *\n * The returned step acts as a single step from the pipeline's\n * perspective — middleware wraps the group as a whole.\n *\n * Inner steps retain their own `requires`/`provides` validation,\n * `retry`, and `timeout` behavior. Conditional steps (via `when()`)\n * are evaluated per inner step.\n *\n * @example\n * ```ts\n * const pipeline = buildPipeline({\n * name: 'checkout',\n * steps: [\n * validateOrder,\n * parallel(reserveInventory, chargePayment),\n * sendConfirmation,\n * ],\n * });\n * ```\n *\n * @param steps - Two or more steps to execute concurrently.\n * @returns A frozen {@link TypedStep} whose `Requires` is the\n * intersection of all inner steps' requires, and `Provides` is the\n * intersection of all inner steps' provides.\n */\nexport function parallel<S extends readonly TypedStep[]>(\n ...steps: [...S]\n): TypedStep<\n AsContext<UnionToIntersection<ExtractRequires<S[number]>>>,\n AsContext<UnionToIntersection<ExtractProvides<S[number]>>>\n> {\n const name = `parallel(${(steps as readonly Step[]).map((s) => s.name).join(', ')})`;\n const innerSteps = steps as readonly Step[];\n\n // ----- run: execute all inner steps concurrently ----- //\n const run: Step['run'] = async (ctx) => {\n const settled = await Promise.allSettled(innerSteps.map((step) => executeInner(step, ctx)));\n\n const succeeded: { step: Step; output: StepOutput }[] = [];\n const allErrors: Error[] = [];\n\n for (const s of settled) {\n if (s.status === 'rejected') {\n allErrors.push(s.reason instanceof Error ? s.reason : new Error(String(s.reason)));\n } else {\n const r = s.value;\n if (r.skipped) continue;\n if (r.output) {\n succeeded.push({ step: r.step, output: r.output });\n } else if (r.errors) {\n allErrors.push(...r.errors);\n }\n }\n }\n\n if (allErrors.length > 0) {\n // Rollback succeeded inner steps in reverse array order (best-effort)\n for (let i = succeeded.length - 1; i >= 0; i--) {\n const { step, output } = succeeded[i];\n if (step.rollback) {\n try {\n await step.rollback(ctx, output);\n } catch {\n // Best-effort — inner rollback errors during partial failure\n // are not surfaced. The pipeline's own rollback report covers\n // the parallel step as a whole.\n }\n }\n }\n return { success: false as const, errors: allErrors };\n }\n\n // Merge outputs in array order (deterministic)\n const merged: StepOutput = {};\n for (const { output } of succeeded) {\n Object.assign(merged, output);\n }\n\n return { success: true as const, data: merged, errors: [] as [] };\n };\n\n // ----- rollback: called when a later sequential step fails ----- //\n // The pipeline passes the merged output. Each inner step's rollback\n // receives the full merged output (a superset of what it produced).\n // This is safe because rollback handlers only read their own keys.\n const rollback: NonNullable<Step['rollback']> = async (ctx, mergedOutput) => {\n const errors: Error[] = [];\n for (let i = innerSteps.length - 1; i >= 0; i--) {\n const step = innerSteps[i];\n if (!step.rollback) continue;\n try {\n await step.rollback(ctx, mergedOutput);\n } catch (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)));\n }\n }\n if (errors.length > 0) {\n const error = new Error(`${name}: ${errors.length} rollback(s) failed`);\n error.cause = errors;\n throw error;\n }\n };\n\n return Object.freeze({\n name,\n requires: undefined,\n provides: undefined,\n run,\n rollback,\n retry: undefined,\n timeout: undefined,\n }) as unknown as TypedStep<\n AsContext<UnionToIntersection<ExtractRequires<S[number]>>>,\n AsContext<UnionToIntersection<ExtractProvides<S[number]>>>\n >;\n}\n","import type { ParserSchema } from 'composable-functions';\nimport type { Step, StepContext, TypedStep } from './types.js';\nimport type { StepMiddleware } from './middleware.js';\nimport { buildPipeline } from './pipeline.js';\nimport type { Pipeline } from './pipeline.js';\n\n// ---------------------------------------------------------------------------\n// Builder types\n// ---------------------------------------------------------------------------\n\n/**\n * A fluent pipeline builder that progressively narrows the accumulated\n * context type as steps are added.\n *\n * Each method returns a new, frozen builder — builders are immutable.\n * This means you can safely fork a builder to create variants:\n *\n * ```ts\n * const base = createPipeline('order').step(validate);\n * const withCharge = base.step(charge).build();\n * const withoutCharge = base.build(); // unaffected by the fork\n * ```\n *\n * @typeParam Args - The pipeline's initial input type.\n * @typeParam Ctx - The accumulated context type so far (grows with each `.step()`).\n */\nexport type PipelineBuilder<Args extends StepContext, Ctx extends StepContext> = {\n /**\n * Add a step to the pipeline.\n *\n * The step's `Requires` type must be satisfied by the current `Ctx`.\n * The returned builder's `Ctx` expands to include the step's `Provides`.\n *\n * @typeParam Provides - The output type of the step being added.\n * @param step - A {@link TypedStep} (from `defineStep` or `when`).\n * @returns A new builder with the expanded context type.\n */\n readonly step: <Provides extends StepContext>(\n step: TypedStep<Ctx, Provides>,\n ) => PipelineBuilder<Args, Ctx & Provides>;\n\n /**\n * Add middleware to the pipeline.\n *\n * Middleware is applied to every step. Multiple `.use()` calls\n * accumulate — earlier middleware is outermost (executes first).\n *\n * @param middleware - One or more {@link StepMiddleware} functions.\n * @returns A new builder with the middleware added.\n */\n readonly use: (...middleware: StepMiddleware[]) => PipelineBuilder<Args, Ctx>;\n\n /**\n * Build the pipeline.\n *\n * @returns A frozen {@link Pipeline} ready to execute with `run()`.\n */\n readonly build: () => Pipeline<Args, Ctx>;\n};\n\n// ---------------------------------------------------------------------------\n// Internal builder state (immutable — each method returns a new builder)\n// ---------------------------------------------------------------------------\n\ntype BuilderState = {\n readonly name: string;\n readonly steps: readonly Step[];\n readonly middleware: readonly StepMiddleware[];\n readonly argsSchema: ParserSchema<StepContext> | undefined;\n readonly strict: boolean;\n};\n\nfunction makeBuilder<Args extends StepContext, Ctx extends StepContext>(\n state: BuilderState,\n): PipelineBuilder<Args, Ctx> {\n return Object.freeze({\n step: <Provides extends StepContext>(step: TypedStep<Ctx, Provides>) =>\n makeBuilder<Args, Ctx & Provides>({\n ...state,\n steps: [...state.steps, step],\n }),\n\n use: (...middleware: StepMiddleware[]) =>\n makeBuilder<Args, Ctx>({\n ...state,\n middleware: [...state.middleware, ...middleware],\n }),\n\n build: () =>\n buildPipeline({\n name: state.name,\n steps: state.steps,\n middleware: state.middleware.length > 0 ? state.middleware : undefined,\n argsSchema: state.argsSchema as ParserSchema<Args> | undefined,\n strict: state.strict || undefined,\n }) as Pipeline<Args, Ctx>,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Start building a pipeline with the fluent builder API.\n *\n * The builder gives progressive type narrowing — each `.step()` call\n * extends the known context type, so TypeScript catches mismatches\n * at compile time.\n *\n * **Type-only args** (no runtime validation):\n * ```ts\n * createPipeline<{ orderId: string }>('placeOrder')\n * .step(validateOrder)\n * .step(chargePayment)\n * .build();\n * ```\n *\n * **Schema args** (runtime validation + type inference):\n * ```ts\n * createPipeline('placeOrder', z.object({ orderId: z.string() }))\n * .step(validateOrder)\n * .step(chargePayment)\n * .build();\n * ```\n *\n * **Strict mode** (no schema):\n * ```ts\n * createPipeline('placeOrder', { strict: true })\n * ```\n *\n * **Schema + strict mode:**\n * ```ts\n * createPipeline('placeOrder', z.object({ orderId: z.string() }), { strict: true })\n * ```\n *\n * @typeParam Args - The pipeline's input type. Inferred from `argsSchema`\n * if provided, otherwise specify via generic parameter.\n * @param name - Pipeline name, used in metadata and error messages.\n * @param schemaOrOptions - A schema for runtime args validation, or a\n * {@link PipelineOptions} object.\n * @param options - When the second argument is a schema, pass options here.\n * @returns A frozen {@link PipelineBuilder} ready for `.step()`,\n * `.use()`, and `.build()`.\n */\nexport type PipelineOptions = {\n strict?: boolean;\n};\n\n// Overload: name only\nexport function createPipeline<Args extends StepContext>(name: string): PipelineBuilder<Args, Args>;\n\n// Overload: name + schema\nexport function createPipeline<Args extends StepContext>(\n name: string,\n argsSchema: ParserSchema<Args>,\n): PipelineBuilder<Args, Args>;\n\n// Overload: name + options (no schema)\nexport function createPipeline<Args extends StepContext>(\n name: string,\n options: PipelineOptions,\n): PipelineBuilder<Args, Args>;\n\n// Overload: name + schema + options\nexport function createPipeline<Args extends StepContext>(\n name: string,\n argsSchema: ParserSchema<Args>,\n options: PipelineOptions,\n): PipelineBuilder<Args, Args>;\n\n// Implementation\nexport function createPipeline<Args extends StepContext>(\n name: string,\n schemaOrOptions?: ParserSchema<Args> | PipelineOptions,\n options?: PipelineOptions,\n): PipelineBuilder<Args, Args> {\n let argsSchema: ParserSchema<Args> | undefined;\n let strict = false;\n\n if (schemaOrOptions != null) {\n if ('safeParse' in schemaOrOptions) {\n argsSchema = schemaOrOptions;\n strict = options?.strict ?? false;\n } else {\n strict = schemaOrOptions.strict ?? false;\n }\n }\n\n return makeBuilder<Args, Args>({\n name,\n steps: [],\n middleware: [],\n argsSchema,\n strict,\n });\n}\n"],"mappings":"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","names":["state"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/internal.ts","../src/define-step.ts","../src/builder.ts","../src/middleware.ts","../src/when.ts","../src/pipeline.ts","../src/parallel.ts","../src/choice.ts","../src/map.ts","../src/filter.ts","../src/flat-map.ts"],"sourcesContent":["export { defineStep } from './define-step.js';\nexport {\n RunsheetError,\n RequiresValidationError,\n ProvidesValidationError,\n ArgsValidationError,\n PredicateError,\n TimeoutError,\n RetryExhaustedError,\n StrictOverlapError,\n ChoiceNoMatchError,\n RollbackError,\n UnknownError,\n} from './errors.js';\nexport type { RunsheetErrorCode } from './errors.js';\nexport { pipeline } from './pipeline.js';\nexport type { PipelineConfig } from './pipeline.js';\nexport { when } from './when.js';\nexport type { ConditionalStep } from './when.js';\nexport { parallel } from './parallel.js';\nexport { choice } from './choice.js';\nexport { map } from './map.js';\nexport { filter } from './filter.js';\nexport { flatMap } from './flat-map.js';\nexport type { StepMiddleware, StepInfo, StepExecutor } from './middleware.js';\nexport type { PipelineBuilder } from './builder.js';\n\nexport type {\n Step,\n TypedStep,\n AggregateStep,\n StepConfig,\n StepContext,\n StepOutput,\n StepSchema,\n ExtractRequires,\n ExtractProvides,\n RetryPolicy,\n StepResult,\n StepSuccess,\n StepFailure,\n StepMeta,\n AggregateResult,\n AggregateSuccess,\n AggregateFailure,\n AggregateMeta,\n RollbackReport,\n RollbackFailure,\n} from './types.js';\n","/**\n * Error codes for errors produced by the runsheet library itself.\n *\n * Use these to distinguish library errors from application errors:\n *\n * ```ts\n * if (!result.success) {\n * if (result.error instanceof RunsheetError) {\n * console.log(result.error.code); // 'REQUIRES_VALIDATION', etc.\n * }\n * }\n * ```\n */\nexport type RunsheetErrorCode =\n | 'REQUIRES_VALIDATION'\n | 'PROVIDES_VALIDATION'\n | 'ARGS_VALIDATION'\n | 'PREDICATE'\n | 'TIMEOUT'\n | 'RETRY_EXHAUSTED'\n | 'STRICT_OVERLAP'\n | 'CHOICE_NO_MATCH'\n | 'ROLLBACK'\n | 'UNKNOWN';\n\n/**\n * Base error class for all errors produced by the runsheet library.\n *\n * Application errors (thrown by step `run` or `rollback` functions)\n * are never wrapped in `RunsheetError` — they pass through as-is.\n * If you see a `RunsheetError` as `result.error`, the library itself\n * produced it.\n *\n * Use `instanceof RunsheetError` to distinguish library errors from\n * application errors. Use `instanceof` on a subclass (e.g.,\n * `TimeoutError`) or check the `code` property for specific failures.\n */\nexport class RunsheetError extends Error {\n /** Discriminant code identifying the type of library error. */\n readonly code: RunsheetErrorCode;\n\n /**\n * @param code - The error code.\n * @param message - A human-readable description of the failure.\n */\n constructor(code: RunsheetErrorCode, message: string) {\n super(message);\n this.name = 'RunsheetError';\n this.code = code;\n }\n}\n\n/** Schema validation failed on the accumulated context before a step ran. */\nexport class RequiresValidationError extends RunsheetError {\n constructor(message: string) {\n super('REQUIRES_VALIDATION', message);\n this.name = 'RequiresValidationError';\n }\n}\n\n/** Schema validation failed on a step's output after it ran. */\nexport class ProvidesValidationError extends RunsheetError {\n constructor(message: string) {\n super('PROVIDES_VALIDATION', message);\n this.name = 'ProvidesValidationError';\n }\n}\n\n/** Schema validation failed on the pipeline's input arguments. */\nexport class ArgsValidationError extends RunsheetError {\n constructor(message: string) {\n super('ARGS_VALIDATION', message);\n this.name = 'ArgsValidationError';\n }\n}\n\n/** A `when()` or `choice()` predicate threw an error. */\nexport class PredicateError extends RunsheetError {\n constructor(message: string) {\n super('PREDICATE', message);\n this.name = 'PredicateError';\n }\n}\n\n/** A step exceeded its configured timeout. */\nexport class TimeoutError extends RunsheetError {\n /** The timeout duration in milliseconds that was exceeded. */\n readonly timeoutMs: number;\n\n constructor(message: string, timeoutMs: number) {\n super('TIMEOUT', message);\n this.name = 'TimeoutError';\n this.timeoutMs = timeoutMs;\n }\n}\n\n/** A step failed after exhausting all retry attempts. */\nexport class RetryExhaustedError extends RunsheetError {\n /** Total number of attempts (initial + retries). */\n readonly attempts: number;\n\n constructor(message: string, attempts: number) {\n super('RETRY_EXHAUSTED', message);\n this.name = 'RetryExhaustedError';\n this.attempts = attempts;\n }\n}\n\n/** Two steps provide the same key (strict mode, detected at build time). */\nexport class StrictOverlapError extends RunsheetError {\n /** The key that is provided by multiple steps. */\n readonly key: string;\n /** The names of the two steps that both provide the key. */\n readonly steps: readonly [string, string];\n\n constructor(message: string, key: string, steps: readonly [string, string]) {\n super('STRICT_OVERLAP', message);\n this.name = 'StrictOverlapError';\n this.key = key;\n this.steps = steps;\n }\n}\n\n/** No branch matched in a `choice()` step. */\nexport class ChoiceNoMatchError extends RunsheetError {\n constructor(message: string) {\n super('CHOICE_NO_MATCH', message);\n this.name = 'ChoiceNoMatchError';\n }\n}\n\n/** A non-Error value was thrown and caught by the pipeline engine. */\nexport class UnknownError extends RunsheetError {\n /** The original thrown value before stringification. */\n readonly originalValue: unknown;\n\n constructor(message: string, originalValue: unknown) {\n super('UNKNOWN', message);\n this.name = 'UnknownError';\n this.originalValue = originalValue;\n }\n}\n\n/** One or more rollback handlers failed in a combinator. */\nexport class RollbackError extends RunsheetError {\n /** The individual errors from each failed rollback handler. */\n readonly causes: readonly Error[];\n\n constructor(message: string, causes: readonly Error[] = []) {\n super('ROLLBACK', message);\n this.name = 'RollbackError';\n this.causes = causes;\n this.cause = causes.length === 1 ? causes[0] : new AggregateError(causes, message);\n }\n}\n","import type {\n AggregateFailure,\n AggregateMeta,\n AggregateSuccess,\n RollbackReport,\n Step,\n StepContext,\n StepMeta,\n StepFailure,\n StepSuccess,\n StepOutput,\n} from './types.js';\nimport { UnknownError } from './errors.js';\n\n/** Ensure a type satisfies StepContext, falling back to StepContext. */\nexport type AsContext<T> = T extends StepContext ? T : StepContext;\n\n/** Normalize an unknown thrown value to an Error instance. */\nexport function toError(err: unknown): Error {\n if (err instanceof Error) return err;\n return new UnknownError(String(err), err);\n}\n\n/** Empty rollback report for single-step failures. */\nexport const EMPTY_ROLLBACK: RollbackReport = Object.freeze({\n completed: Object.freeze([] as string[]),\n failed: Object.freeze([] as never[]),\n});\n\n/** Format schema validation issues into a human-readable string. */\nexport function formatIssues(\n issues: readonly { path: readonly (string | number)[]; message: string }[],\n): string {\n return issues.map((i) => `${i.path.join('.')}: ${i.message}`).join(', ');\n}\n\n/**\n * Collapse an array of errors into a single error.\n *\n * Returns the sole error when there is exactly one, otherwise wraps\n * them in an `AggregateError` with the given message.\n */\nexport function collapseErrors(errors: Error[], message: string): Error {\n return errors.length === 1 ? errors[0] : new AggregateError(errors, message);\n}\n\n/**\n * Create a frozen {@link Step} object with `undefined` defaults for\n * omitted optional fields.\n *\n * Centralises the construction so that if `Step` gains new properties,\n * only this helper needs updating.\n */\nexport function createStepObject(fields: {\n name: string;\n run: Step['run'];\n rollback?: Step['rollback'];\n requires?: Step['requires'];\n provides?: Step['provides'];\n retry?: Step['retry'];\n timeout?: Step['timeout'];\n}): Step {\n return Object.freeze({\n name: fields.name,\n requires: fields.requires ?? undefined,\n provides: fields.provides ?? undefined,\n run: fields.run,\n rollback: fields.rollback ?? undefined,\n retry: fields.retry ?? undefined,\n timeout: fields.timeout ?? undefined,\n });\n}\n\n/**\n * Create a {@link StepMeta} for a step execution.\n *\n * Used by `defineStep` and collection combinators. Contains only\n * the step's name and the arguments it received.\n */\nexport function baseMeta(name: string, args: Readonly<StepContext>): StepMeta {\n return Object.freeze({ name, args });\n}\n\n/**\n * Create an {@link AggregateMeta} for an orchestrator execution.\n *\n * Used by `pipeline`, `parallel`, and `choice` to produce results\n * with orchestration detail (which steps ran).\n */\nexport function aggregateMeta(\n name: string,\n args: Readonly<StepContext>,\n stepsExecuted: readonly string[],\n): AggregateMeta {\n return Object.freeze({ name, args, stepsExecuted });\n}\n\n/**\n * Create a successful {@link StepResult}.\n *\n * The returned object is frozen (immutable). Used by `defineStep`\n * and collection combinators to produce consistent success results.\n */\nexport function stepSuccess<T extends StepOutput>(data: T, meta: StepMeta): StepSuccess<T> {\n return Object.freeze({ success: true, data, meta });\n}\n\n/**\n * Create a failed {@link StepResult}.\n *\n * The returned object is frozen (immutable). When no rollback\n * report is provided, defaults to {@link EMPTY_ROLLBACK} (no\n * rollbacks attempted).\n */\nexport function stepFailure(\n error: Error,\n meta: StepMeta,\n failedStep: string,\n rollback: RollbackReport = EMPTY_ROLLBACK,\n): StepFailure {\n return Object.freeze({ success: false, error, meta, failedStep, rollback });\n}\n\n/**\n * Create a successful {@link AggregateResult}.\n *\n * Like {@link stepSuccess} but with {@link AggregateMeta}.\n */\nexport function aggregateSuccess<T extends StepOutput>(\n data: T,\n meta: AggregateMeta,\n): AggregateSuccess<T> {\n return Object.freeze({ success: true, data, meta });\n}\n\n/**\n * Create a failed {@link AggregateResult}.\n *\n * Like {@link stepFailure} but with {@link AggregateMeta}.\n */\nexport function aggregateFailure(\n error: Error,\n meta: AggregateMeta,\n failedStep: string,\n rollback: RollbackReport = EMPTY_ROLLBACK,\n): AggregateFailure {\n return Object.freeze({ success: false, error, meta, failedStep, rollback });\n}\n","import type {\n RetryPolicy,\n StepConfig,\n StepContext,\n StepOutput,\n StepResult,\n Step,\n TypedStep,\n} from './types.js';\nimport {\n RequiresValidationError,\n ProvidesValidationError,\n TimeoutError,\n RetryExhaustedError,\n} from './errors.js';\nimport {\n toError,\n baseMeta,\n stepSuccess,\n stepFailure,\n formatIssues,\n createStepObject,\n} from './internal.js';\n\n// ---------------------------------------------------------------------------\n// Timeout and retry wrappers\n// ---------------------------------------------------------------------------\n\n// NOTE: Promise.race doesn't cancel the underlying run — if the timer wins,\n// the step's side effects continue in the background. True cancellation\n// would require AbortSignal propagation into step run functions.\n// Timer functions — declared here to avoid depending on @types/node or DOM lib\ndeclare function setTimeout(callback: () => void, ms: number): unknown;\ndeclare function clearTimeout(handle: unknown): void;\n\nfunction withTimeout<T>(fn: () => T | Promise<T>, stepName: string, ms: number): () => Promise<T> {\n return async () => {\n let timer: unknown;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new TimeoutError(`${stepName} timed out after ${ms}ms`, ms));\n }, ms);\n });\n try {\n return await Promise.race([Promise.resolve(fn()), timeout]);\n } finally {\n clearTimeout(timer);\n }\n };\n}\n\nfunction computeDelay(policy: RetryPolicy, attempt: number): number {\n const base = policy.delay ?? 0;\n if (base === 0) return 0;\n const strategy = policy.backoff ?? 'linear';\n return strategy === 'exponential' ? base * 2 ** (attempt - 1) : base * attempt;\n}\n\nfunction withRetry<T>(\n fn: () => Promise<T>,\n stepName: string,\n policy: RetryPolicy,\n): () => Promise<T> {\n return async () => {\n let lastError: Error;\n const errors: Error[] = [];\n\n try {\n return await fn();\n } catch (err) {\n lastError = toError(err);\n errors.push(lastError);\n }\n\n for (let attempt = 1; attempt <= policy.count; attempt++) {\n if (policy.retryIf && !policy.retryIf(errors)) throw lastError!;\n\n const delay = computeDelay(policy, attempt);\n if (delay > 0) await new Promise((r) => setTimeout(() => r(undefined), delay));\n\n try {\n return await fn();\n } catch (err) {\n lastError = toError(err);\n errors.push(lastError);\n }\n }\n\n const error = new RetryExhaustedError(\n `${stepName} failed after ${policy.count} retries`,\n policy.count + 1,\n );\n error.cause = errors;\n throw error;\n };\n}\n\nfunction buildExecutor<Requires extends StepContext, Provides extends StepContext>(\n config: StepConfig<Requires, Provides>,\n): (ctx: Readonly<Requires>) => Promise<Provides> {\n let fn = (ctx: Readonly<Requires>) => Promise.resolve(config.run(ctx));\n if (config.timeout !== undefined) {\n const baseFn = fn;\n const ms = config.timeout;\n fn = (ctx) => withTimeout(() => baseFn(ctx), config.name, ms)();\n }\n if (config.retry !== undefined) {\n const baseFn = fn;\n const policy = config.retry;\n fn = (ctx) => withRetry(() => baseFn(ctx), config.name, policy)();\n }\n return fn;\n}\n\n// ---------------------------------------------------------------------------\n// defineStep\n// ---------------------------------------------------------------------------\n\n/**\n * Define a pipeline step.\n *\n * Returns a frozen {@link TypedStep} with concrete types for `run`,\n * `rollback`, `requires`, and `provides`. The `run` function can be\n * sync or async — both are supported.\n *\n * **With schemas** (runtime validation + type inference):\n * ```ts\n * const charge = defineStep({\n * name: 'charge',\n * requires: z.object({ amount: z.number() }),\n * provides: z.object({ chargeId: z.string() }),\n * run: async (ctx) => ({ chargeId: 'ch_123' }),\n * });\n * ```\n *\n * **With generics only** (no runtime validation):\n * ```ts\n * const log = defineStep<{ order: Order }, { loggedAt: Date }>({\n * name: 'log',\n * run: async (ctx) => ({ loggedAt: new Date() }),\n * });\n * ```\n *\n * **Invariants:**\n * - The returned step object is always frozen (immutable).\n * - The `run` function catches thrown errors and produces\n * `StepResult` values. Step authors should throw to signal failure.\n * - This is the single type-erasure cast point in the library.\n *\n * @typeParam Requires - The context shape this step reads from.\n * @typeParam Provides - The output shape this step produces.\n * @param config - The step configuration. See {@link StepConfig}.\n * @returns A frozen {@link TypedStep} ready for use in pipelines.\n */\nexport function defineStep<Requires extends StepContext, Provides extends StepContext>(\n config: StepConfig<Requires, Provides>,\n): TypedStep<Requires, Provides> {\n const execute = buildExecutor(config);\n\n const run = async (ctx: Readonly<StepContext>): Promise<StepResult<StepOutput>> => {\n const frozenCtx = Object.freeze({ ...ctx });\n const meta = baseMeta(config.name, frozenCtx);\n\n // Validate requires\n if (config.requires) {\n const parsed = config.requires.safeParse(frozenCtx);\n if (!parsed.success) {\n const error = new RequiresValidationError(\n `${config.name} requires: ${formatIssues(parsed.error.issues)}`,\n );\n return stepFailure(error, meta, config.name);\n }\n }\n\n // Execute (with retry + timeout)\n let data: Provides;\n try {\n data = await execute(frozenCtx as Readonly<Requires>);\n } catch (err) {\n return stepFailure(toError(err), meta, config.name);\n }\n\n // Validate provides\n if (config.provides) {\n const parsed = config.provides.safeParse(data);\n if (!parsed.success) {\n const error = new ProvidesValidationError(\n `${config.name} provides: ${formatIssues(parsed.error.issues)}`,\n );\n return stepFailure(error, meta, config.name);\n }\n }\n\n return stepSuccess(data as unknown as StepOutput, meta);\n };\n\n return createStepObject({\n name: config.name,\n requires: config.requires,\n provides: config.provides,\n run: run as unknown as Step['run'],\n rollback: config.rollback\n ? async (ctx: Readonly<StepContext>, output: Readonly<StepContext>) => {\n await config.rollback!(ctx as Readonly<Requires>, output as Readonly<Provides>);\n }\n : undefined,\n retry: config.retry,\n timeout: config.timeout,\n }) as TypedStep<Requires, Provides>;\n}\n","import type {\n AggregateStep,\n ExtractProvides,\n ExtractRequires,\n Step,\n StepContext,\n StepSchema,\n} from './types.js';\nimport type { StepMiddleware } from './middleware.js';\nimport { buildPipelineStep } from './pipeline.js';\n\n// ---------------------------------------------------------------------------\n// Builder types\n// ---------------------------------------------------------------------------\n\n/**\n * A fluent pipeline builder that progressively narrows the accumulated\n * context type as steps are added.\n *\n * Each method returns a new, frozen builder — builders are immutable.\n *\n * This means you can safely fork a builder to create variants:\n *\n * ```ts\n * const base = pipeline({ name: 'order' }).step(validate);\n * const withCharge = base.step(charge).build();\n * const withoutCharge = base.build(); // unaffected by the fork\n * ```\n *\n * @typeParam Args - The pipeline's initial input type.\n * @typeParam Ctx - The accumulated context type so far (grows with each `.step()`).\n */\nexport type PipelineBuilder<Args extends StepContext, Ctx extends StepContext> = {\n /**\n * Add a step to the pipeline.\n *\n * The step's `Requires` type must be satisfied by the current `Ctx`\n * (checked via phantom brands — a step that requires less than `Ctx`\n * is always accepted). The returned builder's `Ctx` expands to\n * include the step's `Provides`.\n *\n * @typeParam S - The step type being added.\n * @param step - A {@link Step} (from `defineStep`, `when`, `pipeline`, etc.).\n * @returns A new builder with the expanded context type.\n */\n readonly step: <S extends Step>(\n step: S & ([Ctx] extends [ExtractRequires<S>] ? unknown : never),\n ) => PipelineBuilder<Args, Ctx & ExtractProvides<S>>;\n\n /**\n * Add middleware to the pipeline.\n *\n * Middleware is applied to every step. Multiple `.use()` calls\n * accumulate — earlier middleware is outermost (executes first).\n *\n * @param middleware - One or more {@link StepMiddleware} functions.\n * @returns A new builder with the middleware added.\n */\n readonly use: (...middleware: StepMiddleware[]) => PipelineBuilder<Args, Ctx>;\n\n /**\n * Build the pipeline. Returns an {@link AggregateStep} — pipelines\n * are steps whose `run()` returns {@link AggregateResult}.\n */\n readonly build: () => AggregateStep<Args, Ctx>;\n};\n\n// ---------------------------------------------------------------------------\n// Internal builder state (immutable — each method returns a new builder)\n// ---------------------------------------------------------------------------\n\n/** @internal */\nexport type BuilderState = {\n readonly name: string;\n readonly steps: readonly Step[];\n readonly middleware: readonly StepMiddleware[];\n readonly argsSchema: StepSchema<StepContext> | undefined;\n readonly strict: boolean;\n};\n\n/** @internal */\nexport function makeBuilder<Args extends StepContext, Ctx extends StepContext>(\n state: BuilderState,\n): PipelineBuilder<Args, Ctx> {\n return Object.freeze({\n step: <S extends Step>(step: S) =>\n makeBuilder<Args, Ctx & ExtractProvides<S>>({\n ...state,\n steps: [...state.steps, step],\n }),\n\n use: (...middleware: StepMiddleware[]) =>\n makeBuilder<Args, Ctx>({\n ...state,\n middleware: [...state.middleware, ...middleware],\n }),\n\n build: () =>\n buildPipelineStep({\n name: state.name,\n steps: state.steps,\n middleware: state.middleware.length > 0 ? state.middleware : undefined,\n argsSchema: state.argsSchema as StepSchema<Args> | undefined,\n strict: state.strict || undefined,\n }) as AggregateStep<Args, Ctx>,\n });\n}\n","import type { Step, StepContext, StepOutput, StepResult } from './types.js';\n\n/**\n * Metadata about the step being executed, passed to middleware.\n *\n * This is a read-only view of the step's public configuration.\n * Middleware can use it for logging, metrics, or conditional behavior.\n */\nexport type StepInfo = {\n /** The step's name. */\n readonly name: string;\n /** The step's requires schema, or `undefined` if not provided. */\n readonly requires: Step['requires'];\n /** The step's provides schema, or `undefined` if not provided. */\n readonly provides: Step['provides'];\n};\n\n/**\n * A function that executes a step (or the next middleware in the chain).\n *\n * Receives the frozen accumulated context and returns a {@link StepResult}.\n */\nexport type StepExecutor = (ctx: Readonly<StepContext>) => Promise<StepResult<StepOutput>>;\n\n/**\n * Middleware that wraps the entire step lifecycle, including schema\n * validation.\n *\n * A middleware receives the step metadata and a `next` function, and\n * returns a new executor. Call `next(ctx)` to proceed to the next\n * middleware (or the actual step execution). You can:\n *\n * - **Observe**: read the context or result for logging/metrics.\n * - **Transform**: modify the result before returning it.\n * - **Short-circuit**: return a `StepResult` without calling `next`.\n *\n * If a middleware throws, the pipeline catches it and treats it as a\n * step failure (triggering rollback for previously completed steps).\n *\n * @example\n * ```ts\n * const timing: StepMiddleware = (step, next) => async (ctx) => {\n * const start = performance.now();\n * const result = await next(ctx);\n * console.log(`${step.name}: ${performance.now() - start}ms`);\n * return result;\n * };\n * ```\n *\n * @param step - Metadata about the step being wrapped.\n * @param next - The next executor in the chain. Call it to continue.\n * @returns A new executor that wraps `next`.\n */\nexport type StepMiddleware = (step: StepInfo, next: StepExecutor) => StepExecutor;\n\n/**\n * Compose an array of middlewares around a step executor.\n *\n * First middleware in the array is the outermost wrapper (executes\n * first on the way in, last on the way out).\n *\n * @param middlewares - Middleware functions, in declaration order.\n * @param step - Metadata about the step being wrapped.\n * @param executor - The base step executor to wrap.\n * @returns A composed executor with all middleware applied.\n */\nexport function applyMiddleware(\n middlewares: readonly StepMiddleware[],\n step: StepInfo,\n executor: StepExecutor,\n): StepExecutor {\n return middlewares.reduceRight<StepExecutor>((next, mw) => mw(step, next), executor);\n}\n","import type { Step, StepContext, TypedStep } from './types.js';\nimport { createStepObject } from './internal.js';\n\n/**\n * A step with a conditional predicate attached.\n *\n * The pipeline engine checks for the `predicate` property to decide\n * whether to execute or skip the step. Use the {@link when} function\n * to create conditional steps — don't construct this type directly.\n */\nexport type ConditionalStep = Step & {\n /** Returns `true` to execute the step, `false` to skip it. */\n readonly predicate: (ctx: Readonly<StepContext>) => boolean;\n};\n\n/**\n * Wrap a step with a guard predicate.\n *\n * The step only executes when the predicate returns `true`. When\n * skipped:\n * - No context snapshot is taken.\n * - No rollback entry is created.\n * - The step name is not recorded in the pipeline's `meta.stepsExecuted`.\n *\n * @param predicate - Guard function. Return `true` to execute, `false` to skip.\n * @param step - The step to conditionally execute.\n * @returns A frozen {@link TypedStep} with the predicate attached.\n */\nexport function when<Requires extends StepContext, Provides extends StepContext>(\n predicate: (ctx: Readonly<Requires>) => boolean,\n step: TypedStep<Requires, Provides>,\n): TypedStep<Requires, Provides> {\n const base = createStepObject({\n name: step.name,\n run: step.run,\n rollback: step.rollback,\n requires: step.requires,\n provides: step.provides,\n retry: step.retry,\n timeout: step.timeout,\n });\n // createStepObject returns a frozen object, so we must build a new\n // object that includes the predicate and freeze it ourselves.\n return Object.freeze({\n ...base,\n predicate: predicate as ConditionalStep['predicate'],\n }) as unknown as TypedStep<Requires, Provides>;\n}\n\n/** Type guard for conditional steps. */\nexport function isConditionalStep(step: Step): step is ConditionalStep {\n return 'predicate' in step && typeof step.predicate === 'function';\n}\n","import type {\n AggregateResult,\n AggregateStep,\n ExtractProvides,\n RollbackFailure,\n RollbackReport,\n Step,\n StepContext,\n StepOutput,\n StepResult,\n StepSchema,\n UnionToIntersection,\n} from './types.js';\nimport type { StepMiddleware } from './middleware.js';\nimport type { PipelineBuilder } from './builder.js';\n// Circular import: builder.ts → buildPipelineStep (here), pipeline.ts →\n// makeBuilder (builder.ts). Safe in ESM because both are function\n// declarations — they're available by the time any function is called.\nimport { makeBuilder } from './builder.js';\nimport { applyMiddleware } from './middleware.js';\nimport { ArgsValidationError, PredicateError, StrictOverlapError } from './errors.js';\nimport {\n toError,\n aggregateMeta,\n aggregateSuccess,\n aggregateFailure,\n formatIssues,\n createStepObject,\n} from './internal.js';\nimport { isConditionalStep } from './when.js';\n\n// ---------------------------------------------------------------------------\n// Pipeline configuration\n// ---------------------------------------------------------------------------\n\n/**\n * Internal configuration shape for the pipeline engine.\n *\n * Users typically don't construct this directly — use `pipeline()`.\n */\nexport type PipelineConfig = {\n /** Pipeline name, used in execution metadata and error messages. */\n readonly name: string;\n /** Steps to execute in order. */\n readonly steps: readonly Step[];\n /** Optional middleware applied to every step. First in array = outermost. */\n readonly middleware?: readonly StepMiddleware[];\n /** Optional schema that validates the pipeline's input arguments. */\n readonly argsSchema?: StepSchema<StepContext>;\n /**\n * When `true`, throws at build time if two or more steps provide the\n * same key. Only checks steps that have a `provides` schema with an\n * inspectable `.shape` property (e.g., Zod objects). Steps without\n * provides schemas are not checked.\n */\n readonly strict?: boolean;\n};\n\n// ---------------------------------------------------------------------------\n// Strict mode — detect provides key collisions at build time\n// ---------------------------------------------------------------------------\n\nfunction checkStrictOverlap(steps: readonly Step[]): void {\n const seen = new Map<string, string>(); // key → step name\n\n for (const step of steps) {\n if (!step.provides) continue;\n\n // Extract keys from schemas that expose .shape (e.g., Zod objects)\n const shape = (step.provides as Record<string, unknown>).shape;\n if (!shape || typeof shape !== 'object') continue;\n\n for (const key of Object.keys(shape)) {\n const existing = seen.get(key);\n if (existing) {\n throw new StrictOverlapError(\n `strict mode: key \"${key}\" is provided by both \"${existing}\" and \"${step.name}\"`,\n key,\n [existing, step.name],\n );\n }\n seen.set(key, step.name);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Execution state — accumulated during pipeline run\n// ---------------------------------------------------------------------------\n\n/** Record of a step that executed successfully during a pipeline run. */\ntype ExecutedStepEntry = {\n step: Step;\n snapshot: StepContext;\n output: StepOutput;\n};\n\n/** Mutable state accumulated during pipeline execution. */\ntype ExecutionState = {\n context: StepContext;\n readonly executed: ExecutedStepEntry[];\n readonly stepsExecuted: string[];\n};\n\nfunction createExecutionState(args: StepContext): ExecutionState {\n return {\n context: Object.freeze({ ...args }),\n executed: [],\n stepsExecuted: [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Rollback\n// ---------------------------------------------------------------------------\n\n/**\n * Execute rollback handlers for previously completed steps in reverse\n * order. Best-effort: if a handler throws, remaining rollbacks still\n * execute. Returns a report of what succeeded and what failed.\n */\nasync function executeRollback(executed: readonly ExecutedStepEntry[]): Promise<RollbackReport> {\n const completed: string[] = [];\n const failed: RollbackFailure[] = [];\n\n for (let i = executed.length - 1; i >= 0; i--) {\n const entry = executed[i];\n if (!entry.step.rollback) continue;\n\n try {\n await entry.step.rollback(entry.snapshot, entry.output);\n completed.push(entry.step.name);\n } catch (err) {\n failed.push({ step: entry.step.name, error: toError(err) });\n }\n }\n\n return Object.freeze({ completed, failed });\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline execution\n// ---------------------------------------------------------------------------\n\n/**\n * Internal result of pipeline execution, pairing the user-facing\n * {@link AggregateResult} with the execution state needed for\n * rollback when this pipeline is used as a step in an outer pipeline.\n */\ntype ExecutionOutcome = {\n result: AggregateResult<StepContext>;\n state: ExecutionState;\n};\n\n/**\n * Core pipeline execution loop.\n *\n * Runs steps sequentially, accumulating context. On failure,\n * executes rollback for previously completed steps. Returns both\n * the result and the execution state (for nested pipeline rollback).\n */\nasync function executePipeline(\n config: PipelineConfig,\n args: StepContext,\n): Promise<ExecutionOutcome> {\n const frozenArgs = Object.freeze({ ...args });\n\n // Validate pipeline args if schema provided\n if (config.argsSchema) {\n const parsed = config.argsSchema.safeParse(frozenArgs);\n if (!parsed.success) {\n const error = new ArgsValidationError(\n `${config.name} args: ${formatIssues(parsed.error.issues)}`,\n );\n const meta = aggregateMeta(config.name, frozenArgs, []);\n const state = createExecutionState(frozenArgs);\n return { result: aggregateFailure(error, meta, config.name), state };\n }\n }\n\n const state = createExecutionState(frozenArgs);\n const middlewares = config.middleware ?? [];\n\n for (const step of config.steps) {\n // Evaluate conditional predicate\n try {\n if (isConditionalStep(step) && !step.predicate(state.context)) {\n continue;\n }\n } catch (err) {\n const cause = toError(err);\n const error = new PredicateError(`${step.name} predicate: ${cause.message}`);\n error.cause = cause;\n const rollback = await executeRollback(state.executed);\n const meta = aggregateMeta(config.name, frozenArgs, [...state.stepsExecuted]);\n return { result: aggregateFailure(error, meta, step.name, rollback), state };\n }\n\n // Snapshot pre-step context\n const snapshot = state.context;\n\n // Wrap step.run with middleware\n const executor =\n middlewares.length > 0\n ? applyMiddleware(\n middlewares,\n { name: step.name, requires: step.requires, provides: step.provides },\n (ctx) => step.run(ctx),\n )\n : (ctx: Readonly<StepContext>) => step.run(ctx);\n\n // Execute\n let result: StepResult<StepOutput>;\n try {\n result = await executor(state.context);\n } catch (err) {\n const error = toError(err);\n const rollback = await executeRollback(state.executed);\n const meta = aggregateMeta(config.name, frozenArgs, [...state.stepsExecuted]);\n return { result: aggregateFailure(error, meta, step.name, rollback), state };\n }\n\n if (!result.success) {\n const rollback = await executeRollback(state.executed);\n const meta = aggregateMeta(config.name, frozenArgs, [...state.stepsExecuted]);\n return { result: aggregateFailure(result.error, meta, step.name, rollback), state };\n }\n\n // Track and accumulate\n const output = result.data;\n state.executed.push({ step, snapshot, output });\n state.stepsExecuted.push(step.name);\n state.context = Object.freeze({ ...state.context, ...output });\n }\n\n const meta = aggregateMeta(config.name, frozenArgs, [...state.stepsExecuted]);\n return { result: aggregateSuccess(state.context, meta), state };\n}\n\n// ---------------------------------------------------------------------------\n// Build an AggregateStep from a PipelineConfig\n// ---------------------------------------------------------------------------\n\n/** @internal — used by the builder; not part of the public API. */\nexport function buildPipelineStep<Args extends StepContext, S extends Step>(config: {\n readonly name: string;\n readonly steps: readonly S[];\n readonly middleware?: readonly StepMiddleware[];\n readonly argsSchema?: StepSchema<Args>;\n readonly strict?: boolean;\n}): AggregateStep<Args, Args & UnionToIntersection<ExtractProvides<S>>> {\n if (config.strict) checkStrictOverlap(config.steps);\n\n const pipelineConfig: PipelineConfig = config as PipelineConfig;\n\n // Track per-execution state for rollback when used as a nested step.\n // INVARIANT: This relies on pipeline.ts storing the exact result.data\n // reference in its outputs array. If the pipeline ever clones\n // result.data, this WeakMap lookup will silently fail.\n // Exercised by the reentrancy test in pipeline.test.ts (\"handles\n // reentrancy — parallel pipelines roll back independently\").\n const stateMap = new WeakMap<object, ExecutionState>();\n\n const run = async (ctx: Readonly<StepContext>): Promise<AggregateResult<StepOutput>> => {\n const outcome = await executePipeline(pipelineConfig, ctx as StepContext);\n if (outcome.result.success) {\n stateMap.set(outcome.result.data, outcome.state);\n }\n return outcome.result;\n };\n\n const rollback = async (_ctx: Readonly<StepContext>, output: Readonly<StepOutput>) => {\n const state = stateMap.get(output);\n if (state) {\n stateMap.delete(output);\n await executeRollback(state.executed);\n }\n };\n\n return createStepObject({\n name: config.name,\n requires: config.argsSchema,\n run,\n rollback,\n }) as unknown as AggregateStep<Args, Args & UnionToIntersection<ExtractProvides<S>>>;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a pipeline — either directly from steps, or via the fluent\n * builder API.\n *\n * **With steps** — returns an {@link AggregateStep} immediately:\n *\n * ```ts\n * const checkout = pipeline({\n * name: 'checkout',\n * steps: [validateOrder, chargePayment, sendConfirmation],\n * middleware: [logging, timing],\n * argsSchema: z.object({ orderId: z.string() }),\n * });\n * ```\n *\n * **Without steps** — returns a {@link PipelineBuilder} with\n * progressive type narrowing:\n *\n * ```ts\n * const checkout = pipeline({ name: 'checkout' })\n * .step(validateOrder)\n * .step(chargePayment)\n * .build();\n *\n * // With schema (runtime validation):\n * pipeline({\n * name: 'checkout',\n * argsSchema: z.object({ orderId: z.string() }),\n * }).step(validateOrder).build();\n *\n * // Type-only args (no runtime validation):\n * pipeline<{ orderId: string }>({ name: 'checkout' })\n * .step(validateOrder)\n * .build();\n * ```\n *\n * Pipelines ARE steps — they can be used directly in another\n * pipeline's steps array for composition.\n */\n\n// Overload: with steps → AggregateStep\nexport function pipeline<Args extends StepContext = StepContext, S extends Step = Step>(config: {\n readonly name: string;\n readonly steps: readonly S[];\n readonly middleware?: readonly StepMiddleware[];\n readonly argsSchema?: StepSchema<Args>;\n readonly strict?: boolean;\n}): AggregateStep<Args, Args & UnionToIntersection<ExtractProvides<S>>>;\n\n// Overload: without steps → PipelineBuilder\nexport function pipeline<Args extends StepContext = StepContext>(config: {\n readonly name: string;\n readonly middleware?: readonly StepMiddleware[];\n readonly argsSchema?: StepSchema<Args>;\n readonly strict?: boolean;\n}): PipelineBuilder<Args, Args>;\n\n// Implementation\nexport function pipeline<Args extends StepContext, S extends Step = Step>(config: {\n readonly name: string;\n readonly steps?: readonly S[];\n readonly middleware?: readonly StepMiddleware[];\n readonly argsSchema?: StepSchema<Args>;\n readonly strict?: boolean;\n}):\n | AggregateStep<Args, Args & UnionToIntersection<ExtractProvides<S>>>\n | PipelineBuilder<Args, Args> {\n // With steps → build immediately\n if (config.steps) {\n return buildPipelineStep(\n config as {\n readonly name: string;\n readonly steps: readonly S[];\n readonly middleware?: readonly StepMiddleware[];\n readonly argsSchema?: StepSchema<Args>;\n readonly strict?: boolean;\n },\n );\n }\n\n // Without steps → return builder\n return makeBuilder<Args, Args>({\n name: config.name,\n steps: [],\n middleware: config.middleware ? [...config.middleware] : [],\n argsSchema: config.argsSchema,\n strict: config.strict ?? false,\n });\n}\n","import type {\n AggregateResult,\n AggregateStep,\n ExtractProvides,\n ExtractRequires,\n Step,\n StepContext,\n StepOutput,\n UnionToIntersection,\n} from './types.js';\nimport type { AsContext } from './internal.js';\nimport {\n toError,\n aggregateMeta,\n aggregateSuccess,\n aggregateFailure,\n collapseErrors,\n createStepObject,\n} from './internal.js';\nimport { PredicateError, RollbackError } from './errors.js';\nimport { isConditionalStep } from './when.js';\n\n// ---------------------------------------------------------------------------\n// Inner step execution result\n// ---------------------------------------------------------------------------\n\ntype InnerResult = {\n step: Step;\n skipped: boolean;\n output?: StepOutput;\n error?: Error;\n};\n\n// ---------------------------------------------------------------------------\n// Execute a single inner step (with conditional check)\n// ---------------------------------------------------------------------------\n\nasync function executeInner(step: Step, ctx: Readonly<StepContext>): Promise<InnerResult> {\n // Conditional check\n try {\n if (isConditionalStep(step) && !step.predicate(ctx)) {\n return { step, skipped: true };\n }\n } catch (err) {\n const cause = toError(err);\n const error = new PredicateError(`${step.name} predicate: ${cause.message}`);\n error.cause = cause;\n return { step, skipped: false, error };\n }\n\n const result = await step.run(ctx);\n if (!result.success) return { step, skipped: false, error: result.error };\n return { step, skipped: false, output: result.data };\n}\n\n// ---------------------------------------------------------------------------\n// parallel()\n// ---------------------------------------------------------------------------\n\n/**\n * Run multiple steps concurrently and merge their outputs.\n *\n * All inner steps receive the same pre-parallel context snapshot and\n * execute via `Promise.allSettled`. On success, outputs are merged in\n * array order (deterministic). On partial failure, succeeded inner\n * steps are rolled back in reverse array order before the failure\n * propagates.\n *\n * Returns an {@link AggregateStep} with orchestration metadata\n * tracking which inner steps executed.\n *\n * Inner steps retain their own `requires`/`provides` validation,\n * `retry`, and `timeout` behavior. Conditional steps (via `when()`)\n * are evaluated per inner step.\n *\n * @example\n * ```ts\n * const p = pipeline({\n * name: 'checkout',\n * steps: [\n * validateOrder,\n * parallel(reserveInventory, chargePayment),\n * sendConfirmation,\n * ],\n * });\n * ```\n *\n * @param steps - Two or more steps to execute concurrently.\n * @returns A frozen {@link AggregateStep} whose `Requires` is the\n * intersection of all inner steps' requires, and `Provides` is the\n * intersection of all inner steps' provides.\n */\nexport function parallel<S extends readonly Step[]>(\n ...steps: [...S]\n): AggregateStep<\n AsContext<UnionToIntersection<ExtractRequires<S[number]>>>,\n AsContext<UnionToIntersection<ExtractProvides<S[number]>>>\n> {\n const name = `parallel(${steps.map((s) => s.name).join(', ')})`;\n const innerSteps: readonly Step[] = steps;\n\n // ----- run: execute all inner steps concurrently ----- //\n const run = async (ctx: Readonly<StepContext>): Promise<AggregateResult<StepOutput>> => {\n const frozenCtx = Object.freeze({ ...ctx });\n\n const settled = await Promise.allSettled(\n innerSteps.map((step) => executeInner(step, frozenCtx)),\n );\n\n const succeeded: { step: Step; output: StepOutput }[] = [];\n const allErrors: Error[] = [];\n const executed: string[] = [];\n\n for (const s of settled) {\n if (s.status === 'rejected') {\n allErrors.push(toError(s.reason));\n } else {\n const r = s.value;\n if (r.skipped) continue;\n if (r.output) {\n succeeded.push({ step: r.step, output: r.output });\n executed.push(r.step.name);\n } else if (r.error) {\n allErrors.push(r.error);\n }\n }\n }\n\n if (allErrors.length > 0) {\n // Rollback succeeded inner steps in reverse array order (best-effort)\n for (let i = succeeded.length - 1; i >= 0; i--) {\n const { step, output } = succeeded[i];\n if (step.rollback) {\n try {\n await step.rollback(frozenCtx, output);\n } catch {\n // Best-effort — inner rollback errors during partial failure\n // are not surfaced. The pipeline's own rollback report covers\n // the parallel step as a whole.\n }\n }\n }\n const error = collapseErrors(allErrors, `${name}: ${allErrors.length} step(s) failed`);\n const meta = aggregateMeta(name, frozenCtx, executed);\n return aggregateFailure(error, meta, name);\n }\n\n // Merge outputs in array order (deterministic)\n const merged: StepOutput = {};\n for (const { output } of succeeded) {\n Object.assign(merged, output);\n }\n\n // Track which inner steps executed and their individual outputs\n // for outer rollback. Individual outputs are needed so that nested\n // pipeline rollback handlers can look up their per-execution state.\n executedMap.set(\n merged,\n succeeded.map((s) => ({ step: s.step, output: s.output })),\n );\n\n const meta = aggregateMeta(name, frozenCtx, executed);\n return aggregateSuccess(merged, meta);\n };\n\n // Track which inner steps ran per execution for outer rollback.\n // INVARIANT: This relies on pipeline.ts storing the exact result.data\n // reference in its outputs array. If the pipeline ever clones\n // result.data, this WeakMap lookup will silently fail.\n // Exercised by parallel.test.ts (\"rolls back all inner steps when\n // a later sequential step fails\").\n type ExecutedEntry = { step: Step; output: StepOutput };\n const executedMap = new WeakMap<object, ExecutedEntry[]>();\n\n // ----- rollback: called when a later sequential step fails ----- //\n // The pipeline passes the merged output. Only inner steps that\n // actually executed are rolled back, and each receives its own\n // individual output (not the merged superset).\n // Rollback is called by the outer pipeline when a later step fails.\n // The thrown RollbackError is intentional — the pipeline's own\n // executeRollback loop catches it and records it in result.rollback.failed.\n const rollback: NonNullable<Step['rollback']> = async (ctx, mergedOutput) => {\n const entries = executedMap.get(mergedOutput);\n if (!entries) return;\n executedMap.delete(mergedOutput);\n const errors: Error[] = [];\n for (let i = entries.length - 1; i >= 0; i--) {\n const { step, output } = entries[i];\n if (!step.rollback) continue;\n try {\n await step.rollback(ctx, output);\n } catch (err) {\n errors.push(toError(err));\n }\n }\n if (errors.length > 0) {\n throw new RollbackError(`${name}: ${errors.length} rollback(s) failed`, errors);\n }\n };\n\n return createStepObject({\n name,\n run,\n rollback,\n }) as unknown as AggregateStep<\n AsContext<UnionToIntersection<ExtractRequires<S[number]>>>,\n AsContext<UnionToIntersection<ExtractProvides<S[number]>>>\n >;\n}\n","import type {\n AggregateResult,\n AggregateStep,\n ExtractProvides,\n ExtractRequires,\n Step,\n StepContext,\n StepOutput,\n UnionToIntersection,\n} from './types.js';\nimport type { AsContext } from './internal.js';\nimport {\n toError,\n aggregateMeta,\n aggregateSuccess,\n aggregateFailure,\n createStepObject,\n} from './internal.js';\nimport { ChoiceNoMatchError, PredicateError, RollbackError } from './errors.js';\n\n/** A [predicate, step] tuple used by {@link choice}. */\ntype BranchTuple = readonly [(ctx: Readonly<StepContext>) => boolean, Step];\n\n/** Extract the Requires type from a branch tuple's step. */\ntype BranchRequires<T> = T extends readonly [unknown, infer S extends Step]\n ? ExtractRequires<S>\n : T extends Step\n ? ExtractRequires<T>\n : StepContext;\n\n/** Extract the Provides type from a branch tuple's step. */\ntype BranchProvides<T> = T extends readonly [unknown, infer S extends Step]\n ? ExtractProvides<S>\n : T extends Step\n ? ExtractProvides<T>\n : StepContext;\n\n// ---------------------------------------------------------------------------\n// Normalize args: convert trailing bare step into a [() => true, step] tuple\n// ---------------------------------------------------------------------------\n\ntype NormalizedBranch = readonly [(ctx: Readonly<StepContext>) => boolean, Step];\n\nfunction normalizeBranches(args: readonly (BranchTuple | Step)[]): readonly NormalizedBranch[] {\n return args.map((arg): NormalizedBranch => {\n if (Array.isArray(arg)) return arg as NormalizedBranch;\n // Bare step → default branch\n return [() => true, arg as Step];\n });\n}\n\n// ---------------------------------------------------------------------------\n// choice()\n// ---------------------------------------------------------------------------\n\n/**\n * Execute the first branch whose predicate returns `true`.\n *\n * Similar to an AWS Step Functions Choice state — predicates are evaluated\n * in order, and the first match wins. Exactly one branch executes. If no\n * predicate matches, the step fails with a `CHOICE_NO_MATCH` error.\n *\n * A bare step (without a predicate tuple) can be passed as the last argument\n * to serve as a default branch — it is equivalent to `[() => true, step]`.\n *\n * Returns an {@link AggregateStep} with orchestration metadata\n * tracking which branch executed.\n *\n * All branches should provide the same output shape so that\n * subsequent steps can rely on a consistent context type.\n *\n * @example\n * ```ts\n * const p = pipeline({\n * name: 'payment',\n * steps: [\n * validateOrder,\n * choice(\n * [(ctx) => ctx.method === 'card', chargeCard],\n * [(ctx) => ctx.method === 'bank', chargeBankTransfer],\n * chargeDefault, // default\n * ),\n * sendReceipt,\n * ],\n * });\n * ```\n *\n * @param branches - One or more `[predicate, step]` tuples, optionally\n * followed by a bare step as the default.\n * @returns A frozen {@link AggregateStep} that executes the first\n * matching branch.\n */\n\n// Overload: all branches are tuples (no default)\nexport function choice<B extends readonly BranchTuple[]>(\n ...branches: [...B]\n): AggregateStep<\n AsContext<UnionToIntersection<BranchRequires<B[number]>>>,\n AsContext<UnionToIntersection<BranchProvides<B[number]>>>\n>;\n\n// Overload: tuples + trailing bare step as default\nexport function choice<B extends readonly BranchTuple[], D extends Step>(\n ...args: [...B, D]\n): AggregateStep<\n AsContext<UnionToIntersection<BranchRequires<B[number]> | ExtractRequires<D>>>,\n AsContext<UnionToIntersection<BranchProvides<B[number]> | ExtractProvides<D>>>\n>;\n\n// Implementation\nexport function choice(...args: (BranchTuple | Step)[]): AggregateStep<StepContext, StepContext> {\n const innerBranches = normalizeBranches(args);\n const name = `choice(${innerBranches.map(([, step]) => step.name).join(', ')})`;\n\n // Track which branch ran per execution for rollback.\n // INVARIANT: This relies on pipeline.ts storing the exact result.data\n // reference in its outputs array. If the pipeline ever clones\n // result.data, this WeakMap lookup will silently fail.\n // Exercised by choice.test.ts (\"rolls back the matched branch when\n // a later step fails\").\n const branchMap = new WeakMap<object, number>();\n\n const run = async (ctx: Readonly<StepContext>): Promise<AggregateResult<StepOutput>> => {\n const frozenCtx = Object.freeze({ ...ctx });\n\n for (let i = 0; i < innerBranches.length; i++) {\n const [predicate, step] = innerBranches[i];\n\n // Evaluate predicate\n let matches: boolean;\n try {\n matches = predicate(frozenCtx);\n } catch (err) {\n const cause = toError(err);\n const error = new PredicateError(`${name} predicate: ${cause.message}`);\n error.cause = cause;\n const meta = aggregateMeta(name, frozenCtx, []);\n return aggregateFailure(error, meta, name);\n }\n\n if (!matches) continue;\n\n const result = await step.run(frozenCtx);\n if (!result.success) {\n const meta = aggregateMeta(name, frozenCtx, [step.name]);\n return aggregateFailure(result.error, meta, name);\n }\n\n // Track which branch ran for rollback\n branchMap.set(result.data, i);\n\n const meta = aggregateMeta(name, frozenCtx, [step.name]);\n return aggregateSuccess(result.data, meta);\n }\n\n // No branch matched\n const meta = aggregateMeta(name, frozenCtx, []);\n return aggregateFailure(new ChoiceNoMatchError(`${name}: no branch matched`), meta, name);\n };\n\n // Rollback: only the matched branch needs rollback.\n // The thrown RollbackError is intentional — the pipeline's own\n // executeRollback loop catches it and records it in result.rollback.failed.\n const rollback: NonNullable<Step['rollback']> = async (ctx, output) => {\n const branchIndex = branchMap.get(output);\n if (branchIndex === undefined) return;\n branchMap.delete(output);\n const [, step] = innerBranches[branchIndex];\n if (step.rollback) {\n try {\n await step.rollback(ctx, output);\n } catch (err) {\n throw new RollbackError(`${name}: 1 rollback(s) failed`, [toError(err)]);\n }\n }\n };\n\n return createStepObject({\n name,\n run,\n rollback,\n }) as unknown as AggregateStep<StepContext, StepContext>;\n}\n","import type {\n ExtractProvides,\n Step,\n StepContext,\n StepOutput,\n StepResult,\n TypedStep,\n} from './types.js';\nimport {\n toError,\n baseMeta,\n stepSuccess,\n stepFailure,\n collapseErrors,\n createStepObject,\n} from './internal.js';\nimport { RollbackError } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// Runtime step detection\n// ---------------------------------------------------------------------------\n\nfunction isStep(x: unknown): x is Step {\n return typeof x === 'object' && x !== null && 'run' in x && 'name' in x;\n}\n\n// ---------------------------------------------------------------------------\n// map()\n// ---------------------------------------------------------------------------\n\n/**\n * Iterate over a collection and run a function or step per item, concurrently.\n *\n * **Function form:** `(item, ctx) => result` — items can be any type.\n *\n * **Step form:** each item must be an object whose keys are spread into\n * the pipeline context before the step runs.\n *\n * The step receives `{ ...ctx, ...item }`. The step's own\n * `requires`/`provides` validation, `retry`, and `timeout` apply\n * per item. On partial failure, succeeded items are rolled back\n * (if the step has a rollback handler).\n *\n * @example\n * ```ts\n * // Function form\n * const pipeline = pipeline({\n * name: 'notify',\n * steps: [\n * map('emails', (ctx) => ctx.users, async (user) => {\n * await sendEmail(user.email);\n * return { email: user.email, sentAt: new Date() };\n * }),\n * ],\n * });\n *\n * // Step form\n * const pipeline = pipeline({\n * name: 'process',\n * steps: [\n * map('results', (ctx) => ctx.items, processItem),\n * ],\n * });\n * ```\n *\n * @param key - The output key under which results are collected.\n * @param collection - A selector that extracts the collection from context.\n * @param fnOrStep - A per-item function or a step to execute for each item.\n * @returns A frozen {@link TypedStep} that provides `{ [key]: Result[] }`.\n */\n\n// Overload: plain function callback\nexport function map<K extends string, Item, Result, Ctx extends StepContext = StepContext>(\n key: K,\n collection: (ctx: Readonly<Ctx>) => Item[],\n fn: (item: Item, ctx: Readonly<Ctx>) => Result | Promise<Result>,\n): TypedStep<Ctx, Record<K, Awaited<Result>[]>>;\n\n// Overload: step callback\nexport function map<K extends string, S extends Step, Ctx extends StepContext = StepContext>(\n key: K,\n collection: (ctx: Readonly<Ctx>) => StepContext[],\n step: S,\n): TypedStep<Ctx, Record<K, ExtractProvides<S>[]>>;\n\n// Implementation\nexport function map(\n key: string,\n collection: (ctx: Readonly<StepContext>) => unknown[],\n fnOrStep: ((item: unknown, ctx: Readonly<StepContext>) => unknown) | Step,\n): TypedStep<StepContext, StepContext> {\n const stepMode = isStep(fnOrStep);\n const name = stepMode ? `map(${key}, ${(fnOrStep as Step).name})` : `map(${key})`;\n\n // Track per-execution data for rollback (step mode only).\n // INVARIANT: This relies on pipeline.ts storing the exact result.data\n // reference in its outputs array. If the pipeline ever clones\n // result.data, this WeakMap lookup will silently fail.\n // Exercised by map.test.ts (\"rolls back all items on external\n // failure (later step fails)\").\n const executionMap = new WeakMap<object, { items: unknown[]; ctx: StepContext }>();\n\n const run = async (ctx: Readonly<StepContext>): Promise<StepResult<StepOutput>> => {\n const frozenCtx = Object.freeze({ ...ctx });\n const meta = baseMeta(name, frozenCtx);\n\n // Extract collection\n let items: unknown[];\n try {\n items = collection(frozenCtx);\n } catch (err) {\n return stepFailure(toError(err), meta, name);\n }\n\n if (stepMode) {\n return runStepMode(fnOrStep as Step, items, frozenCtx, name, key, executionMap, meta);\n } else {\n return runFunctionMode(\n fnOrStep as (item: unknown, ctx: Readonly<StepContext>) => unknown,\n items,\n frozenCtx,\n key,\n name,\n meta,\n );\n }\n };\n\n // Rollback (step mode only): roll back each succeeded item in reverse.\n // The thrown RollbackError is intentional — the pipeline's own\n // executeRollback loop catches it and records it in result.rollback.failed.\n const rollback: Step['rollback'] = stepMode\n ? async (_ctx, output) => {\n const step = fnOrStep as Step;\n if (!step.rollback) return;\n const exec = executionMap.get(output);\n if (!exec) return;\n executionMap.delete(output);\n const results = (output as Record<string, unknown>)[key] as StepOutput[];\n const errors: Error[] = [];\n for (let i = results.length - 1; i >= 0; i--) {\n try {\n const itemCtx = Object.freeze({ ...exec.ctx, ...(exec.items[i] as StepContext) });\n await step.rollback(itemCtx, results[i]);\n } catch (err) {\n errors.push(toError(err));\n }\n }\n if (errors.length > 0) {\n throw new RollbackError(`${name}: ${errors.length} rollback(s) failed`, errors);\n }\n }\n : undefined;\n\n return createStepObject({\n name,\n run: run as Step['run'],\n rollback,\n }) as unknown as TypedStep<StepContext, StepContext>;\n}\n\n// ---------------------------------------------------------------------------\n// Step mode: run inner step per item with validation + partial rollback\n// ---------------------------------------------------------------------------\n\nasync function runStepMode(\n step: Step,\n items: unknown[],\n ctx: Readonly<StepContext>,\n name: string,\n key: string,\n executionMap: WeakMap<object, { items: unknown[]; ctx: StepContext }>,\n meta: ReturnType<typeof baseMeta>,\n): Promise<StepResult<StepOutput>> {\n const settled = await Promise.allSettled(\n items.map(async (item) => {\n const itemCtx = { ...ctx, ...(item as StepContext) };\n return step.run(itemCtx);\n }),\n );\n\n const succeeded: { index: number; output: StepOutput }[] = [];\n const allErrors: Error[] = [];\n\n for (let i = 0; i < settled.length; i++) {\n const s = settled[i];\n if (s.status === 'rejected') {\n allErrors.push(toError(s.reason));\n } else if (!s.value.success) {\n allErrors.push(s.value.error);\n } else {\n succeeded.push({ index: i, output: s.value.data });\n }\n }\n\n if (allErrors.length > 0) {\n // Roll back succeeded items in reverse order (best-effort)\n if (step.rollback) {\n for (let i = succeeded.length - 1; i >= 0; i--) {\n try {\n const itemCtx = Object.freeze({ ...ctx, ...(items[succeeded[i].index] as StepContext) });\n await step.rollback(itemCtx, succeeded[i].output);\n } catch {\n // Best-effort — swallowed during partial failure\n }\n }\n }\n return stepFailure(\n collapseErrors(allErrors, `${name}: ${allErrors.length} item(s) failed`),\n meta,\n name,\n );\n }\n\n // Collect results in original order\n const results = succeeded.map((s) => s.output);\n const data: StepOutput = { [key]: results };\n executionMap.set(data, { items, ctx: { ...ctx } });\n return stepSuccess(data, meta);\n}\n\n// ---------------------------------------------------------------------------\n// Function mode: run callback per item, no rollback\n// ---------------------------------------------------------------------------\n\nasync function runFunctionMode(\n fn: (item: unknown, ctx: Readonly<StepContext>) => unknown,\n items: unknown[],\n ctx: Readonly<StepContext>,\n key: string,\n stepName: string,\n meta: ReturnType<typeof baseMeta>,\n): Promise<StepResult<StepOutput>> {\n const settled = await Promise.allSettled(items.map(async (item) => fn(item, ctx)));\n\n // Check for errors first — don't collect partial results\n const allErrors: Error[] = [];\n for (const s of settled) {\n if (s.status === 'rejected') allErrors.push(toError(s.reason));\n }\n if (allErrors.length > 0) {\n return stepFailure(\n collapseErrors(allErrors, `${stepName}: ${allErrors.length} item(s) failed`),\n meta,\n stepName,\n );\n }\n\n // All succeeded — collect results in original order\n const results = settled.map((s) => (s as PromiseFulfilledResult<unknown>).value);\n\n const data: StepOutput = { [key]: results };\n return stepSuccess(data, meta);\n}\n","import type { Step, StepContext, StepOutput, StepResult, TypedStep } from './types.js';\nimport {\n toError,\n baseMeta,\n stepSuccess,\n stepFailure,\n collapseErrors,\n createStepObject,\n} from './internal.js';\n\n// ---------------------------------------------------------------------------\n// filter()\n// ---------------------------------------------------------------------------\n\n/**\n * Filter a collection from context using a predicate, concurrently.\n *\n * Extracts a collection from the pipeline context, evaluates the\n * predicate for each item via `Promise.allSettled`, and collects\n * items that pass into an array under the given key.\n *\n * Original order is preserved.\n *\n * The predicate can be sync or async. If any predicate throws, the\n * entire step fails — no partial results are returned.\n *\n * There is no rollback (filtering is a pure operation with nothing\n * to undo).\n *\n * @example\n * ```ts\n * const pipeline = pipeline({\n * name: 'notify',\n * steps: [\n * filter(\n * 'eligible',\n * (ctx) => ctx.users,\n * (user) => user.optedIn,\n * ),\n * map('emails', (ctx) => ctx.eligible, sendEmail),\n * ],\n * });\n *\n * // Async predicate\n * filter('valid', (ctx) => ctx.orders, async (order) => {\n * const inventory = await checkInventory(order.sku);\n * return inventory.available >= order.quantity;\n * });\n * ```\n *\n * @param key - The output key under which filtered results are collected.\n * @param collection - A selector that extracts the collection from context.\n * @param predicate - A per-item predicate. Return `true` to keep, `false` to discard.\n * @returns A frozen {@link TypedStep} that provides `{ [key]: Item[] }`.\n */\nexport function filter<K extends string, Item, Ctx extends StepContext = StepContext>(\n key: K,\n collection: (ctx: Readonly<Ctx>) => Item[],\n predicate: (item: Item, ctx: Readonly<Ctx>) => boolean | Promise<boolean>,\n): TypedStep<Ctx, Record<K, Item[]>> {\n const name = `filter(${key})`;\n\n const run = async (ctx: Readonly<StepContext>): Promise<StepResult<StepOutput>> => {\n const frozenCtx = Object.freeze({ ...ctx });\n const meta = baseMeta(name, frozenCtx);\n\n let items: unknown[];\n try {\n items = collection(frozenCtx as Readonly<Ctx>);\n } catch (err) {\n return stepFailure(toError(err), meta, name);\n }\n\n return runFilter(\n items,\n frozenCtx,\n predicate as (item: unknown, ctx: Readonly<StepContext>) => boolean | Promise<boolean>,\n key,\n name,\n meta,\n );\n };\n\n return createStepObject({\n name,\n run: run as Step['run'],\n }) as unknown as TypedStep<Ctx, Record<K, Item[]>>;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nasync function runFilter(\n items: unknown[],\n ctx: Readonly<StepContext>,\n predicate: (item: unknown, ctx: Readonly<StepContext>) => boolean | Promise<boolean>,\n key: string,\n name: string,\n meta: ReturnType<typeof baseMeta>,\n): Promise<StepResult<StepOutput>> {\n const settled = await Promise.allSettled(items.map(async (item) => predicate(item, ctx)));\n\n // Check for errors first — don't collect partial results\n const allErrors: Error[] = [];\n for (const s of settled) {\n if (s.status === 'rejected') allErrors.push(toError(s.reason));\n }\n if (allErrors.length > 0) {\n return stepFailure(\n collapseErrors(allErrors, `${name}: ${allErrors.length} predicate(s) failed`),\n meta,\n name,\n );\n }\n\n // All succeeded — collect results preserving original order\n const results: unknown[] = [];\n for (let i = 0; i < settled.length; i++) {\n if ((settled[i] as PromiseFulfilledResult<boolean>).value) {\n results.push(items[i]);\n }\n }\n\n const data: StepOutput = { [key]: results };\n return stepSuccess(data, meta);\n}\n","import type { Step, StepContext, StepOutput, StepResult, TypedStep } from './types.js';\nimport {\n toError,\n baseMeta,\n stepSuccess,\n stepFailure,\n collapseErrors,\n createStepObject,\n} from './internal.js';\n\n// ---------------------------------------------------------------------------\n// flatMap()\n// ---------------------------------------------------------------------------\n\n/**\n * Map each item in a collection to an array, then flatten one level.\n *\n * Extracts a collection from the pipeline context, runs the callback\n * for each item via `Promise.allSettled`, and flattens the per-item\n * arrays into a single array under the given key.\n *\n * The callback can be sync or async. If any callback throws, the\n * entire step fails — no partial results are returned.\n *\n * There is no rollback (pure transformation with nothing to undo).\n *\n * @example\n * ```ts\n * // Expand orders into line items\n * const pipeline = pipeline({\n * name: 'process',\n * steps: [\n * flatMap(\n * 'lineItems',\n * (ctx) => ctx.orders,\n * (order) => order.items,\n * ),\n * ],\n * });\n *\n * // Async callback\n * flatMap('emails', (ctx) => ctx.teams, async (team) => {\n * const members = await fetchMembers(team.id);\n * return members.map((m) => m.email);\n * });\n * ```\n *\n * @param key - The output key under which flattened results are collected.\n * @param collection - A selector that extracts the collection from context.\n * @param fn - A per-item callback that returns an array (or Promise of array).\n * @returns A frozen {@link TypedStep} that provides `{ [key]: Item[] }`.\n */\nexport function flatMap<K extends string, Item, Result, Ctx extends StepContext = StepContext>(\n key: K,\n collection: (ctx: Readonly<Ctx>) => Item[],\n fn: (item: Item, ctx: Readonly<Ctx>) => Result[] | Promise<Result[]>,\n): TypedStep<Ctx, Record<K, Result[]>> {\n const name = `flatMap(${key})`;\n\n const run = async (ctx: Readonly<StepContext>): Promise<StepResult<StepOutput>> => {\n const frozenCtx = Object.freeze({ ...ctx });\n const meta = baseMeta(name, frozenCtx);\n\n let items: unknown[];\n try {\n items = collection(frozenCtx as Readonly<Ctx>);\n } catch (err) {\n return stepFailure(toError(err), meta, name);\n }\n\n return runFlatMap(\n items,\n frozenCtx,\n fn as (item: unknown, ctx: Readonly<StepContext>) => unknown[] | Promise<unknown[]>,\n key,\n name,\n meta,\n );\n };\n\n return createStepObject({\n name,\n run: run as Step['run'],\n }) as unknown as TypedStep<Ctx, Record<K, Result[]>>;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nasync function runFlatMap(\n items: unknown[],\n ctx: Readonly<StepContext>,\n fn: (item: unknown, ctx: Readonly<StepContext>) => unknown[] | Promise<unknown[]>,\n key: string,\n name: string,\n meta: ReturnType<typeof baseMeta>,\n): Promise<StepResult<StepOutput>> {\n const settled = await Promise.allSettled(items.map(async (item) => fn(item, ctx)));\n\n // Check for errors first — don't collect partial results\n const allErrors: Error[] = [];\n for (const s of settled) {\n if (s.status === 'rejected') allErrors.push(toError(s.reason));\n }\n if (allErrors.length > 0) {\n return stepFailure(\n collapseErrors(allErrors, `${name}: ${allErrors.length} callback(s) failed`),\n meta,\n name,\n );\n }\n\n // All succeeded — flatten results\n const results: unknown[] = [];\n for (const s of settled) {\n results.push(...(s as PromiseFulfilledResult<unknown[]>).value);\n }\n\n const data: StepOutput = { [key]: results };\n return stepSuccess(data, meta);\n}\n"],"mappings":"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","names":["meta","state","meta","meta"]}