DBDelve
Dive deep into your data. A fast, native, multi-engine database client written in Rust. Free and open source, no telemetry.
翻訳待ち
Loading the full README. The saved excerpt is shown below.
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.Read the full README ↗Act is an event-sourcing framework for TypeScript built on three primitives: actions, state, and reactions. An action commits events, state folds them, and reactions run on what was committed. Pick a store at bootstrap (Postgres for production, SQLite for embedded, in-memory for tests); the application code stays the same.
The WolfDesk help desk, animated from its builders: actions pass the state's ▲ preconditions and commit events, projections fold them, and reactions pick them up and call more actions. Play it live, or see how it's made.
The primitives:
| Action | The change you want to make. Validated against a Zod schema, checked against named preconditions (rules on the state, the actor and the payload), emitted as zero or more immutable events that must keep the state's invariants (its schema), 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. |
npm i @rotorsoft/act zod # ESM only: "type": "module" in package.json
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({ by: z.number() }) })
.patch({ Incremented: ({ data }, s) => ({ count: s.count + data.by }) })
.on({ increment: z.object({ by: z.number() }) })
.emit("Incremented")
.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 }
Next, hello.ts adds a reaction to this counter (pnpm -F calculator dev:hello).
New in 2.0. A rule that spans streams, such as a unique email or a limited number of seats, needs no saga. An action loads the other stream with .loads, decides, and commits to every stream at once: all of it lands or none of it does.
.on({ Enroll })
.loads(Seats, (data) => `course-${data.course}`, ["SeatTaken"])
.requires([{ description: "a free seat", holds: (_, __, ___, { Seats }) => Seats.taken < Seats.capacity }])
.emit((data) => [["Enrolled", data], ["SeatTaken", {}]])
Every pattern, what a race does and what it costs: Rules across streams. Watch it play.
| 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. Other processes see a commit through notify (Postgres LISTEN/NOTIFY), or through an opt-in poll (start_correlations, or a lane's cycleMs); without either, an idle worker waits for its own next commit. See cross-process reactions. |
| 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. |
This is the beginning of the README. Read the rest at the original. Read the full README ↗