A c t
TypeScript-first Event Sourcing Framework
Build robust, auditable, and reactive systems with composable state machines, pure functions, and zero runtime bloat.
Install
npm install @rotorsoft/act
Minimal App
import { act, state } from "@rotorsoft/act";
import { z } from "zod";
const Counter = state("Counter", z.object({ count: z.number() }))
  .init(() => ({ count: 0 }))
  .emits({ Incremented: z.object({ amount: z.number() }) })
  .patch({ Incremented: (event, state) => ({ count: state.count + event.data.amount }) })
  .on("increment", z.object({ by: z.number() }))
  .emit((action, state) => ["Incremented", { amount: action.by }])
  .build();
const app = act().with(Counter).build();
await app.do("increment", { stream: "counter1", actor: { id: "1", name: "User" } }, { by: 1 });
console.log(await app.load(Counter, "counter1"));
Run & Explore
Run your app and see the output in your terminal or browser console.
Get Started
The interactive sandbox is available only on desktop Chrome browsers.

Try it in your browser

Open in StackBlitz Source on GitHub

Functional Event Sourcing

Every state change is a pure function of previous state and events. Immutability and replayability by design.

Composable State Machines

Model your domain as composable, strongly-typed state machines. No classes, just functions and data.

TypeScript Native

Type safety and inference everywhere. Catch errors at compile time, not at runtime.

Reactive by Default

Reactions let you build event-driven flows and side effects with ease.

Postgres Adapter Included

Production-ready Postgres adapter for scalable, persistent event storage. Switch between in-memory and Postgres with a single line of code.

Minimal Footprint

Minimal and dependency-light. No codegen, no runtime bloat, and a tiny bundle size.