Skip to main content

ActionBuilder

@rotorsoft/act-root


@rotorsoft/act-root / act/src / ActionBuilder

Type Alias: ActionBuilder<TState, TEvents, TActions, TName>

ActionBuilder<TState, TEvents, TActions, TName> = object

Defined in: libs/act/src/builders/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/builders/state-builder.ts:368

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/builders/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 string]: TNewActions }, TName>; (eventName): ActionBuilder<TState, TEvents, TActions & { [P in string]: TNewActions }, TName>; }

Defines the action handler that emits events. Same two overloads as the post-.given() form above:

  • Function โ€” receives (action, snapshot) and returns one or more [EventName, data] tuples (or undefined).
  • String โ€” passthrough: the action payload becomes the event data directly. Must reference an event declared in .emits().

The two overloads are kept separate (rather than merged into a handler | string union) so that TS contextual typing of the function alternative isn't degraded by considering the string branch โ€” under the union form TState could collapse to its Schema constraint inside the callback.

Call Signatureโ€‹

(handler): ActionBuilder<TState, TEvents, TActions & { [P in string]: TNewActions }, TName>

Parametersโ€‹
handlerโ€‹

ActionHandler<TState, TEvents, { [P in string]: TNewActions }, TKey>

Returnsโ€‹

ActionBuilder<TState, TEvents, TActions & { [P in string]: TNewActions }, TName>

Call Signatureโ€‹

(eventName): ActionBuilder<TState, TEvents, TActions & { [P in string]: TNewActions }, TName>

Parametersโ€‹
eventNameโ€‹

keyof TEvents & string

Returnsโ€‹

ActionBuilder<TState, TEvents, TActions & { [P in string]: TNewActions }, TName>

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 string]: TNewActions }, TName>; (eventName): ActionBuilder<TState, TEvents, TActions & { [P in string]: 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.

Call Signatureโ€‹

(handler): ActionBuilder<TState, TEvents, TActions & { [P in string]: TNewActions }, TName>

Custom handler โ€” receives (action, snapshot) and returns one or more [EventName, data] tuples (or undefined).

Parametersโ€‹
handlerโ€‹

ActionHandler<TState, TEvents, { [P in string]: TNewActions }, TKey>

Returnsโ€‹

ActionBuilder<TState, TEvents, TActions & { [P in string]: TNewActions }, TName>

Call Signatureโ€‹

(eventName): ActionBuilder<TState, TEvents, TActions & { [P in string]: TNewActions }, TName>

Passthrough โ€” the action payload becomes the event data directly. Must reference an event declared in .emits().

Parametersโ€‹
eventNameโ€‹

keyof TEvents & string

Returnsโ€‹

ActionBuilder<TState, TEvents, TActions & { [P in string]: TNewActions }, TName>

Paramโ€‹

Function that returns events to emit, or event name string for passthrough

Returnsโ€‹

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/builders/state-builder.ts:346

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;
})