ActionBuilder
@rotorsoft/act-root / act/src / ActionBuilder
Type Alias: ActionBuilder<TState, TEvents, TActions, TName, TSnap>
ActionBuilder<
TState,TEvents,TActions,TName,TSnap> =object
Defined in: libs/act/src/types/builders.ts:760
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 declare preconditions, and emits zero 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
TSnap
TSnap extends boolean = false
true once .snap(...) has been called. Gates the
.autocloses({ keep }) rolling-window option — a windowed close is
meaningless without snapshots, so keep only typechecks after
.snap in the chain.
Properties
archives
archives: (
archive) =>ActionBuilder<TState,TEvents,TActions,TName,TSnap>
Defined in: libs/act/src/types/builders.ts:938
Declares the archiver the online close cycle runs before
truncating a stream this state's .autocloses(...) predicate
accepted. Hosts use it to write events to durable storage (S3,
an analytics warehouse, cold tier) before the tombstone lands,
so the truncate doesn't lose history that the operator still
needs.
Threads into CloseTarget.archive via the same plumbing
app.close({ stream, archive }) already uses — the cycle holds
the stream's guard while the archiver runs, and a thrown
archiver leaves the stream guarded but un-truncated. No partial
truncate state, no data loss. It is not retried automatically;
app.close([{ stream, archive }]) resumes the guarded stream.
One archiver per state. A second .archives(...) call replaces
the first (same shape as .snap / .discloses /
.autocloses). Absent → the cycle truncates without an archive
step.
Parameters
archive
AutocloseArchiver<TEvents>
(stream, head) => Promise<void>. Runs while
the stream is locked against new writes; the truncate runs
immediately after a successful resolve.
Returns
ActionBuilder<TState, TEvents, TActions, TName, TSnap>
The ActionBuilder for chaining.
Example
Archive to S3 before truncate.
state({ Ticket: ticketSchema })
.emits({ TicketOpened, TicketResolved })
// ...
.autocloses({ is: "TicketResolved" })
.archives(async (stream) => {
const events = await loadEvents(stream);
await s3.upload(`tickets/${stream}.jsonl`, events);
})
autocloses
autocloses: (
policy) =>ActionBuilder<TState,TEvents,TActions,TName,TSnap>
Defined in: libs/act/src/types/builders.ts:890
Declares the online close predicate for this state. The
orchestrator's autoclose cycle iterates the state's streams once
per tick and calls the predicate per candidate; truthy results are
scheduled for atomic truncate-and-seed via Store.truncate on the
next batch.
One predicate per state. A second .autocloses(...) call replaces
the first (same shape as .snap / .discloses — state-level, not
per-event). Absent → the state opts out of online close entirely;
the cycle skips it and pays zero per-tick cost for it.
Pass a declarative AutoclosePolicy object literal covering the
three operational pressure points (after, is, reaches). Top-level
fields combine with AND; an optional or: {...} block opens an
alternative OR path. Validated via Zod at build time; misconfiguration
throws before act().build() completes.
Under the hood this compiles to a synthesized reaction that runs
on a per-aggregate synthetic stream: it defers to head.created + the policy's min after while the cooldown holds and closes the stream once
the policy matches. There is no background sweep.
There is no function-predicate form: an opaque predicate has no
derivable due-time or terminal event to react to. For conditions the
declarative form can't express, call app.close(...) from your own logic.
Parameters
policy
[TSnap] extends [true] ? AutoclosePolicy : Omit<AutoclosePolicy, "keep"> & object
The declarative AutoclosePolicy bag.
Returns
ActionBuilder<TState, TEvents, TActions, TName, TSnap>
The ActionBuilder for chaining.
Example
Declarative — cooldown after terminal (a Ticket closes
90 days after resolution).
.autocloses({ is: "TicketResolved", after: { days: 90 } })
build
build: () =>
State<TState,TEvents,TActions,TName>
Defined in: libs/act/src/types/builders.ts:960
Finalizes and builds the state definition.
Call this method after defining all actions, preconditions, 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>
discloses
discloses: (
disclose) =>ActionBuilder<TState,TEvents,TActions,TName,TSnap>
Defined in: libs/act/src/types/builders.ts:848
Declares the disclosure predicate for sensitive(...)-marked event
fields. Gates external reads: returning true allows the actor to see
plaintext on the event; returning false substitutes "[REDACTED]".
When absent, the framework default-denies on every external read —
fail-safe.
One predicate per state. A second .discloses(...) call replaces the
first (same shape as snapshots being state-level, not per-event).
The predicate receives the full event including merged PII so it can
branch on the payload itself (e.g.
event.data.ownerId === actor.id). Reducers, projections, and
reactions are unaffected — they follow separate visibility rules.
Parameters
disclose
(event, actor) => boolean
Predicate (event, actor) => boolean. true =
plaintext, false = "[REDACTED]" substitution.
Returns
ActionBuilder<TState, TEvents, TActions, TName, TSnap>
The ActionBuilder for chaining.
Example
Owner-or-admin disclosure
state({ User: userSchema })
.init(() => ({ ... }))
.emits({ UserRegistered: z.object({ email: sensitive(z.string()) }) })
.discloses((event, actor) =>
actor.id === event.stream || actor.roles?.includes("admin"))
on
on: <
TKey,TNewActions>(entry,options?) =>ActionRules<TState,TEvents,TActions,TName,TSnap,TKey,TNewActions>
Defined in: libs/act/src/types/builders.ts:798
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 emit zero or more events (none commits nothing).
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.
Pass an optional second argument to declare a per-action retry
policy — the orchestrator retries this action on
ConcurrencyError up to maxRetries extra times, applying
backoff between attempts when set. Omit the argument to keep the
current single-attempt behavior (ConcurrencyError surfaces on
first conflict).
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 }
options?
Optional per-action retry policy (ActionOptions).
Returns
ActionRules<TState, TEvents, TActions, TName, TSnap, TKey, TNewActions>
An object with .loads(), .requires() and .emit() for further configuration
Example
Simple action without preconditions
.on({ increment: z.object({ by: z.number() }) })
.emit((action) => ["Incremented", { amount: action.by }])
snap
snap: (
snap) =>ActionBuilder<TState,TEvents,TActions,TName,true>
Defined in: libs/act/src/types/builders.ts:817
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, true>
The ActionBuilder for chaining
Example
Snapshot every 10 events
.snap((snapshot) => snapshot.patches >= 10)