ActionBuilder
@rotorsoft/act-root / act/src / ActionBuilder
Type Alias: ActionBuilder<TState, TEvents, TActions, TName>
ActionBuilder<
TState,TEvents,TActions,TName> =object
Defined in: libs/act/src/state-builder.ts:137
Builder interface for defining actions (commands) on a state.
Actions represent user/system intents to modify state. Each action is validated against a schema, can have business rule invariants, and must emit one or more events.
See
state for complete usage examples
Type Parameters
TState
TState extends Schema
State schema type
TEvents
TEvents extends Schemas
Event schemas type
TActions
TActions extends Schemas
Action schemas type
TName
TName extends string = string
State name literal type
Properties
build()
build: () =>
State<TState,TEvents,TActions,TName>
Defined in: libs/act/src/state-builder.ts:336
Finalizes and builds the state definition.
Call this method after defining all actions, invariants, and patches to create the complete State object that can be registered with Act.
Returns
State<TState, TEvents, TActions, TName>
The complete strongly-typed State definition
Example
const Counter = state({ Counter: schema })
.init(() => ({ count: 0 }))
.emits({ Incremented: z.object({ amount: z.number() }) })
.patch({ Incremented: ({ data }, state) => ({ count: state.count + data.amount }) })
.on({ increment: z.object({ by: z.number() }) })
.emit((action) => ["Incremented", { amount: action.by }])
.build(); // Returns State<TState, TEvents, TActions, TName>
on()
on: <
TKey,TNewActions>(entry) =>object
Defined in: libs/act/src/state-builder.ts:182
Defines an action (command) that can be executed on this state.
Actions represent intents to change state - they should be named in imperative form (e.g., "CreateUser", "IncrementCounter", "PlaceOrder"). Actions are validated against their schema and must emit at least one event.
Pass a { ActionName: schema } record — use shorthand { ActionName }
when the variable name matches the action name. The key becomes the
action name, the value the Zod schema.
Type Parameters
TKey
TKey extends string
Action name (string literal type)
TNewActions
TNewActions extends Schema
Action payload schema type
Parameters
entry
ActionEntry<TKey, TNewActions>
Single-key record { ActionName: schema }
Returns
An object with .given() and .emit() for further configuration
emit()
emit: (
handler) =>ActionBuilder<TState,TEvents,TActions&{ [P in TKey]: TNewActions },TName>
Defines the action handler that emits events.
The handler receives the action payload and current state snapshot,
and must return one or more events to emit. Return a single event as
["EventName", data] or multiple events as an array of event tuples.
Pass a string event name for passthrough: the action payload becomes the event data directly.
Parameters
handler
Function that returns events to emit, or event name string for passthrough
ActionHandler<TState, TEvents, { [P in TKey]: TNewActions }, TKey> | keyof TEvents & string
Returns
ActionBuilder<TState, TEvents, TActions & { [P in TKey]: TNewActions }, TName>
The ActionBuilder for chaining more actions
Examples
.emit("Incremented")
.emit((action) => ["Incremented", { amount: action.by }])
.emit((action) => [
["Incremented", { amount: action.by }],
["LogUpdated", { message: `Incremented by ${action.by}` }]
])
given()
given: (
rules) =>object
Adds business rule invariants that must hold before the action can execute.
Invariants are checked after loading the current state but before emitting events.
Each invariant should return true or an error message string. All invariants
must pass for the action to succeed.
Parameters
rules
Invariant<TState>[]
Array of invariant functions
Returns
An object with .emit() to finalize the action
emit()
emit: (
handler) =>ActionBuilder<TState,TEvents,TActions&{ [P in TKey]: TNewActions },TName>
Defines the action handler that emits events.
The handler receives the action payload and current state snapshot, and must return one or more events to emit. Events are applied to state via the patch handlers defined earlier.
Pass a string event name for passthrough: the action payload becomes the event data directly.
Parameters
handler
Function that returns events to emit, or event name string for passthrough
ActionHandler<TState, TEvents, { [P in TKey]: TNewActions }, TKey> | keyof TEvents & string
Returns
ActionBuilder<TState, TEvents, TActions & { [P in TKey]: TNewActions }, TName>
The ActionBuilder for chaining more actions
Examples
.emit((action, snapshot) => {
const newBalance = snapshot.state.balance + action.amount;
return ["Deposited", { amount: action.amount, newBalance }];
})
.emit("TicketAssigned")
Example
.given([
(_, snap) => snap.state.status === "active" || "Must be active",
(target, snap) => snap.state.ownerId === target.actor.id || "Not authorized"
])
Examples
.on({ increment: z.object({ by: z.number() }) })
.emit((action) => ["Incremented", { amount: action.by }])
.on({ withdraw: z.object({ amount: z.number() }) })
.given([
(_, snap) => snap.state.balance >= 0 || "Account closed",
(_, snap, action) => snap.state.balance >= action.amount || "Insufficient funds"
])
.emit((action) => ["Withdrawn", { amount: action.amount }])
const OpenTicket = z.object({ title: z.string() });
.on({ OpenTicket })
.emit((action) => ["TicketOpened", { title: action.title }])
snap()
snap: (
snap) =>ActionBuilder<TState,TEvents,TActions,TName>
Defined in: libs/act/src/state-builder.ts:314
Defines a snapshotting strategy to optimize state reconstruction.
Snapshots store the current state at a point in time, allowing faster state loading
by avoiding replaying all events from the beginning. The snap function is called
after each event is applied and should return true when a snapshot should be taken.
Parameters
snap
(snapshot) => boolean
Predicate function that returns true when a snapshot should be taken
Returns
ActionBuilder<TState, TEvents, TActions, TName>
The ActionBuilder for chaining
Examples
.snap((snapshot) => snapshot.patches >= 10)
.snap((snapshot) => {
const estimatedSize = JSON.stringify(snapshot.state).length;
return estimatedSize > 10000 || snapshot.patches >= 50;
})
.snap((snapshot) => {
const hoursSinceLastSnapshot = snapshot.patches * 0.1; // Estimate
return hoursSinceLastSnapshot >= 24;
})