Composable HTTP controllers for TypeScript. Web-standards-first, framework-extensible. Pairs with composable-functions for business logic.
Status: v0.x — under active development. API is unstable and may change without notice.
| Package | Description |
|---|---|
@composable-controllers/core |
Generic types, parsers, the Step<T, R> algebra, response finalizers, and the controller(...) builder. Web-standards-only (Request → Response). |
@composable-controllers/react-router |
React Router v7+ adapter. Provides toAction/toLoader adapters, RR-shaped finalizers, and the act/load recipes. |
Controllers in remaster apps had drifted into a familiar shape: a 2-line shim that mounts framework context, delegates to a business Composable, and reshapes the Result for the wire — but every app re-implemented the wiring inline. This library extracts that discipline.
Three composition layers, deliberately separate:
- Business — write your domain logic as
composable-functionsComposablevalues. - Result-transformer algebra — observe a business
Result<T>and either pass it through or terminate with a response.Step<T, R>s compose; named helpers (redirectOnSuccess,surfaceError,notFoundIf,traceErrors) cover the common shapes. - Controller construction —
controller({ parse, run, transforms, finalize })assembles a typed parser, the business call, the transformer chain, and a final responder into a singleController<R, Ctx>.
Per-framework packs (just RR for now) layer on adapters and recipe-level conveniences.
// app/routes/users.new.tsx
import { act, redirectOnSuccess } from '@composable-controllers/react-router'
import { createUser, createUserSchema } from '~/business/users.server'
export const action = act(createUserSchema, createUser, {
transforms: [redirectOnSuccess(() => '/users')],
})A working app is in examples/react-router-app.
pnpm install
pnpm tsc # type-check all packages + example
pnpm test # run vitest in every package
pnpm build # build core, then the RR pack
pnpm lint # biome checkThe example app:
pnpm --filter @composable-controllers/example-react-router-app devBoots an RR v7 dev server. /users exercises the load recipe; /users/new exercises act end-to-end.
Releases are driven by Claude Code via .claude/skills/release/SKILL.md. The maintainer says "release"; the skill analyzes the diff, drafts a changeset, runs checks, commits, asks for a pnpm release (npm OTP required), and creates per-package GitHub releases.
MIT © Seasoned