Statekit is a Go-native statechart execution engine with XState JSON compatibility for visualization.
go get go.klarlabs.de/statekitHere's a simple traffic light machine:
package main
import (
"fmt"
"go.klarlabs.de/statekit"
)
type Context struct{}
func main() {
// Build the machine using the fluent API
machine, err := statekit.NewMachine[Context]("traffic_light").
WithInitial("green").
State("green").
On("TIMER").Target("yellow").
Done().
State("yellow").
On("TIMER").Target("red").
Done().
State("red").
On("TIMER").Target("green").
Done().
Build()
if err != nil {
panic(err)
}
// Create an interpreter to run the machine
interp := statekit.NewInterpreter(machine)
// Start the machine
interp.Start()
fmt.Println("Current state:", interp.State().Value) // "green"
// Send events to transition
interp.Send(statekit.Event{Type: "TIMER"})
fmt.Println("Current state:", interp.State().Value) // "yellow"
interp.Send(statekit.Event{Type: "TIMER"})
fmt.Println("Current state:", interp.State().Value) // "red"
interp.Send(statekit.Event{Type: "TIMER"})
fmt.Println("Current state:", interp.State().Value) // "green"
}Machines are defined using the fluent builder API:
machine, err := statekit.NewMachine[MyContext]("machine_id").
WithInitial("initial_state").
State("state1").
On("EVENT").Target("state2").
Done().
State("state2").
// ... transitions ...
Done().
Build()The interpreter executes the machine:
interp := statekit.NewInterpreter(machine)
interp.Start() // Enter initial state
interp.Send(statekit.Event{Type: "X"}) // Send events
interp.State() // Get current state
interp.Matches("state_id") // Check if in a state
interp.Done() // Check if in final stateContext is type-safe state data:
type OrderContext struct {
OrderID string
Total float64
}
machine, _ := statekit.NewMachine[OrderContext]("order").
WithContext(OrderContext{OrderID: "123", Total: 99.99}).
// ...
Build()
// Access context
ctx := interp.State().Context
fmt.Println(ctx.OrderID) // "123"For a more declarative approach, use struct tags:
type MyMachine struct {
statekit.MachineDef `id:"traffic_light" initial:"green"`
Green statekit.StateNode `on:"TIMER->yellow"`
Yellow statekit.StateNode `on:"TIMER->red"`
Red statekit.StateNode `on:"TIMER->green"`
}
registry := statekit.NewActionRegistry[Context]()
machine, _ := statekit.FromStruct[MyMachine, Context](registry)See Reflection DSL Guide for more details.
Visualize your machine using the statekit viz command:
statekit viz --go-package . --format html -o machine.htmlOr export to JSON programmatically:
exporter := export.NewNativeExporter(machine)See Visualization Guide for more details.
- Guards and Actions - Add behavior to your machines
- Hierarchical States - Nested states and event bubbling
- Patterns & Recipes - Common patterns and best practices
- Performance Tuning - Optimization techniques
- Reflection DSL - Declarative machine definition
- Visualization - Interactive graphs and diagrams
- XState Migration - Coming from XState/TypeScript?
- API Reference - Complete API documentation