Skip to content

Latest commit

 

History

3,801 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status Store Conformance Coverage Status Repo Size

Act — Fluent Event Sourcing for TypeScript Practical Event Sourcing in TypeScript — Book Cover

Get Started Documentation   Get the Book

What it is

Act is an event-sourcing framework for TypeScript. The domain is expressed through three composable primitives: actions, state, and reactions. An action validates input against a Zod schema, commits one or more events under optimistic concurrency, and reduces them into derived state via a patch handler. Reactions fire on commit, drain in order, retry under back-pressure, and surface to the operator when something downstream wedges. The framework wires the rest of the pipeline: snapshots and a cache layer for fast cold loads, correlation across stream boundaries, a recovery API for blocked streams, time-travel queries against the same log. Pick a store at bootstrap. Postgres for production, SQLite for embedded, in-memory for tests. The application code stays the same.

   ┌─────────────┐    action     ┌─────────────────┐    reaction      ┌──────────────┐
   │             │ ────────────► │       Act       │ ───────────────► │              │
   │   client    │               │  events, state, │                  │  downstream  │
   │             │ ◄──────────── │  drain, recover │                  │              │
   └─────────────┘    load()     └─────────────────┘                  └──────────────┘

The primitives:

Action The change you want to make. Validated against a Zod schema, emitted as one or more immutable events, committed under optimistic concurrency.
State The data you care about. Defined as a Zod schema, evolved by emit-and-patch, served back through load() with snapshot and cache layers in front of replay.
Reaction What happens as a result. Fires in commit order, retried under back-pressure with configurable backoff, blocked-stream surfaced to operators when a downstream wedges.

30-second demo

Prefer a running project over a snippet? npx degit rotorsoft/act-starter my-app — a minimal, commented starter with a state, a projection, a reaction, a generated REST API, and tests (Rotorsoft/act-starter).

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: ({ data }, s) => ({ count: s.count + data.amount }) })
  .on({ increment: z.object({ by: z.number() }) })
  .emit((action) => ["Incremented", { amount: action.by }])
  .build();

const app = act().withState(Counter).build();
await app.do("increment", { stream: "c1", actor: { id: "1", name: "u" } }, { by: 5 });

const snap = await app.load(Counter, "c1");
console.log(snap.state); // { count: 5 }

What's in the box

Production stores Postgres, SQLite, and in-memory all pass the same runStoreTck. Application code doesn't change between them; only the bootstrap line differs.
Zod end to end Schemas define every action, event, and state shape at runtime and generate the TypeScript types at compile time. One source of truth, full inference into reducers, projections, and queries.
No external broker The event store carries the message-bus role. Postgres exposes cross-process wakeups via LISTEN/NOTIFY; the orchestrator falls back to a polling debounce when the hook isn't there, so correctness is preserved either way.
HTTP integrations Outbound webhook reaction helper with auto Idempotency-Key, status-classified retries, and a published receiver-side dedup contract. SSE for incremental state broadcast lives on the same subpath.
Live inspector A web app you point at any Act store. Browse the event log, watch correlation and drain in real time, inspect blocked streams, page through subscription positions.
Interactive diagrams An SVG of the domain model with click-through to source, plus the act CLI that walks the same content in the terminal.
Time-travel app.load(State, id, _, { before: N }) reconstructs state at any historical event id or timestamp through the same call you use for the current state.
Recovery loop app.blocked_streams() surfaces what's wedged. app.unblock(...) resumes from the watermark without replaying history; app.reset(...) rebuilds projections from scratch.
AI scaffolding The bundled Claude Code skill turns a functional spec into a working monorepo. Domain, tRPC API, React client, vitest.

Packages

Core

Package Description
@rotorsoft/act
npm downloads
The framework. State, actions, reactions, slices, projections, the correlate / drain / settle loop, snapshots, cache, recovery. Zod-typed end to end.
@rotorsoft/act‑pg
npm downloads
Postgres store. Atomic stream claiming via FOR UPDATE SKIP LOCKED, connection pooling, optional LISTEN/NOTIFY for cross-process wakeups.
@rotorsoft/act‑sqlite
npm downloads
libSQL store for single-node and edge deployments.
@rotorsoft/act‑patch
npm downloads
Immutable deep-merge patch utility used by state reducers. Zero dependencies, browser-safe.
@rotorsoft/act‑crypto
npm downloads
Authenticated envelope encryption (AES-256-GCM, versioned wire format) for adapters that want column-level encryption with operator-controlled keys.

Integrations

