Обзор

Task

Create typed async behaviors that can return plain values or Results.

asyncresulttyped-errors

Install

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

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

ts@lib/flow/task.ts
import { type Awaitable, type Result, err, isResult, ok } from "./core/result";
import { type Behavior, type RuntimeContext, define } from "./core/runtime";
/**
* Runtime context provided to a `task` callback.
*
* The context includes the standard Flow runtime context plus helpers for creating explicit
* success and failure results.
*
* @typeParam C - Custom context object type.
*/
export type TaskContext<C extends object = {}> = RuntimeContext<C> & {
ok: typeof ok;
succeed: typeof ok;
fail: typeof err;
err: typeof err;
};
/**
* Return type accepted from a `task` callback.
*
* Plain values are treated as success. Branded `Result` values are preserved.
*
* @typeParam O - Output type.
* @typeParam E - Expected domain error type.
*/
export type TaskReturn<O, E> = O | Result<O, E>;
/**
* Creates a typed behavior from synchronous or asynchronous application code.
*
* `task` is the main authoring boundary for Flow programs. Return plain values for success or use
* `context.fail`/`context.err` for typed domain failures. Thrown exceptions are captured by the
* runtime as `Thrown` errors.
*
* @typeParam I - Input type.
* @typeParam O - Output type.
* @typeParam E - Expected domain error type.
* @typeParam C - Custom context type.
* @param run - Callback invoked for each behavior execution.
* @returns A behavior that wraps plain values in `ok` and preserves explicit results.
*
* @example
* ```ts
* const loadUser = task<string, { id: string }, { kind: "missing" }>((id, context) => {
* if (!id) return context.fail({ kind: "missing" });
* return { id };
* });
* ```
*/
export function task<I, O, E = never, C extends object = {}>(
run: (input: I, context: TaskContext<C>) => Awaitable<TaskReturn<O, E>>,
): Behavior<I, O, E, C> {
return define(async (input: I, context: RuntimeContext<C>) => {
const result = await run(input, {
...context,
ok,
succeed: ok,
fail: err,
err,
});
return isResult(result) ? result : ok(result);
});
}

Update the import paths to match your project setup.

Create typed async Behavior values from ordinary functions while keeping expected failures in the Result channel.

Usage

task is the authoring boundary for most Flow programs. It turns an async or sync callback into a Behavior and gives the callback helpers for returning success or typed failure without throwing.

Use it when:

  • Wrap application code that may return a value, a Promise, or a Result.
  • Model domain failures with context.fail instead of exceptions.
  • Start a pipeline before composing with map, pipe, retry, or timeout.
import { task } from "@/lib/flow/task";
type UserError = { kind: "missing-id" };
const loadUser = task<string, { id: string }, UserError>(async (id, context) => {
if (!id) return context.fail({ kind: "missing-id" });
return { id };
});

Behavior

The callback runs once per Behavior execution. Flow awaits the result, wraps plain values in ok, and preserves explicit Result values so downstream helpers can keep the same success and failure types.

Use context.fail or context.err for expected domain failures. Thrown exceptions are converted to runtime Thrown errors by the runtime, and an already-aborted signal returns Cancelled before domain logic continues.

Types

Plain callback returns become Ok values, while explicit Result returns preserve their success and error types.

Import from @/lib/flow/task.

Composition Notes

Use task at application boundaries, then compose with pipe, retry, timeout, and recovery helpers.

Do not use task only to transform an existing behavior result; prefer map, tap, mapError, or recover.

Source

ts@lib/flow/task.ts
import { type Awaitable, type Result, err, isResult, ok } from "./core/result";
import { type Behavior, type RuntimeContext, define } from "./core/runtime";
/**
* Runtime context provided to a `task` callback.
*
* The context includes the standard Flow runtime context plus helpers for creating explicit
* success and failure results.
*
* @typeParam C - Custom context object type.
*/
export type TaskContext<C extends object = {}> = RuntimeContext<C> & {
ok: typeof ok;
succeed: typeof ok;
fail: typeof err;
err: typeof err;
};
/**
* Return type accepted from a `task` callback.
*
* Plain values are treated as success. Branded `Result` values are preserved.
*
* @typeParam O - Output type.
* @typeParam E - Expected domain error type.
*/
export type TaskReturn<O, E> = O | Result<O, E>;
/**
* Creates a typed behavior from synchronous or asynchronous application code.
*
* `task` is the main authoring boundary for Flow programs. Return plain values for success or use
* `context.fail`/`context.err` for typed domain failures. Thrown exceptions are captured by the
* runtime as `Thrown` errors.
*
* @typeParam I - Input type.
* @typeParam O - Output type.
* @typeParam E - Expected domain error type.
* @typeParam C - Custom context type.
* @param run - Callback invoked for each behavior execution.
* @returns A behavior that wraps plain values in `ok` and preserves explicit results.
*
* @example
* ```ts
* const loadUser = task<string, { id: string }, { kind: "missing" }>((id, context) => {
* if (!id) return context.fail({ kind: "missing" });
* return { id };
* });
* ```
*/
export function task<I, O, E = never, C extends object = {}>(
run: (input: I, context: TaskContext<C>) => Awaitable<TaskReturn<O, E>>,
): Behavior<I, O, E, C> {
return define(async (input: I, context: RuntimeContext<C>) => {
const result = await run(input, {
...context,
ok,
succeed: ok,
fail: err,
err,
});
return isResult(result) ? result : ok(result);
});
}