Documentation
¶
Overview ¶
Package statekit provides a Go-native statechart execution engine.
Define and execute statecharts in Go — visualize them with built-in tools.
Features ¶
- Fluent Builder API
- Hierarchical States (Compound/Nested)
- History States (Shallow and Deep)
- Delayed Transitions (Timers)
- Parallel States (Orthogonal Regions)
- Reflection DSL (Struct tags)
- Native Visualization (HTML simulator, Mermaid, ASCII, TUI)
Quick Start ¶
machine, _ := statekit.NewMachine[struct{}]("traffic").
WithInitial("green").
State("green").On("TIMER").Target("yellow").Done().
State("yellow").On("TIMER").Target("red").Done().
State("red").On("TIMER").Target("green").Done().
Build()
interp := statekit.NewInterpreter(machine)
interp.Start()
interp.Send(statekit.Event{Type: "TIMER"})
Visualization ¶
Export your machine to Statekit Native JSON for visualization:
exporter := export.NewNativeExporter(machine)
jsonStr, _ := exporter.ExportJSONIndent("", " ")
fmt.Println(jsonStr)
Or use the CLI:
statekit viz --go-package . --format html -o machine.html
Package statekit provides event sourcing support for state machine persistence.
Example ¶
package main
import (
"fmt"
"go.klarlabs.de/statekit"
)
func main() {
// Create a simple traffic light state machine
machine, _ := statekit.NewMachine[struct{}]("traffic").
WithInitial("green").
State("green").On("TIMER").Target("yellow").Done().
State("yellow").On("TIMER").Target("red").Done().
State("red").On("TIMER").Target("green").Done().
Build()
interp := statekit.NewInterpreter(machine)
interp.Start()
fmt.Println(interp.State().Value)
interp.Send(statekit.Event{Type: "TIMER"})
fmt.Println(interp.State().Value)
}
Output: green yellow
Index ¶
- Constants
- Variables
- func FromStruct[M any, C any](registry *ActionRegistry[C]) (*ir.MachineConfig[C], error)
- func FromStructWithContext[M any, C any](registry *ActionRegistry[C], ctx C) (*ir.MachineConfig[C], error)
- type Action
- type ActionRegistry
- type ActionType
- type ActorID
- type ActorMetadata
- type ActorRef
- type ChooseBranch
- type Clock
- type ClusterMembership
- type ClusterNode
- type CompoundNode
- type ConsistentHashRouter
- type DistributedInterpreter
- func (di *DistributedInterpreter[C]) Commit(ctx context.Context) (int, error)
- func (di *DistributedInterpreter[C]) Context() C
- func (di *DistributedInterpreter[C]) Done() bool
- func (di *DistributedInterpreter[C]) ForceSnapshot(ctx context.Context) error
- func (di *DistributedInterpreter[C]) LockHeld() bool
- func (di *DistributedInterpreter[C]) LockLost() <-chan struct{}
- func (di *DistributedInterpreter[C]) Matches(stateID StateID) bool
- func (di *DistributedInterpreter[C]) Send(event Event) error
- func (di *DistributedInterpreter[C]) SendAll(events []Event) error
- func (di *DistributedInterpreter[C]) State() State[C]
- func (di *DistributedInterpreter[C]) Stop(ctx context.Context) error
- func (di *DistributedInterpreter[C]) StreamID() string
- func (di *DistributedInterpreter[C]) UncommittedCount() int
- func (di *DistributedInterpreter[C]) Version() int
- type DistributedInterpreterOption
- type ErrConcurrencyConflict
- type Event
- type EventStore
- type EventType
- type FakeClock
- type FinalNode
- type Guard
- type GuardType
- type HistoryBuilder
- type HistoryType
- type Interpreter
- func NewInterpreter[C any](machine *ir.MachineConfig[C], opts ...Option[C]) *Interpreter[C]
- func ReplayEvents[C any](ctx context.Context, machine *ir.MachineConfig[C], eventStore EventStore, ...) (*Interpreter[C], error)
- func ReplayToVersion[C any](ctx context.Context, machine *ir.MachineConfig[C], eventStore EventStore, ...) (*Interpreter[C], error)
- func (i *Interpreter[C]) Close() error
- func (i *Interpreter[C]) Done() bool
- func (i *Interpreter[C]) GetActor(id ActorID) *ActorRef
- func (i *Interpreter[C]) HasTag(tag string) bool
- func (i *Interpreter[C]) Matches(id StateID) bool
- func (i *Interpreter[C]) Restore(s Snapshot[C]) error
- func (i *Interpreter[C]) Send(event Event)
- func (i *Interpreter[C]) SendParent(event Event) error
- func (i *Interpreter[C]) SendTo(id ActorID, event Event) error
- func (i *Interpreter[C]) Snapshot() Snapshot[C]
- func (i *Interpreter[C]) Start()
- func (i *Interpreter[C]) State() State[C]
- func (i *Interpreter[C]) Stop()
- func (i *Interpreter[C]) UpdateContext(fn func(ctx *C))
- func (i *Interpreter[C]) Use(p plugin.Plugin[C])
- type InvokeBuilder
- func (b *InvokeBuilder[C]) Done() *MachineBuilder[C]
- func (b *InvokeBuilder[C]) End() *StateBuilder[C]
- func (b *InvokeBuilder[C]) ID(id string) *InvokeBuilder[C]
- func (b *InvokeBuilder[C]) OnDone(target StateID) *InvokeBuilder[C]
- func (b *InvokeBuilder[C]) OnDoneAction(action ActionType) *InvokeBuilder[C]
- func (b *InvokeBuilder[C]) OnError(target StateID) *InvokeBuilder[C]
- func (b *InvokeBuilder[C]) OnErrorAction(action ActionType) *InvokeBuilder[C]
- type InvokeMachineBuilder
- type InvokeServiceBuilder
- type Lock
- type MachineBuilder
- func (b *MachineBuilder[C]) Build() (*ir.MachineConfig[C], error)
- func (b *MachineBuilder[C]) State(id StateID) *StateBuilder[C]
- func (b *MachineBuilder[C]) WithAction(name ActionType, action Action[C]) *MachineBuilder[C]
- func (b *MachineBuilder[C]) WithChildMachine(name string, factory ir.ChildMachineFactory[C]) *MachineBuilder[C]
- func (b *MachineBuilder[C]) WithContext(ctx C) *MachineBuilder[C]
- func (b *MachineBuilder[C]) WithGuard(name GuardType, guard Guard[C]) *MachineBuilder[C]
- func (b *MachineBuilder[C]) WithInitial(initial StateID) *MachineBuilder[C]
- func (b *MachineBuilder[C]) WithService(name ServiceType, service Service[C]) *MachineBuilder[C]
- type MachineConfig
- type MachineDef
- type MachineInvokeBuilder
- func (b *MachineInvokeBuilder[C]) AutoForward(events ...EventType) *MachineInvokeBuilder[C]
- func (b *MachineInvokeBuilder[C]) Done() *MachineBuilder[C]
- func (b *MachineInvokeBuilder[C]) End() *StateBuilder[C]
- func (b *MachineInvokeBuilder[C]) ID(id string) *MachineInvokeBuilder[C]
- func (b *MachineInvokeBuilder[C]) OnDone(target StateID) *MachineInvokeBuilder[C]
- func (b *MachineInvokeBuilder[C]) OnDoneAction(action ActionType) *MachineInvokeBuilder[C]
- func (b *MachineInvokeBuilder[C]) OnError(target StateID) *MachineInvokeBuilder[C]
- func (b *MachineInvokeBuilder[C]) OnErrorAction(action ActionType) *MachineInvokeBuilder[C]
- type MachineSnapshot
- type MembershipEvent
- type MembershipEventType
- type MemoryEventStore
- func (s *MemoryEventStore) AppendEvents(ctx context.Context, streamID string, expectedVersion int, ...) error
- func (s *MemoryEventStore) GetStreamVersion(ctx context.Context, streamID string) (int, error)
- func (s *MemoryEventStore) LoadEvents(ctx context.Context, streamID string, fromVersion int) ([]PersistedEvent, error)
- type MemorySnapshotStore
- type MemoryStreamLock
- type Option
- type PendingTimer
- type PersistedEvent
- type PersistentInterpreter
- func (pi *PersistentInterpreter[C]) Commit(ctx context.Context) (int, error)
- func (pi *PersistentInterpreter[C]) Context() C
- func (pi *PersistentInterpreter[C]) Done() bool
- func (pi *PersistentInterpreter[C]) ForceSnapshot(ctx context.Context) error
- func (pi *PersistentInterpreter[C]) Matches(stateID StateID) bool
- func (pi *PersistentInterpreter[C]) Rollback()
- func (pi *PersistentInterpreter[C]) Send(event Event)
- func (pi *PersistentInterpreter[C]) SendAll(events []Event)
- func (pi *PersistentInterpreter[C]) State() State[C]
- func (pi *PersistentInterpreter[C]) Stop()
- func (pi *PersistentInterpreter[C]) StreamID() string
- func (pi *PersistentInterpreter[C]) UncommittedCount() int
- func (pi *PersistentInterpreter[C]) Version() int
- type PersistentInterpreterOption
- type RegionBuilder
- type RestoreError
- type Service
- type ServiceContext
- type ServiceType
- type Snapshot
- type SnapshotConfig
- type SnapshotStore
- type SnapshotStrategy
- type SpawnOption
- type State
- type StateBuilder
- func (b *StateBuilder[C]) After(d time.Duration) *TransitionBuilder[C]
- func (b *StateBuilder[C]) Always() *TransitionBuilder[C]
- func (b *StateBuilder[C]) Done() *MachineBuilder[C]
- func (b *StateBuilder[C]) End() *StateBuilder[C]
- func (b *StateBuilder[C]) EndMachine() *MachineBuilder[C]
- func (b *StateBuilder[C]) EndState() *RegionBuilder[C]
- func (b *StateBuilder[C]) Final() *StateBuilder[C]
- func (b *StateBuilder[C]) History(id StateID) *HistoryBuilder[C]
- func (b *StateBuilder[C]) Invoke(src ServiceType) *InvokeBuilder[C]
- func (b *StateBuilder[C]) InvokeMachine(machineRef string) *MachineInvokeBuilder[C]
- func (b *StateBuilder[C]) On(event EventType) *TransitionBuilder[C]
- func (b *StateBuilder[C]) OnEntry(action ActionType) *StateBuilder[C]
- func (b *StateBuilder[C]) OnExit(action ActionType) *StateBuilder[C]
- func (b *StateBuilder[C]) Parallel() *StateBuilder[C]
- func (b *StateBuilder[C]) Region(id StateID) *RegionBuilder[C]
- func (b *StateBuilder[C]) State(id StateID) *StateBuilder[C]
- func (b *StateBuilder[C]) Tags(tags ...string) *StateBuilder[C]
- func (b *StateBuilder[C]) WithInitial(initial StateID) *StateBuilder[C]
- type StateID
- type StateNode
- type StateType
- type StreamLock
- type StreamRouter
- type SupervisionStrategy
- type Timer
- type TransitionBuilder
- func (b *TransitionBuilder[C]) After(d time.Duration) *TransitionBuilder[C]
- func (b *TransitionBuilder[C]) Do(action ActionType) *TransitionBuilder[C]
- func (b *TransitionBuilder[C]) Done() *MachineBuilder[C]
- func (b *TransitionBuilder[C]) End() *StateBuilder[C]
- func (b *TransitionBuilder[C]) EndMachine() *MachineBuilder[C]
- func (b *TransitionBuilder[C]) EndState() *RegionBuilder[C]
- func (b *TransitionBuilder[C]) Guard(guard GuardType) *TransitionBuilder[C]
- func (b *TransitionBuilder[C]) Internal() *TransitionBuilder[C]
- func (b *TransitionBuilder[C]) On(event EventType) *TransitionBuilder[C]
- func (b *TransitionBuilder[C]) Raise(events ...EventType) *TransitionBuilder[C]
- func (b *TransitionBuilder[C]) Target(target StateID) *TransitionBuilder[C]
Examples ¶
Constants ¶
const ( ErrCodeSnapshotMachineMismatch = "SNAPSHOT_MACHINE_MISMATCH" ErrCodeSnapshotInvalidState = "SNAPSHOT_INVALID_STATE" ErrCodeSnapshotVersionMismatch = "SNAPSHOT_VERSION_MISMATCH" )
Error codes for snapshot operations
const ( StateTypeAtomic = ir.StateTypeAtomic StateTypeCompound = ir.StateTypeCompound StateTypeFinal = ir.StateTypeFinal StateTypeHistory = ir.StateTypeHistory // v2.0 StateTypeParallel = ir.StateTypeParallel // v2.0 HistoryTypeShallow = ir.HistoryTypeShallow // v2.0 HistoryTypeDeep = ir.HistoryTypeDeep // v2.0 )
Re-export constants
Variables ¶
var ErrActorAlreadyExists = errors.New("actor already exists")
ErrActorAlreadyExists is returned when spawning an actor with a duplicate ID
var ErrActorNotFound = errors.New("actor not found")
ErrActorNotFound is returned when an actor ID doesn't exist in the registry
var ErrActorStopped = errors.New("actor stopped")
ErrActorStopped is returned when sending to a stopped actor
var ErrLockHeld = errors.New("lock held by another node")
ErrLockHeld is returned when attempting to acquire a lock held by another node.
var ErrLockLost = errors.New("lock lost")
ErrLockLost is returned when a lock is lost unexpectedly.
var ErrNoParent = errors.New("no parent actor")
ErrNoParent is returned when SendParent is called on a root interpreter
Functions ¶
func FromStruct ¶
func FromStruct[M any, C any](registry *ActionRegistry[C]) (*ir.MachineConfig[C], error)
FromStruct builds a MachineConfig from a struct definition using the reflection DSL.
The struct M must embed MachineDef and define states using StateNode, CompoundNode, or FinalNode marker types with appropriate struct tags.
Actions and guards referenced in tags must be registered in the provided ActionRegistry.
Example:
type MyMachine struct {
statekit.MachineDef `id:"example" initial:"idle"`
Idle statekit.StateNode `on:"START->running:canStart" entry:"logStart"`
Running statekit.StateNode `on:"STOP->idle"`
}
registry := statekit.NewActionRegistry[MyContext]().
WithAction("logStart", func(ctx *MyContext, e statekit.Event) { ... }).
WithGuard("canStart", func(ctx MyContext, e statekit.Event) bool { ... })
machine, err := statekit.FromStruct[MyMachine, MyContext](registry)
func FromStructWithContext ¶
func FromStructWithContext[M any, C any](registry *ActionRegistry[C], ctx C) (*ir.MachineConfig[C], error)
FromStructWithContext builds a MachineConfig with an initial context value.
Types ¶
type Action ¶
Action is a side-effect function executed during transitions. It receives a pointer to the context for modification and the triggering event.
func Choose ¶
func Choose[C any](branches ...ChooseBranch[C]) Action[C]
Choose builds an Action that runs the Then of the first branch whose When guard passes, then stops — the action equivalent of XState's choose(). A branch with a nil When acts as an else. If no branch matches, it is a no-op.
Register it like any named action and reference it from transitions or entry/exit hooks:
WithAction("classify", statekit.Choose(
statekit.ChooseBranch[Ctx]{When: isGold, Then: tagGold},
statekit.ChooseBranch[Ctx]{When: isSilver, Then: tagSilver},
statekit.ChooseBranch[Ctx]{Then: tagBronze}, // else
))
type ActionRegistry ¶
type ActionRegistry[C any] struct { // contains filtered or unexported fields }
ActionRegistry holds action and guard function implementations that are referenced by name in the reflection DSL.
ActionRegistry is not safe for concurrent use. It should be fully configured before calling FromStruct or FromStructWithContext.
func NewActionRegistry ¶
func NewActionRegistry[C any]() *ActionRegistry[C]
NewActionRegistry creates a new empty action registry.
func (*ActionRegistry[C]) WithAction ¶
func (r *ActionRegistry[C]) WithAction(name ActionType, action Action[C]) *ActionRegistry[C]
WithAction registers an action function by name. Returns the registry for method chaining.
func (*ActionRegistry[C]) WithGuard ¶
func (r *ActionRegistry[C]) WithGuard(name GuardType, guard Guard[C]) *ActionRegistry[C]
WithGuard registers a guard function by name. Returns the registry for method chaining.
type ActorMetadata ¶
type ActorMetadata struct {
// ID is the actor's unique identifier
ID ActorID `json:"id"`
// SpawnedInState is the state that spawned this actor
SpawnedInState StateID `json:"spawned_in_state"`
// Supervision is the supervision strategy for this actor
Supervision SupervisionStrategy `json:"supervision"`
// AutoForward lists event types that were auto-forwarded to this actor
AutoForward []EventType `json:"auto_forward,omitempty"`
}
ActorMetadata captures information about a spawned actor for persistence (v0.14). This is a metadata-only snapshot - the actor's internal state is not captured. When restoring, actors must be manually respawned by the application.
type ActorRef ¶
type ActorRef struct {
// contains filtered or unexported fields
}
ActorRef provides a handle to communicate with a spawned child actor. It is safe to use concurrently from multiple goroutines.
func Spawn ¶
func Spawn[ParentC, ChildC any]( parent *Interpreter[ParentC], id ActorID, childMachine *ir.MachineConfig[ChildC], opts ...SpawnOption, ) (*ActorRef, error)
Spawn creates and starts a child actor from a machine configuration. The actor is associated with the current state and will be stopped when that state is exited (state-scoped lifecycle).
Options can configure supervision strategy, auto-forwarding, and completion handlers.
func SpawnWithContext ¶
func SpawnWithContext[ParentC, ChildC any]( parent *Interpreter[ParentC], id ActorID, childMachine *ir.MachineConfig[ChildC], initContext func(ParentC) ChildC, opts ...SpawnOption, ) (*ActorRef, error)
SpawnWithContext is like Spawn but allows initializing the child context from the parent context.
func (*ActorRef) Done ¶
func (a *ActorRef) Done() <-chan struct{}
Done returns a channel that's closed when the actor stops. Use this to wait for actor completion.
type ChooseBranch ¶
type ChooseBranch[C any] struct { // When gates this branch. A nil When always matches — use it as the // final else branch. When Guard[C] // Then is the action executed when this branch is selected. May be nil. Then Action[C] }
ChooseBranch is one arm of a Choose action: when When passes (or is nil, acting as an "else"), Then runs and evaluation stops (v1.x).
type Clock ¶
type Clock interface {
// AfterFunc schedules fn to run after d. The returned Timer.Stop
// cancels the callback if it has not yet fired.
AfterFunc(d time.Duration, fn func()) Timer
}
Clock abstracts wall-clock timing so timer-driven behavior (delayed transitions, supervision timers) can be deterministically controlled in tests via FakeClock.
Production code uses the default systemClock; pass FakeClock via WithClock for tests.
func SystemClock ¶
func SystemClock() Clock
SystemClock returns the default wall-clock implementation.
type ClusterMembership ¶
type ClusterMembership interface {
// Join registers this node with the cluster.
Join(ctx context.Context, node ClusterNode) error
// Leave removes this node from the cluster.
Leave(ctx context.Context) error
// Members returns all known cluster members.
Members(ctx context.Context) ([]ClusterNode, error)
// Watch returns a channel that receives membership changes.
Watch(ctx context.Context) (<-chan MembershipEvent, error)
}
ClusterMembership provides cluster membership management. Implementations can use etcd, consul, or a custom gossip protocol.
type ClusterNode ¶
type ClusterNode struct {
ID string
Address string
JoinedAt time.Time
LastSeen time.Time
Metadata map[string]string
}
ClusterNode represents a node in the cluster.
type CompoundNode ¶
type CompoundNode struct{}
CompoundNode is a marker type for defining compound (nested) states.
Use struct tags to configure the compound state:
- initial:"childState" - Required initial child state
- on:"EVENT->target" - Parent-level transitions
- entry:"action" - Parent entry actions
- exit:"action" - Parent exit actions
Child states are defined as fields within the struct that embeds CompoundNode.
Example:
type ActiveState struct {
statekit.CompoundNode `initial:"idle" on:"RESET->done"`
Idle statekit.StateNode `on:"START->working"`
Working statekit.StateNode `on:"STOP->idle"`
}
type ConsistentHashRouter ¶
type ConsistentHashRouter struct {
// Replicas is the number of virtual nodes per physical node.
Replicas int
}
ConsistentHashRouter routes streams using consistent hashing.
func NewConsistentHashRouter ¶
func NewConsistentHashRouter(replicas int) *ConsistentHashRouter
NewConsistentHashRouter creates a new consistent hash router.
func (*ConsistentHashRouter) IsLocal ¶
func (r *ConsistentHashRouter) IsLocal(streamID string, members []ClusterNode, localNodeID string) bool
IsLocal returns true if this node should handle the stream.
func (*ConsistentHashRouter) RouteStream ¶
func (r *ConsistentHashRouter) RouteStream(streamID string, members []ClusterNode) string
RouteStream returns the node that should handle the stream using consistent hashing.
type DistributedInterpreter ¶
type DistributedInterpreter[C any] struct { // contains filtered or unexported fields }
DistributedInterpreter wraps a PersistentInterpreter with distributed locking. It ensures only one node processes events for a stream at a time.
func NewDistributedInterpreter ¶
func NewDistributedInterpreter[C any]( ctx context.Context, streamID string, machine *ir.MachineConfig[C], eventStore EventStore, streamLock StreamLock, opts ...DistributedInterpreterOption[C], ) (*DistributedInterpreter[C], error)
NewDistributedInterpreter creates a new distributed interpreter. It acquires a lock for the stream before hydrating state.
func (*DistributedInterpreter[C]) Commit ¶
func (di *DistributedInterpreter[C]) Commit(ctx context.Context) (int, error)
Commit persists uncommitted events.
func (*DistributedInterpreter[C]) Context ¶
func (di *DistributedInterpreter[C]) Context() C
Context returns the current context.
func (*DistributedInterpreter[C]) Done ¶
func (di *DistributedInterpreter[C]) Done() bool
Done returns true if in a final state.
func (*DistributedInterpreter[C]) ForceSnapshot ¶
func (di *DistributedInterpreter[C]) ForceSnapshot(ctx context.Context) error
ForceSnapshot creates a snapshot.
func (*DistributedInterpreter[C]) LockHeld ¶
func (di *DistributedInterpreter[C]) LockHeld() bool
LockHeld returns true if the lock is still held.
func (*DistributedInterpreter[C]) LockLost ¶
func (di *DistributedInterpreter[C]) LockLost() <-chan struct{}
LockLost returns a channel that's closed when the lock is lost.
func (*DistributedInterpreter[C]) Matches ¶
func (di *DistributedInterpreter[C]) Matches(stateID StateID) bool
Matches checks if current state matches or is descendant of given state.
func (*DistributedInterpreter[C]) Send ¶
func (di *DistributedInterpreter[C]) Send(event Event) error
Send processes an event if the lock is still held.
func (*DistributedInterpreter[C]) SendAll ¶
func (di *DistributedInterpreter[C]) SendAll(events []Event) error
SendAll processes multiple events.
func (*DistributedInterpreter[C]) State ¶
func (di *DistributedInterpreter[C]) State() State[C]
State returns the current state.
func (*DistributedInterpreter[C]) Stop ¶
func (di *DistributedInterpreter[C]) Stop(ctx context.Context) error
Stop releases the lock and stops the interpreter.
func (*DistributedInterpreter[C]) StreamID ¶
func (di *DistributedInterpreter[C]) StreamID() string
StreamID returns the stream identifier.
func (*DistributedInterpreter[C]) UncommittedCount ¶
func (di *DistributedInterpreter[C]) UncommittedCount() int
UncommittedCount returns the number of uncommitted events.
func (*DistributedInterpreter[C]) Version ¶
func (di *DistributedInterpreter[C]) Version() int
Version returns the current stream version.
type DistributedInterpreterOption ¶
type DistributedInterpreterOption[C any] func(*DistributedInterpreter[C])
DistributedInterpreterOption configures a DistributedInterpreter.
func WithDistributedSnapshotConfig ¶
func WithDistributedSnapshotConfig[C any](config SnapshotConfig) DistributedInterpreterOption[C]
WithDistributedSnapshotConfig sets the snapshot configuration.
func WithDistributedSnapshotStore ¶
func WithDistributedSnapshotStore[C any](store SnapshotStore[C]) DistributedInterpreterOption[C]
WithDistributedSnapshotStore sets the snapshot store.
func WithLockTTL ¶
func WithLockTTL[C any](ttl time.Duration) DistributedInterpreterOption[C]
WithLockTTL sets the lock TTL and renewal interval. Defaults: TTL=30s, renewInterval=10s (renew at 1/3 of TTL).
type ErrConcurrencyConflict ¶
ErrConcurrencyConflict is returned when optimistic concurrency check fails.
func (*ErrConcurrencyConflict) Error ¶
func (e *ErrConcurrencyConflict) Error() string
type EventStore ¶
type EventStore interface {
// AppendEvents atomically appends events to a stream.
// Returns ErrConcurrencyConflict if expectedVersion doesn't match.
AppendEvents(ctx context.Context, streamID string, expectedVersion int, events []PersistedEvent) error
// LoadEvents loads all events for a stream from a specific version.
// Pass fromVersion=0 to load all events.
LoadEvents(ctx context.Context, streamID string, fromVersion int) ([]PersistedEvent, error)
// GetStreamVersion returns the current version of a stream.
// Returns 0 if the stream doesn't exist.
GetStreamVersion(ctx context.Context, streamID string) (int, error)
}
EventStore defines the interface for event persistence. Implementations handle storage of events for event sourcing.
type FakeClock ¶
type FakeClock struct {
// contains filtered or unexported fields
}
FakeClock is a test-only Clock whose passage of time is controlled by Advance and Now. Safe for concurrent use; callbacks run on the goroutine that calls Advance.
func NewFakeClock ¶
NewFakeClock returns a FakeClock anchored at the given time. Use time.Now() (or any deterministic timestamp) as the anchor.
func (*FakeClock) Advance ¶
Advance moves the clock forward by d, firing any callbacks whose deadline falls within the new interval. Callbacks fire in deadline order; ties resolve by registration order.
Callbacks run synchronously on the caller's goroutine before Advance returns. This makes timer-driven tests deterministic.
func (*FakeClock) Now ¶
Now returns the FakeClock's current time. The clock does not advance on its own — call Advance to move it forward.
func (*FakeClock) PendingTimers ¶
PendingTimers reports how many timers are scheduled and not yet fired or stopped. Useful for test invariants.
type FinalNode ¶
type FinalNode struct{}
FinalNode is a marker type for defining final states.
Final states indicate the machine has completed. They typically have no outgoing transitions.
Example:
Completed statekit.FinalNode
type Guard ¶
Guard is a predicate that determines if a transition should occur. It receives the current context (by value) and the triggering event.
type HistoryBuilder ¶
type HistoryBuilder[C any] struct { // contains filtered or unexported fields }
HistoryBuilder provides a fluent API for constructing history states
func (*HistoryBuilder[C]) Deep ¶
func (b *HistoryBuilder[C]) Deep() *HistoryBuilder[C]
Deep sets the history type to deep (remembers full leaf path)
func (*HistoryBuilder[C]) Default ¶
func (b *HistoryBuilder[C]) Default(target StateID) *HistoryBuilder[C]
Default sets the default target state if no history is recorded
func (*HistoryBuilder[C]) End ¶
func (b *HistoryBuilder[C]) End() *StateBuilder[C]
End completes the history state definition and returns to the parent StateBuilder
func (*HistoryBuilder[C]) Shallow ¶
func (b *HistoryBuilder[C]) Shallow() *HistoryBuilder[C]
Shallow sets the history type to shallow (remembers immediate child)
type HistoryType ¶
type HistoryType = ir.HistoryType
HistoryType specifies how history states remember previous states (v2.0)
type Interpreter ¶
type Interpreter[C any] struct { // contains filtered or unexported fields }
Interpreter is the statechart runtime that processes events and manages state
func NewInterpreter ¶
func NewInterpreter[C any](machine *ir.MachineConfig[C], opts ...Option[C]) *Interpreter[C]
NewInterpreter creates a new interpreter for the given machine configuration
func ReplayEvents ¶
func ReplayEvents[C any]( ctx context.Context, machine *ir.MachineConfig[C], eventStore EventStore, streamID string, ) (*Interpreter[C], error)
ReplayEvents replays events from the store to a new interpreter. Useful for debugging or creating a read model.
func ReplayToVersion ¶
func ReplayToVersion[C any]( ctx context.Context, machine *ir.MachineConfig[C], eventStore EventStore, streamID string, targetVersion int, ) (*Interpreter[C], error)
ReplayToVersion replays events up to a specific version.
func (*Interpreter[C]) Close ¶
func (i *Interpreter[C]) Close() error
Close implements io.Closer by calling Stop. Enables idiomatic `defer interp.Close()` cleanup.
func (*Interpreter[C]) Done ¶
func (i *Interpreter[C]) Done() bool
Done returns true if the machine is in a final state
func (*Interpreter[C]) GetActor ¶
func (i *Interpreter[C]) GetActor(id ActorID) *ActorRef
GetActor returns the ActorRef for the given ID, or nil if not found
func (*Interpreter[C]) HasTag ¶
func (i *Interpreter[C]) HasTag(tag string) bool
HasTag reports whether any currently active state carries the given tag. "Active" means the current leaf state, its ancestors, and — when inside a parallel state — every active region leaf and its ancestors (v1.x).
func (*Interpreter[C]) Matches ¶
func (i *Interpreter[C]) Matches(id StateID) bool
Matches checks if the current state matches the given state ID For hierarchical states, returns true if current state equals id or is a descendant of id For parallel states, also checks all active region states
Example ¶
package main
import (
"fmt"
"go.klarlabs.de/statekit"
)
func main() {
machine, _ := statekit.NewMachine[struct{}]("nested").
WithInitial("parent").
State("parent").
WithInitial("child").
State("child").End().
Done().
Build()
interp := statekit.NewInterpreter(machine)
interp.Start()
// Current state is "child"
fmt.Println(interp.State().Value)
// Matches both the current state and its ancestors
fmt.Println(interp.Matches("child"))
fmt.Println(interp.Matches("parent"))
}
Output: child true true
func (*Interpreter[C]) Restore ¶
func (i *Interpreter[C]) Restore(s Snapshot[C]) error
Restore restores the interpreter to the state captured in the snapshot. The machine configuration must match (same machine ID). Returns an error if the snapshot is incompatible.
func (*Interpreter[C]) Send ¶
func (i *Interpreter[C]) Send(event Event)
Send processes an event and potentially transitions to a new state
func (*Interpreter[C]) SendParent ¶
func (i *Interpreter[C]) SendParent(event Event) error
SendParent sends an event to the parent interpreter. Returns ErrNoParent if this is a root interpreter (no parent).
func (*Interpreter[C]) SendTo ¶
func (i *Interpreter[C]) SendTo(id ActorID, event Event) error
SendTo sends an event to a specific child actor by ID
func (*Interpreter[C]) Snapshot ¶
func (i *Interpreter[C]) Snapshot() Snapshot[C]
Snapshot creates a snapshot of the current interpreter state. The snapshot captures all information needed to restore the interpreter to this exact state later.
Note on actors: Actor metadata is captured but actors are NOT automatically restored. The SpawnedActors field contains metadata about what actors were running, allowing the application to manually respawn them if needed.
func (*Interpreter[C]) Start ¶
func (i *Interpreter[C]) Start()
Start initializes the interpreter and enters the initial state
func (*Interpreter[C]) State ¶
func (i *Interpreter[C]) State() State[C]
State returns the current state of the interpreter
func (*Interpreter[C]) Stop ¶
func (i *Interpreter[C]) Stop()
Stop cancels all active timers, services, invoked machines, and actors, then stops the interpreter
func (*Interpreter[C]) UpdateContext ¶
func (i *Interpreter[C]) UpdateContext(fn func(ctx *C))
UpdateContext allows updating the context with a function
func (*Interpreter[C]) Use ¶
func (i *Interpreter[C]) Use(p plugin.Plugin[C])
Use registers a plugin with the interpreter. Plugins receive callbacks during interpreter execution. Multiple plugins can be registered; they are called in registration order.
type InvokeBuilder ¶
type InvokeBuilder[C any] struct { // contains filtered or unexported fields }
InvokeBuilder provides a fluent API for constructing service invocations (v3.0)
The name is kept for backward compatibility; use the InvokeServiceBuilder alias for clearer intent next to InvokeMachineBuilder.
func (*InvokeBuilder[C]) Done ¶
func (b *InvokeBuilder[C]) Done() *MachineBuilder[C]
Done completes the state definition and returns to the machine builder
func (*InvokeBuilder[C]) End ¶
func (b *InvokeBuilder[C]) End() *StateBuilder[C]
End completes the invocation definition and returns to the StateBuilder
func (*InvokeBuilder[C]) ID ¶
func (b *InvokeBuilder[C]) ID(id string) *InvokeBuilder[C]
ID sets a custom ID for this invocation
func (*InvokeBuilder[C]) OnDone ¶
func (b *InvokeBuilder[C]) OnDone(target StateID) *InvokeBuilder[C]
OnDone sets the transition target when the service completes successfully
func (*InvokeBuilder[C]) OnDoneAction ¶
func (b *InvokeBuilder[C]) OnDoneAction(action ActionType) *InvokeBuilder[C]
OnDoneAction sets an action to execute when the service completes successfully
func (*InvokeBuilder[C]) OnError ¶
func (b *InvokeBuilder[C]) OnError(target StateID) *InvokeBuilder[C]
OnError sets the transition target when the service fails
func (*InvokeBuilder[C]) OnErrorAction ¶
func (b *InvokeBuilder[C]) OnErrorAction(action ActionType) *InvokeBuilder[C]
OnErrorAction sets an action to execute when the service fails
type InvokeMachineBuilder ¶
type InvokeMachineBuilder[C any] = MachineInvokeBuilder[C]
InvokeMachineBuilder is a clearer alias for MachineInvokeBuilder. Prefer this name in new code — it parallels InvokeServiceBuilder so the two service/machine invocation paths are self-documenting.
type InvokeServiceBuilder ¶
type InvokeServiceBuilder[C any] = InvokeBuilder[C]
InvokeServiceBuilder is a clearer alias for InvokeBuilder. Prefer this name in new code — it parallels InvokeMachineBuilder so the two service/machine invocation paths are self-documenting.
type Lock ¶
type Lock interface {
// Renew extends the lock's TTL.
Renew(ctx context.Context, ttl time.Duration) error
// Release releases the lock.
Release(ctx context.Context) error
// Done returns a channel that's closed when the lock is lost.
Done() <-chan struct{}
}
Lock represents a held distributed lock.
type MachineBuilder ¶
type MachineBuilder[C any] struct { // contains filtered or unexported fields }
MachineBuilder provides a fluent API for constructing state machines
func NewMachine ¶
func NewMachine[C any](id string) *MachineBuilder[C]
NewMachine creates a new MachineBuilder with the given ID
Example (Hierarchical) ¶
package main
import (
"fmt"
"go.klarlabs.de/statekit"
)
func main() {
// Hierarchical state machine with nested states
machine, _ := statekit.NewMachine[struct{}]("editor").
WithInitial("editing").
State("editing").
WithInitial("idle").
On("SAVE").Target("saved").End(). // Parent handles SAVE for all children
State("idle").
On("TYPE").Target("dirty").
End().
End().
State("dirty").
On("CLEAR").Target("idle").
End().
End().
Done().
State("saved").Final().Done().
Build()
interp := statekit.NewInterpreter(machine)
interp.Start()
fmt.Println(interp.State().Value) // Starts in leaf state
fmt.Println(interp.Matches("editing")) // Matches parent
interp.Send(statekit.Event{Type: "TYPE"})
fmt.Println(interp.State().Value)
interp.Send(statekit.Event{Type: "SAVE"}) // Bubbles up to parent
fmt.Println(interp.State().Value)
}
Output: idle true dirty saved
Example (WithAction) ¶
package main
import (
"fmt"
"go.klarlabs.de/statekit"
)
func main() {
type Context struct {
Count int
}
machine, _ := statekit.NewMachine[Context]("counter").
WithInitial("idle").
WithAction("increment", func(ctx *Context, e statekit.Event) {
ctx.Count++
}).
State("idle").
OnEntry("increment").
On("COUNT").Target("idle").
Done().
Build()
interp := statekit.NewInterpreter(machine)
interp.Start()
fmt.Println(interp.State().Context.Count) // Entry action runs on Start
interp.Send(statekit.Event{Type: "COUNT"})
fmt.Println(interp.State().Context.Count) // Entry action runs again on self-transition
}
Output: 1 2
Example (WithGuard) ¶
package main
import (
"fmt"
"go.klarlabs.de/statekit"
)
func main() {
type Context struct {
Approved bool
}
machine, _ := statekit.NewMachine[Context]("approval").
WithInitial("pending").
WithContext(Context{Approved: true}).
WithGuard("isApproved", func(ctx Context, e statekit.Event) bool {
return ctx.Approved
}).
State("pending").
On("SUBMIT").Target("approved").Guard("isApproved").
Done().
State("approved").Final().Done().
Build()
interp := statekit.NewInterpreter(machine)
interp.Start()
interp.Send(statekit.Event{Type: "SUBMIT"})
fmt.Println(interp.State().Value)
}
Output: approved
func (*MachineBuilder[C]) Build ¶
func (b *MachineBuilder[C]) Build() (*ir.MachineConfig[C], error)
Build constructs the final MachineConfig from the builder
func (*MachineBuilder[C]) State ¶
func (b *MachineBuilder[C]) State(id StateID) *StateBuilder[C]
State starts building a new state with the given ID
func (*MachineBuilder[C]) WithAction ¶
func (b *MachineBuilder[C]) WithAction(name ActionType, action Action[C]) *MachineBuilder[C]
WithAction registers a named action
func (*MachineBuilder[C]) WithChildMachine ¶
func (b *MachineBuilder[C]) WithChildMachine(name string, factory ir.ChildMachineFactory[C]) *MachineBuilder[C]
WithChildMachine registers a child machine factory for machine composition (v0.14). The factory creates a child interpreter when the machine is invoked.
func (*MachineBuilder[C]) WithContext ¶
func (b *MachineBuilder[C]) WithContext(ctx C) *MachineBuilder[C]
WithContext sets the initial context value
func (*MachineBuilder[C]) WithGuard ¶
func (b *MachineBuilder[C]) WithGuard(name GuardType, guard Guard[C]) *MachineBuilder[C]
WithGuard registers a named guard
func (*MachineBuilder[C]) WithInitial ¶
func (b *MachineBuilder[C]) WithInitial(initial StateID) *MachineBuilder[C]
WithInitial sets the initial state ID
func (*MachineBuilder[C]) WithService ¶
func (b *MachineBuilder[C]) WithService(name ServiceType, service Service[C]) *MachineBuilder[C]
WithService registers a named service (v3.0)
type MachineConfig ¶
type MachineConfig[C any] = ir.MachineConfig[C]
MachineConfig is the immutable internal representation of a statechart
type MachineDef ¶
type MachineDef struct{}
MachineDef is a marker type that must be embedded in a struct to define a state machine using the reflection DSL.
Use struct tags to configure the machine:
- id:"machineId" - Required machine identifier
- initial:"stateName" - Required initial state name
Example:
type MyMachine struct {
statekit.MachineDef `id:"myMachine" initial:"idle"`
Idle statekit.StateNode `on:"START->running"`
Running statekit.StateNode `on:"STOP->idle"`
}
type MachineInvokeBuilder ¶
type MachineInvokeBuilder[C any] struct { // contains filtered or unexported fields }
MachineInvokeBuilder provides a fluent API for constructing child machine invocations (v0.14)
The name is kept for backward compatibility; use the InvokeMachineBuilder alias for clearer intent next to InvokeServiceBuilder.
func (*MachineInvokeBuilder[C]) AutoForward ¶
func (b *MachineInvokeBuilder[C]) AutoForward(events ...EventType) *MachineInvokeBuilder[C]
AutoForward adds event types that should be auto-forwarded to the child machine
func (*MachineInvokeBuilder[C]) Done ¶
func (b *MachineInvokeBuilder[C]) Done() *MachineBuilder[C]
Done completes the state definition and returns to the machine builder
func (*MachineInvokeBuilder[C]) End ¶
func (b *MachineInvokeBuilder[C]) End() *StateBuilder[C]
End completes the machine invocation definition and returns to the StateBuilder
func (*MachineInvokeBuilder[C]) ID ¶
func (b *MachineInvokeBuilder[C]) ID(id string) *MachineInvokeBuilder[C]
ID sets a custom ID for this machine invocation
func (*MachineInvokeBuilder[C]) OnDone ¶
func (b *MachineInvokeBuilder[C]) OnDone(target StateID) *MachineInvokeBuilder[C]
OnDone sets the transition target when the child machine reaches a final state
func (*MachineInvokeBuilder[C]) OnDoneAction ¶
func (b *MachineInvokeBuilder[C]) OnDoneAction(action ActionType) *MachineInvokeBuilder[C]
OnDoneAction sets an action to execute when the child machine completes
func (*MachineInvokeBuilder[C]) OnError ¶
func (b *MachineInvokeBuilder[C]) OnError(target StateID) *MachineInvokeBuilder[C]
OnError sets the transition target when the child machine encounters an error
func (*MachineInvokeBuilder[C]) OnErrorAction ¶
func (b *MachineInvokeBuilder[C]) OnErrorAction(action ActionType) *MachineInvokeBuilder[C]
OnErrorAction sets an action to execute when the child machine fails
type MachineSnapshot ¶
type MachineSnapshot[C any] struct { // Current state value StateValue ir.StateID `json:"state_value"` // Full state path for hierarchical states StatePath []ir.StateID `json:"state_path,omitempty"` // Machine context Context C `json:"context"` // History memory for shallow history states HistoryShallow map[ir.StateID]ir.StateID `json:"history_shallow,omitempty"` // History memory for deep history states HistoryDeep map[ir.StateID]ir.StateID `json:"history_deep,omitempty"` // Parallel region states ActiveInParallel map[ir.StateID]ir.StateID `json:"active_in_parallel,omitempty"` // Current parallel state (if any) CurrentParallel ir.StateID `json:"current_parallel,omitempty"` // Timestamp when snapshot was taken Timestamp time.Time `json:"timestamp"` }
MachineSnapshot captures the complete state of an interpreter for persistence.
type MembershipEvent ¶
type MembershipEvent struct {
Type MembershipEventType
Node ClusterNode
}
MembershipEvent represents a cluster membership change.
type MembershipEventType ¶
type MembershipEventType int
MembershipEventType indicates the type of membership change.
const ( // MemberJoined indicates a new member joined. MemberJoined MembershipEventType = iota // MemberLeft indicates a member left gracefully. MemberLeft // MemberFailed indicates a member failed (no heartbeat). MemberFailed )
type MemoryEventStore ¶
type MemoryEventStore struct {
// contains filtered or unexported fields
}
MemoryEventStore is an in-memory implementation of EventStore for testing.
func NewMemoryEventStore ¶
func NewMemoryEventStore() *MemoryEventStore
NewMemoryEventStore creates a new in-memory event store.
func (*MemoryEventStore) AppendEvents ¶
func (s *MemoryEventStore) AppendEvents(ctx context.Context, streamID string, expectedVersion int, events []PersistedEvent) error
AppendEvents atomically appends events to a stream.
func (*MemoryEventStore) GetStreamVersion ¶
GetStreamVersion returns the current version of a stream.
func (*MemoryEventStore) LoadEvents ¶
func (s *MemoryEventStore) LoadEvents(ctx context.Context, streamID string, fromVersion int) ([]PersistedEvent, error)
LoadEvents loads all events for a stream from a specific version.
type MemorySnapshotStore ¶
type MemorySnapshotStore[C any] struct { // contains filtered or unexported fields }
MemorySnapshotStore is an in-memory implementation of SnapshotStore for testing.
func NewMemorySnapshotStore ¶
func NewMemorySnapshotStore[C any]() *MemorySnapshotStore[C]
NewMemorySnapshotStore creates a new in-memory snapshot store.
func (*MemorySnapshotStore[C]) LoadSnapshot ¶
func (s *MemorySnapshotStore[C]) LoadSnapshot(ctx context.Context, streamID string, maxVersion int) (*MachineSnapshot[C], int, error)
LoadSnapshot loads the latest snapshot at or before maxVersion.
func (*MemorySnapshotStore[C]) SaveSnapshot ¶
func (s *MemorySnapshotStore[C]) SaveSnapshot(ctx context.Context, streamID string, version int, snapshot *MachineSnapshot[C]) error
SaveSnapshot saves a state snapshot at the given version.
type MemoryStreamLock ¶
type MemoryStreamLock struct {
// contains filtered or unexported fields
}
MemoryStreamLock is an in-memory implementation of StreamLock for testing. In production, use Redis, etcd, or PostgreSQL advisory locks.
func NewMemoryStreamLock ¶
func NewMemoryStreamLock() *MemoryStreamLock
NewMemoryStreamLock creates a new in-memory stream lock.
func (*MemoryStreamLock) Acquire ¶
func (s *MemoryStreamLock) Acquire(ctx context.Context, streamID string, ttl time.Duration) (Lock, error)
Acquire blocks until the lock is acquired or context is cancelled.
func (*MemoryStreamLock) TryAcquire ¶
func (s *MemoryStreamLock) TryAcquire(ctx context.Context, streamID string, ttl time.Duration) (Lock, error)
TryAcquire attempts to acquire a lock without blocking.
type Option ¶
type Option[C any] func(*Interpreter[C])
Option configures a new Interpreter at construction time.
type PendingTimer ¶
type PendingTimer struct {
// StateID is the state that owns this delayed transition
StateID StateID `json:"state_id"`
// TransitionIndex identifies which transition in the state
TransitionIndex int `json:"transition_index"`
// Target is the destination state
Target StateID `json:"target"`
// Remaining is how much time is left until the timer fires
Remaining time.Duration `json:"remaining_ns"`
}
PendingTimer represents an active delayed transition that hasn't fired yet.
type PersistedEvent ¶
type PersistedEvent struct {
// ID is a unique identifier for this event (e.g., UUID)
ID string `json:"id"`
// StreamID identifies the aggregate/stream this event belongs to
StreamID string `json:"stream_id"`
// Type is the event type that triggered the transition
Type EventType `json:"type"`
// Version is the stream version (monotonically increasing per stream)
Version int `json:"version"`
// Timestamp when the event was recorded
Timestamp time.Time `json:"timestamp"`
// Payload contains serialized event data
Payload json.RawMessage `json:"payload,omitempty"`
// Metadata holds additional context (correlation ID, user ID, etc.)
Metadata map[string]any `json:"metadata,omitempty"`
// StateAfter is the state after processing this event
StateAfter ir.StateID `json:"state_after"`
}
PersistedEvent represents a stored event in the event store.
type PersistentInterpreter ¶
type PersistentInterpreter[C any] struct { // contains filtered or unexported fields }
PersistentInterpreter wraps an Interpreter with event sourcing capabilities. All state transitions are persisted to an EventStore and can be replayed.
func NewPersistentInterpreter ¶
func NewPersistentInterpreter[C any]( ctx context.Context, streamID string, machine *ir.MachineConfig[C], eventStore EventStore, opts ...PersistentInterpreterOption[C], ) (*PersistentInterpreter[C], error)
NewPersistentInterpreter creates a new persistent interpreter. It automatically hydrates state from the event store if events exist.
func (*PersistentInterpreter[C]) Commit ¶
func (pi *PersistentInterpreter[C]) Commit(ctx context.Context) (int, error)
Commit persists all uncommitted events to the event store. Returns the number of events committed.
func (*PersistentInterpreter[C]) Context ¶
func (pi *PersistentInterpreter[C]) Context() C
Context returns a copy of the current context.
func (*PersistentInterpreter[C]) Done ¶
func (pi *PersistentInterpreter[C]) Done() bool
Done returns true if the interpreter is in a final state.
func (*PersistentInterpreter[C]) ForceSnapshot ¶
func (pi *PersistentInterpreter[C]) ForceSnapshot(ctx context.Context) error
ForceSnapshot creates a snapshot regardless of the configured strategy.
func (*PersistentInterpreter[C]) Matches ¶
func (pi *PersistentInterpreter[C]) Matches(stateID StateID) bool
Matches checks if the current state matches or is a descendant of the given state.
func (*PersistentInterpreter[C]) Rollback ¶
func (pi *PersistentInterpreter[C]) Rollback()
Rollback discards all uncommitted events.
func (*PersistentInterpreter[C]) Send ¶
func (pi *PersistentInterpreter[C]) Send(event Event)
Send processes an event and records it for persistence. Events are recorded if they match a transition (including self-transitions). Events that don't match any transition are not recorded. Call Commit() to persist uncommitted events.
func (*PersistentInterpreter[C]) SendAll ¶
func (pi *PersistentInterpreter[C]) SendAll(events []Event)
SendAll processes multiple events.
func (*PersistentInterpreter[C]) State ¶
func (pi *PersistentInterpreter[C]) State() State[C]
State returns the current state.
func (*PersistentInterpreter[C]) Stop ¶
func (pi *PersistentInterpreter[C]) Stop()
Stop stops the underlying interpreter.
func (*PersistentInterpreter[C]) StreamID ¶
func (pi *PersistentInterpreter[C]) StreamID() string
StreamID returns the stream identifier.
func (*PersistentInterpreter[C]) UncommittedCount ¶
func (pi *PersistentInterpreter[C]) UncommittedCount() int
UncommittedCount returns the number of uncommitted events.
func (*PersistentInterpreter[C]) Version ¶
func (pi *PersistentInterpreter[C]) Version() int
Version returns the current stream version.
type PersistentInterpreterOption ¶
type PersistentInterpreterOption[C any] func(*PersistentInterpreter[C])
PersistentInterpreterOption configures a PersistentInterpreter.
func WithSnapshotConfig ¶
func WithSnapshotConfig[C any](config SnapshotConfig) PersistentInterpreterOption[C]
WithSnapshotConfig sets the snapshot configuration.
func WithSnapshotStore ¶
func WithSnapshotStore[C any](store SnapshotStore[C]) PersistentInterpreterOption[C]
WithSnapshotStore sets the snapshot store for the interpreter.
type RegionBuilder ¶
type RegionBuilder[C any] struct { // contains filtered or unexported fields }
RegionBuilder provides a fluent API for constructing parallel regions (v2.0)
func (*RegionBuilder[C]) EndRegion ¶
func (b *RegionBuilder[C]) EndRegion() *StateBuilder[C]
EndRegion completes the region and returns to the parent parallel state
func (*RegionBuilder[C]) State ¶
func (b *RegionBuilder[C]) State(id StateID) *StateBuilder[C]
State starts building a state within this region
func (*RegionBuilder[C]) WithInitial ¶
func (b *RegionBuilder[C]) WithInitial(initial StateID) *RegionBuilder[C]
WithInitial sets the initial state for this region
type RestoreError ¶
RestoreError represents an error during snapshot restoration.
func (*RestoreError) Error ¶
func (e *RestoreError) Error() string
type Service ¶
Service is an async operation invoked when entering a state. It runs in a goroutine and can send events back to the machine. The service should respect the context for cancellation.
type ServiceContext ¶
type ServiceContext[C any] = ir.ServiceContext[C]
ServiceContext provides the execution context for a service (v3.0)
type Snapshot ¶
type Snapshot[C any] struct { // MachineID identifies which machine type this snapshot belongs to MachineID string `json:"machine_id"` // Version is the machine version for compatibility checking Version string `json:"version,omitempty"` // CurrentState is the active state ID (leaf state, or parallel state ID) CurrentState StateID `json:"current_state"` // Context is the user-defined context data Context C `json:"context"` // ShallowHistory maps compound state IDs to their last active child ShallowHistory map[StateID]StateID `json:"shallow_history,omitempty"` // DeepHistory maps compound state IDs to their last active leaf DeepHistory map[StateID]StateID `json:"deep_history,omitempty"` // ActiveInParallel maps region IDs to their current leaf states // Only populated when snapshot was taken from a parallel state ActiveInParallel map[StateID]StateID `json:"active_in_parallel,omitempty"` // CurrentParallel holds the parallel state ID if currently in a parallel state CurrentParallel StateID `json:"current_parallel,omitempty"` // PendingTimers captures active delayed transitions PendingTimers []PendingTimer `json:"pending_timers,omitempty"` // SpawnedActors captures metadata about spawned child actors (v0.14) // Note: Actors are NOT automatically restored. This metadata allows // the application to manually respawn actors if needed. SpawnedActors []ActorMetadata `json:"spawned_actors,omitempty"` // CreatedAt is when the snapshot was taken CreatedAt time.Time `json:"created_at"` }
Snapshot captures the complete state of an interpreter for persistence. It can be serialized to JSON and later used to restore an interpreter to the exact same state.
func (Snapshot[C]) MarshalJSON ¶
MarshalJSON serializes the snapshot to JSON.
func (*Snapshot[C]) UnmarshalJSON ¶
UnmarshalJSON deserializes the snapshot from JSON.
type SnapshotConfig ¶
type SnapshotConfig struct {
// Strategy determines when to create snapshots
Strategy SnapshotStrategy
// Interval is the number of events between snapshots (for SnapshotByInterval)
Interval int
// TimeInterval is the duration between snapshots (for SnapshotByTime)
TimeInterval time.Duration
}
SnapshotConfig configures snapshot behavior.
type SnapshotStore ¶
type SnapshotStore[C any] interface { // SaveSnapshot saves a state snapshot at the given version. SaveSnapshot(ctx context.Context, streamID string, version int, snapshot *MachineSnapshot[C]) error // LoadSnapshot loads the latest snapshot at or before maxVersion. // Returns nil if no snapshot exists. LoadSnapshot(ctx context.Context, streamID string, maxVersion int) (*MachineSnapshot[C], int, error) }
SnapshotStore defines the interface for state snapshot persistence. Snapshots enable faster state reconstruction by avoiding full event replay.
type SnapshotStrategy ¶
type SnapshotStrategy int
SnapshotStrategy determines when snapshots are created.
const ( // SnapshotNever disables automatic snapshots SnapshotNever SnapshotStrategy = iota // SnapshotByInterval creates snapshots every N events SnapshotByInterval // SnapshotOnFinal creates a snapshot when reaching a final state SnapshotOnFinal // SnapshotByTime creates snapshots after a time interval SnapshotByTime )
type SpawnOption ¶
type SpawnOption func(*spawnOptions)
SpawnOption configures actor spawning behavior
func WithAutoForward ¶
func WithAutoForward(events ...EventType) SpawnOption
WithAutoForward configures events to automatically forward to the child
func WithOnDone ¶
func WithOnDone(target StateID) SpawnOption
WithOnDone sets the target state when the child reaches a final state
func WithOnError ¶
func WithOnError(target StateID) SpawnOption
WithOnError sets the target state when the child encounters an error
func WithSupervision ¶
func WithSupervision(s SupervisionStrategy) SpawnOption
WithSupervision sets the supervision strategy for the spawned actor
type State ¶
type State[C any] struct { Value StateID // Current state ID (leaf state, or parallel state when in parallel) Context C // Current context // Parallel state tracking (v2.0) // When inside a parallel state, maps region ID to its current leaf state // Empty when not in a parallel state ActiveInParallel map[StateID]StateID }
State represents the current runtime state of an interpreter
type StateBuilder ¶
type StateBuilder[C any] struct { // contains filtered or unexported fields }
StateBuilder provides a fluent API for constructing states
func (*StateBuilder[C]) After ¶
func (b *StateBuilder[C]) After(d time.Duration) *TransitionBuilder[C]
After starts building a delayed transition that triggers automatically after the specified duration (v2.0)
func (*StateBuilder[C]) Always ¶
func (b *StateBuilder[C]) Always() *TransitionBuilder[C]
Always starts building an eventless ("always") transition (v1.x). It is evaluated when the state is entered and after every transition, in declaration order; the first whose guard passes is taken. A target is required. Use multiple Always() calls to express guarded routing with a final guardless fallback.
func (*StateBuilder[C]) Done ¶
func (b *StateBuilder[C]) Done() *MachineBuilder[C]
Done completes the state definition and returns the root MachineBuilder.
Watch out: when called from a nested StateBuilder, Done() teleports the chain all the way back to the machine root, skipping any intermediate parent states. This is rarely what you want for nested states. Prefer End() for "back one level" or EndMachine() for "back to the root" — both are clearer at a glance.
func (*StateBuilder[C]) End ¶
func (b *StateBuilder[C]) End() *StateBuilder[C]
End completes a nested state and returns to the parent StateBuilder Use this instead of Done() when building nested states
func (*StateBuilder[C]) EndMachine ¶
func (b *StateBuilder[C]) EndMachine() *MachineBuilder[C]
EndMachine completes the state definition and returns the root MachineBuilder. Equivalent to Done() but its name makes the intent unambiguous when reading nested-state-builder chains.
func (*StateBuilder[C]) EndState ¶
func (b *StateBuilder[C]) EndState() *RegionBuilder[C]
EndState completes a state within a region and returns to the RegionBuilder (v2.0) Use this instead of End() when building states inside parallel regions
func (*StateBuilder[C]) Final ¶
func (b *StateBuilder[C]) Final() *StateBuilder[C]
Final marks this state as a final state
func (*StateBuilder[C]) History ¶
func (b *StateBuilder[C]) History(id StateID) *HistoryBuilder[C]
History starts building a history state within this compound state (v2.0) History states remember the last active child and transition back to it
func (*StateBuilder[C]) Invoke ¶
func (b *StateBuilder[C]) Invoke(src ServiceType) *InvokeBuilder[C]
Invoke starts building a service invocation for this state (v3.0) The service is started when entering the state and cancelled when exiting
func (*StateBuilder[C]) InvokeMachine ¶
func (b *StateBuilder[C]) InvokeMachine(machineRef string) *MachineInvokeBuilder[C]
InvokeMachine starts building a child machine invocation for this state (v0.14). The child machine is spawned when entering the state and stopped when exiting. The machineRef must match a name registered with WithChildMachine.
func (*StateBuilder[C]) On ¶
func (b *StateBuilder[C]) On(event EventType) *TransitionBuilder[C]
On starts building a new transition triggered by the given event
func (*StateBuilder[C]) OnEntry ¶
func (b *StateBuilder[C]) OnEntry(action ActionType) *StateBuilder[C]
OnEntry adds an entry action to the state
func (*StateBuilder[C]) OnExit ¶
func (b *StateBuilder[C]) OnExit(action ActionType) *StateBuilder[C]
OnExit adds an exit action to the state
func (*StateBuilder[C]) Parallel ¶
func (b *StateBuilder[C]) Parallel() *StateBuilder[C]
Parallel marks this state as a parallel state (v2.0) Use Region() to add orthogonal regions that execute simultaneously
func (*StateBuilder[C]) Region ¶
func (b *StateBuilder[C]) Region(id StateID) *RegionBuilder[C]
Region starts building a new region within this parallel state (v2.0)
func (*StateBuilder[C]) State ¶
func (b *StateBuilder[C]) State(id StateID) *StateBuilder[C]
State starts building a nested child state
func (*StateBuilder[C]) Tags ¶
func (b *StateBuilder[C]) Tags(tags ...string) *StateBuilder[C]
Tags attaches one or more tags to the state for lightweight querying via Interpreter.HasTag (v1.x).
func (*StateBuilder[C]) WithInitial ¶
func (b *StateBuilder[C]) WithInitial(initial StateID) *StateBuilder[C]
WithInitial sets the initial child state for a compound state
type StateNode ¶
type StateNode struct{}
StateNode is a marker type for defining atomic states in the reflection DSL.
Use struct tags to configure the state:
- on:"EVENT->target" - Define a transition (can specify multiple with comma)
- on:"EVENT->target:guard" - Transition with guard condition
- on:"EVENT->target/action1;action2" - Transition with actions
- on:"EVENT->target/action:guard" - Transition with action and guard
- entry:"action1,action2" - Entry actions
- exit:"action1,action2" - Exit actions
Example:
Idle statekit.StateNode `on:"START->running:canStart" entry:"logIdle"`
type StreamLock ¶
type StreamLock interface {
// Acquire attempts to acquire a lock for the given stream.
// Returns a Lock handle if successful, or an error if the lock is held by another node.
// The lock should automatically expire after ttl if not renewed.
Acquire(ctx context.Context, streamID string, ttl time.Duration) (Lock, error)
// TryAcquire attempts to acquire a lock without blocking.
// Returns ErrLockHeld if the lock is already held by another node.
TryAcquire(ctx context.Context, streamID string, ttl time.Duration) (Lock, error)
}
StreamLock provides distributed locking for state machine streams. Implementations can use Redis, etcd, PostgreSQL advisory locks, etc.
type StreamRouter ¶
type StreamRouter interface {
// RouteStream returns the node ID that should handle the stream.
RouteStream(streamID string, members []ClusterNode) string
// IsLocal returns true if this node should handle the stream.
IsLocal(streamID string, members []ClusterNode, localNodeID string) bool
}
StreamRouter determines which node should handle a given stream. This enables consistent hashing for stream distribution.
type SupervisionStrategy ¶
type SupervisionStrategy int
SupervisionStrategy defines how parent handles child actor errors
const ( // SupervisionEscalate bubbles the error to the parent via xstate.error.actor.<id> event SupervisionEscalate SupervisionStrategy = iota // SupervisionRecover logs the error and continues without stopping the child SupervisionRecover // SupervisionRestart stops the child and restarts it with initial state SupervisionRestart // SupervisionStop stops the child silently without generating an error event SupervisionStop )
type Timer ¶
type Timer interface {
// Stop prevents the callback from firing. Returns true if the
// call stops the timer, false if the timer has already expired
// or has been stopped.
Stop() bool
}
Timer is the cancelable handle returned by Clock.AfterFunc. Matches the Stop semantics of *time.Timer.
type TransitionBuilder ¶
type TransitionBuilder[C any] struct { // contains filtered or unexported fields }
TransitionBuilder provides a fluent API for constructing transitions
func (*TransitionBuilder[C]) After ¶
func (b *TransitionBuilder[C]) After(d time.Duration) *TransitionBuilder[C]
After starts a new delayed transition on the same state (chainable) (v2.0)
func (*TransitionBuilder[C]) Do ¶
func (b *TransitionBuilder[C]) Do(action ActionType) *TransitionBuilder[C]
Do adds an action to be executed during the transition
func (*TransitionBuilder[C]) Done ¶
func (b *TransitionBuilder[C]) Done() *MachineBuilder[C]
Done completes the state definition and returns to the machine builder
func (*TransitionBuilder[C]) End ¶
func (b *TransitionBuilder[C]) End() *StateBuilder[C]
End completes the transition and returns to the parent StateBuilder Use this instead of Done() when building transitions in nested states
func (*TransitionBuilder[C]) EndMachine ¶
func (b *TransitionBuilder[C]) EndMachine() *MachineBuilder[C]
EndMachine completes the state definition and returns to the MachineBuilder. Equivalent to Done() but its name makes the intent unambiguous in nested-state-builder chains.
func (*TransitionBuilder[C]) EndState ¶
func (b *TransitionBuilder[C]) EndState() *RegionBuilder[C]
EndState completes the transition and returns to the RegionBuilder (v2.0) Use this when building transitions in states inside parallel regions
func (*TransitionBuilder[C]) Guard ¶
func (b *TransitionBuilder[C]) Guard(guard GuardType) *TransitionBuilder[C]
Guard sets the guard condition for the transition
func (*TransitionBuilder[C]) Internal ¶
func (b *TransitionBuilder[C]) Internal() *TransitionBuilder[C]
Internal marks the transition as internal (v1.x): its actions run without exiting or re-entering the source state — entry/exit hooks do not fire and the active state does not change. Target is optional; when set it must be the owning state. Contrast with an external self-transition (a plain Target back to the same state), which does exit and re-enter.
func (*TransitionBuilder[C]) On ¶
func (b *TransitionBuilder[C]) On(event EventType) *TransitionBuilder[C]
On starts a new transition on the same state (chainable)
func (*TransitionBuilder[C]) Raise ¶
func (b *TransitionBuilder[C]) Raise(events ...EventType) *TransitionBuilder[C]
Raise enqueues internal events emitted when this transition is taken (v1.x). Raised events are processed in the same macrostep — before control returns to the caller and before any externally sent event.
func (*TransitionBuilder[C]) Target ¶
func (b *TransitionBuilder[C]) Target(target StateID) *TransitionBuilder[C]
Target sets the target state for the transition
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package ai provides primitives for LLM-driven state transitions.
|
Package ai provides primitives for LLM-driven state transitions. |
|
Package aiplugin provides plugins for AI/LLM-driven state machines.
|
Package aiplugin provides plugins for AI/LLM-driven state machines. |
|
cmd
|
|
|
statekit
command
Package main provides the statekit CLI tool for visualizing state machines.
|
Package main provides the statekit CLI tool for visualizing state machines. |
|
statekit-mcp
command
|
|
|
statekit/commands
Package commands provides CLI commands for the statekit tool.
|
Package commands provides CLI commands for the statekit tool. |
|
Package debug provides utilities for debugging and inspecting statekit state machines.
|
Package debug provides utilities for debugging and inspecting statekit state machines. |
|
examples
|
|
|
actor_supervisor
command
Package main demonstrates the actor model features of statekit.
|
Package main demonstrates the actor model features of statekit. |
|
form_wizard
command
Package main demonstrates history states with a multi-step form wizard.
|
Package main demonstrates history states with a multi-step form wizard. |
|
game_save
command
Package main demonstrates persistence with snapshots for a game save system.
|
Package main demonstrates persistence with snapshots for a game save system. |
|
incident_lifecycle
command
Package main demonstrates an SRE incident lifecycle using hierarchical states.
|
Package main demonstrates an SRE incident lifecycle using hierarchical states. |
|
llm_agent
command
Package main demonstrates a deterministic LLM agent runtime using statekit + aiplugin.
|
Package main demonstrates a deterministic LLM agent runtime using statekit + aiplugin. |
|
logging_plugin
command
Package main demonstrates the plugin system for extending interpreter behavior.
|
Package main demonstrates the plugin system for extending interpreter behavior. |
|
order_workflow
command
Package main demonstrates an e-commerce order workflow using the reflection DSL.
|
Package main demonstrates an e-commerce order workflow using the reflection DSL. |
|
pedestrian_light
Package pedestrianlight implements a pedestrian crossing signal with hierarchical states.
|
Package pedestrianlight implements a pedestrian crossing signal with hierarchical states. |
|
session_timeout
command
Package main demonstrates delayed (timed) transitions with a session timeout.
|
Package main demonstrates delayed (timed) transitions with a session timeout. |
|
stripe_webhook
command
Package main models a Stripe webhook saga as a statechart with the outbox pattern.
|
Package main models a Stripe webhook saga as a statechart with the outbox pattern. |
|
text_editor
command
Package main demonstrates parallel (orthogonal) states with a text editor.
|
Package main demonstrates parallel (orthogonal) states with a text editor. |
|
Package generate provides Go code generation from Statekit Native JSON definitions.
|
Package generate provides Go code generation from Statekit Native JSON definitions. |
|
Package health provides liveness and readiness probes for statekit state machines.
|
Package health provides liveness and readiness probes for statekit state machines. |
|
Package http provides HTTP middleware and handlers for statekit state machines.
|
Package http provides HTTP middleware and handlers for statekit state machines. |
|
internal
|
|
|
parser
Package parser provides reflection-based parsing for struct-defined state machines.
|
Package parser provides reflection-based parsing for struct-defined state machines. |
|
Package lint provides static analysis for statekit state machines.
|
Package lint provides static analysis for statekit state machines. |
|
Package mcp provides an MCP (Model Context Protocol) server for creating, managing, and visualizing statekit state machines.
|
Package mcp provides an MCP (Model Context Protocol) server for creating, managing, and visualizing statekit state machines. |
|
Package metrics provides Prometheus metrics integration for statekit state machines.
|
Package metrics provides Prometheus metrics integration for statekit state machines. |
|
Package otel provides OpenTelemetry tracing integration for statekit state machines.
|
Package otel provides OpenTelemetry tracing integration for statekit state machines. |
|
Package plugin provides a hook system for extending interpreter behavior.
|
Package plugin provides a hook system for extending interpreter behavior. |
|
Package statetest provides utilities for testing statekit state machines.
|
Package statetest provides utilities for testing statekit state machines. |
|
Package viz provides visualization models and renderers for state machines.
|
Package viz provides visualization models and renderers for state machines. |
|
ascii
Package ascii provides ASCII/Unicode box diagram rendering for state machines.
|
Package ascii provides ASCII/Unicode box diagram rendering for state machines. |
|
goparser
Package goparser provides Go source code parsing to extract state machine definitions.
|
Package goparser provides Go source code parsing to extract state machine definitions. |
|
mermaid
Package mermaid provides Mermaid stateDiagram-v2 rendering for state machines.
|
Package mermaid provides Mermaid stateDiagram-v2 rendering for state machines. |
|
tui
Package tui provides an interactive terminal UI for visualizing state machines.
|
Package tui provides an interactive terminal UI for visualizing state machines. |