Genel bakış

Getting Started

Install the complete typed async workflow library.

Flow is a small TypeScript workflow layer for composing async application behavior with typed success, typed failure, and cooperative cancellation.

Why Flow

Async application code often mixes expected failures, thrown exceptions, timeout handling, and cancellation in one control path. Flow separates those concerns: domain failures are explicit Result values, runtime failures are preserved as runtime errors, and cancellation travels through AbortSignal.

Use Flow when you want workflows that remain easy to compose, test, and reason about after they grow beyond one async function.

For more context on the design tradeoffs, read the Flow Motivation.

Mental Model

A Flow unit is a Behavior<I, O, E, C>. It receives input I, returns output O, can fail with domain error E, and can read context C. Every Behavior receives a RuntimeContext containing an AbortSignal, so parent workflows, timeouts, races, and manual aborts can stop work cooperatively.

Composition helpers connect Behavior values without erasing their types. Sequential helpers pass successful output forward. Parallel helpers preserve object keys. Recovery helpers operate only on expected domain failures.

Install

The shadcn CLI reads a project-root components.json to decide where files go. Flow items are registry:lib sources; targets resolve through the lib alias (@lib/…aliases.lib).

If the project already uses shadcn/ui, you already have this file. Otherwise create a minimal components.json with only the schema-required fields (style, tailwind, rsc, aliases):

{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tailwind": {
"config": "",
"css": "src/styles.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"lib": "@/lib"
}
}

aliases requires components and utils. Add lib so Flow installs under @/lib/flow/*. Point TypeScript path aliases the same way, for example "@/*": ["./src/*"] in tsconfig.json.

Install the complete Flow bundle when you want every helper at once.

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

Install a single item when you only need one helper.

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

Files land under @/lib/flow/* (for example @/lib/flow/task).

Quick Start

import { map, pipe, retry, task, timeout } from "@/lib/flow";
type ProfileError = { kind: "not-found" } | { kind: "rate-limited" };
const loadProfile = task<string, { id: string; name: string }, ProfileError>(
async (id, context) => {
const response = await fetch(`/api/profiles/${id}`);
if (response.status === 404) return context.fail({ kind: "not-found" });
if (response.status === 429) return context.fail({ kind: "rate-limited" });
return response.json();
},
);
const toName = task((profile: { id: string; name: string }) => profile.name);
const workflow = pipe(
retry(loadProfile, {
attempts: 3,
delay: 200,
when: (error) => error.kind === "rate-limited",
}),
toName,
);
const boundedWorkflow = timeout(workflow, 1000);

Core Concepts

Flow is built around a small set of types. Behavior describes executable workflow units, Result describes explicit outcomes, and RuntimeContext carries cancellation plus optional application context.

Expected failures should be returned as typed domain errors with context.fail, err, or another Result helper. They stay visible in TypeScript and compose through helpers such as sequence, pipe, all, recover, and mapError.

Thrown exceptions are runtime failures. Flow captures them as Thrown so programming errors and unexpected platform failures do not get confused with domain errors.

Every Behavior runs with an AbortSignal. Helpers check the signal before starting work and pass linked child signals to nested work where needed. timeout, race, and all abort child controllers when continuing work is no longer useful.

Cancellation is cooperative: code that performs long-running work should observe context.signal or use helpers such as wait, timeout, and delay.

Use task to bring application code into Flow. Use sequence or pipe for dependent steps. Use map and tap for success-only transforms and effects. Use recover, catchKind, and mapError for expected domain failures. Use all, allSettled, and race for keyed parallel work.

Learning Path

  1. Task
  2. Input, Succeed, and Fail
  3. Map, Tap, Sequence, and Pipe
  4. Choose and When
  5. All, All Settled, and Race
  6. Wait, Timeout, Delay, and Retry
  7. Map Error, Recover, and Catch Kind

FAQ

For installation choices, cancellation details, and helper comparisons, read the Flow FAQ.