Package Description
@rotorsoft/act‑http
npm downloads
Outbound webhook helper with auto Idempotency-Key, status-classified retries, and a published receiver-side dedup contract. The /sse subpath broadcasts incremental state to live UIs.
@rotorsoft/act‑ops
npm downloads
Operational primitives for Act apps and act-independent receivers — idempotency, retry-budget sizing, poison-message classification. No peer dep on @rotorsoft/act, so non-Act consumers can speak the same contract without the orchestrator.
@rotorsoft/act‑pino
npm downloads
Pino logger adapter for transports, redaction, async sinks.
@rotorsoft/act‑otel
npm downloads
Prometheus metrics bridge over the lifecycle events.
@rotorsoft/act‑notify
npm downloads
Notify-broker decorator — ride Redis (or any broker) for cross-process wakeups.
@rotorsoft/act‑diagram
npm downloads
Interactive SVG of the domain model with click-through to source. Also ships the act CLI for the same content in the terminal.
@rotorsoft/act‑tck
npm downloads
Executable conformance kit for Store, Cache, and Logger ports. Third-party adapters validate themselves against it.

Workspace apps (not on npm)

Package Description
@rotorsoft/act‑inspector Web app you point at any Act store. Browse the event log, watch correlation and drain in real time, inspect blocked streams, page through subscription positions.

AI-assisted scaffolding

The repo ships a Claude Code skill at .claude/skills/scaffold-act-app. Drop a functional spec into Claude Code and ask it to build the app: event-modeling diagrams, event-storming boards, JSON configs, user stories, and prose all work. The skill maps the spec's vocabulary into framework concepts (aggregates into states, commands into actions, policies into reactions, read models into projections), scaffolds the monorepo, and walks the build process end to end with production guidance for Postgres, background processing, automated jobs, and error handling.

To install:

# In the project root
mkdir -p .claude/skills
cp -r /path/to/act-root/.claude/skills/scaffold-act-app .claude/skills/

# Or globally for all your projects
cp -r /path/to/act-root/.claude/skills/scaffold-act-app ~/.claude/skills/

Then ask Claude Code: "Build me an app from this spec: <link-or-file>".

Quality signals

100% statement, branch, function, and line coverage on every PR. Property-based tests cover commit version monotonicity, claim/lease lifecycle, cache/store coherence, correlate→drain delivery exactness, and close idempotency. A CI bench fails the build when any scenario's p50 regresses past 1.5× the checked-in baseline; numbers are in PERFORMANCE.md. The three in-tree stores (Postgres, SQLite, InMemory) pass the same TCK and the conformance workflow runs on every PR. Public API stability is governed by STABILITY.md: breaking changes require an explicit BREAKING CHANGE: footer and a written migration note.

Documentation

  • Get started — walkthrough from install to a working app
  • Concepts — state management, event sourcing, error handling, real-time, testing, configuration
  • Architecture — concurrency model, cache and snapshots, correlation and drain, cross-process reactions, priority lanes, close cycle, schema evolution, extension points
  • Guides — production checklist, projections to a database, external integration, writing a custom store/cache/logger, contributing a new package
  • API reference — typedoc, refreshed on every push to master
  • Performance — throughput numbers per store, CI regression guard, optimization history
  • Philosophy — DDD / Event Sourcing / CQRS lineage, integration patterns, why this shape
  • The bookPractical Event Sourcing in TypeScript, Event Sourcing / CQRS / DDD applied end to end through a multiplayer Risk game

Examples

  • Calculator — actions are key presses, a digit board tracks how many times each digit has been pressed. The hello-world for the framework.
  • WolfDesk — reference implementation of the WolfDesk ticketing system from Vlad Khononov's Learning Domain-Driven Design. Multi-slice domain, real workflows, blocked-stream recovery, webhook integration.
  • Multi-transport HTTP demo — exposes the calculator over tRPC, Hono REST, and OpenAPI on a single Hono root, all generated from one Act registry via @rotorsoft/act-http. The Vite client toggles between transports against the same stream. See the auto-generated API guide for the narrative; the shape the AI-scaffolding skill produces.

Contributing

Fork, branch, install (pnpm install), test (pnpm test), lint (pnpm lint), commit, push, PR. Conventional commits. 100% coverage gate. The full pre-handoff workflow lives in CLAUDE.md; the per-package contributing guide is in docs/docs/guides/contributing-new-package.md. Open an issue or join GitHub Discussions for questions.

Versioning

SemVer. What semver protects and what it doesn't is in STABILITY.md, which also publishes the support window (which majors are maintained, for how long), the deprecation policy, and the security-fix policy. Release notes and breaking changes are in CHANGELOG.md.

License

MIT

About

Event-sourcing framework for TypeScript. Three primitives (actions, state, reactions), Zod end to end, no broker required. Postgres, SQLite, or in-memory.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages