Overview

Recover

Recover domain errors into success or a new typed failure.

errorsrecovery

Install

$
Terminal window
pnpm dlx shadcn@latest add https://archyn.net/r/recover.json

Copy the files below into the target paths shown in each tab.

ts@lib/flow/recover.ts
import { type Awaitable, type Result, err } from "./core/result";
import { type Behavior, isRuntimeError, runBehavior } from "./core/runtime";
import { type TaskContext, task } from "./task";
/**
* Recovers expected domain failures into success or a new domain failure.
*
* Runtime errors bypass the handler. The returned behavior replaces the source domain error type
* with the handler error type.
*
* @typeParam I - Input type.
* @typeParam O - Source output type.
* @typeParam O2 - Handler output type.
* @typeParam E - Source domain error type.
* @typeParam E2 - Handler domain error type.
* @typeParam C - Custom context type.
* @param source - Behavior whose domain failures should be recovered.
* @param handler - Callback invoked for source domain failures.
* @returns A behavior that can produce source output, handler output, or handler failure.
*
* @example
* ```ts
* const loadWithFallback = recover(loadUser, (_error, _input, context) =>
* context.ok({ id: "anonymous" }),
* );
* ```
*/
export function recover<I, O, O2, E, E2, C extends object>(
source: Behavior<I, O, E, C>,
handler: (error: E, input: I, context: TaskContext<C>) => Awaitable<Result<O2, E2>>,
): Behavior<I, O | O2, E2, C> {
return async (input, context) => {
const result = await runBehavior(source, input, context);
if (result.ok) {
return result;
}
if (isRuntimeError(result.error)) {
return err(result.error);
}
return task((_input: I, taskContext: TaskContext<C>) =>
handler(result.error as E, input, taskContext),
)(input, context);
};
}

Update the import paths to match your project setup.

Recover from typed domain errors by returning a success or a new typed failure.

Usage

recover handles expected domain failures from a source Behavior. It does not catch runtime errors, so cancellation and thrown exceptions stay visible.

Use it when:

  • Provide fallback data for a domain failure.
  • Convert one domain failure into another domain failure.
  • Continue a pipeline after a known recoverable error.
import { recover } from "@/lib/flow/recover";
import { task } from "@/lib/flow/task";
type CacheError = { kind: "cache-miss" };
const readCache = task<string, string, CacheError>((key, context) =>
key === "profile" ? "cached profile" : context.fail({ kind: "cache-miss" }),
);
const readCacheOrDefault = recover(readCache, (_error, key, context) =>
context.ok(`default for ${key}`),
);

Behavior

If the source succeeds, the result passes through. If the source returns a domain failure, recover invokes the handler as a task.

Only domain errors reach the handler. Runtime errors and cancellation are returned unchanged so recovery does not hide operational failures.

Types

The handler output is unioned with source output, and the handler error type replaces the source error type.

Import from @/lib/flow/recover.

Composition Notes

Use recover near the point where a fallback value or alternate workflow is valid.

Do not use recover when only one discriminated error kind should be handled; use catchKind.

Source

ts@lib/flow/recover.ts
import { type Awaitable, type Result, err } from "./core/result";
import { type Behavior, isRuntimeError, runBehavior } from "./core/runtime";
import { type TaskContext, task } from "./task";
/**
* Recovers expected domain failures into success or a new domain failure.
*
* Runtime errors bypass the handler. The returned behavior replaces the source domain error type
* with the handler error type.
*
* @typeParam I - Input type.
* @typeParam O - Source output type.
* @typeParam O2 - Handler output type.
* @typeParam E - Source domain error type.
* @typeParam E2 - Handler domain error type.
* @typeParam C - Custom context type.
* @param source - Behavior whose domain failures should be recovered.
* @param handler - Callback invoked for source domain failures.
* @returns A behavior that can produce source output, handler output, or handler failure.
*
* @example
* ```ts
* const loadWithFallback = recover(loadUser, (_error, _input, context) =>
* context.ok({ id: "anonymous" }),
* );
* ```
*/
export function recover<I, O, O2, E, E2, C extends object>(
source: Behavior<I, O, E, C>,
handler: (error: E, input: I, context: TaskContext<C>) => Awaitable<Result<O2, E2>>,
): Behavior<I, O | O2, E2, C> {
return async (input, context) => {
const result = await runBehavior(source, input, context);
if (result.ok) {
return result;
}
if (isRuntimeError(result.error)) {
return err(result.error);
}
return task((_input: I, taskContext: TaskContext<C>) =>
handler(result.error as E, input, taskContext),
)(input, context);
};
}