@verbose/fsm
sh
npm install @verbose/fsmFinite state machine implementation with reactive state tracking and TypeScript-safe transitions.
createMachine<S, E>(definition)
Creates a state machine from a definition object. State and history are exposed as signals/computed values.
ts
import { createMachine } from '@verbose/fsm'
type State = 'idle' | 'loading' | 'success' | 'error'
type Event = 'FETCH' | 'RESOLVE' | 'REJECT' | 'RESET'
const machine = createMachine<State, Event>({
initial: 'idle',
states: {
idle: {
on: { FETCH: 'loading' },
},
loading: {
on: {
RESOLVE: 'success',
REJECT: 'error',
},
onEnter() {
console.log('started loading')
},
onExit() {
console.log('loading finished')
},
},
success: {
on: { RESET: 'idle' },
},
error: {
on: { RESET: 'idle', FETCH: 'loading' },
},
},
onTransition(from, event, to) {
console.log(`${from} → ${event} → ${to}`)
},
})Machine API
Reading state
ts
machine.state.value // 'idle' (Computed)
machine.history.value // [{ from, event, to, timestamp }, ...]
machine.is('loading') // true/false
machine.can('FETCH') // true if 'FETCH' is valid in current stateSending events
ts
const transitioned = machine.send('FETCH') // returns booleanReturns true if the transition succeeded, false if the event is not valid in the current state.
Reset
ts
machine.reset() // returns to initial state, clears historyMachine Definition
ts
type MachineDefinition<S extends string, E extends string> = {
initial: S
states: StateMap<S, E>
onTransition?: (from: S, event: E, to: S) => void
}
type StateMap<S extends string, E extends string> = {
[state in S]: {
on?: Partial<Record<E, S>>
onEnter?: () => void
onExit?: () => void
}
}onEnter and onExit are called synchronously during the transition.
Integration with components
ts
@Component()
class FetchButton extends BaseComponent {
machine = createMachine<State, Event>({ /* ... */ })
async fetch() {
this.machine.send('FETCH')
try {
const data = await api.getData()
this.machine.send('RESOLVE')
} catch {
this.machine.send('REJECT')
}
}
render() {
return (
<button
disabled={this.machine.is('loading')}
onClick={() => this.fetch()}
>
{this.machine.state.value === 'loading' ? 'Loading…' : 'Fetch'}
</button>
)
}
}@StateMachine and @Transition Decorators
Declarative FSM integration for class components.
ts
import { StateMachine, Transition } from '@verbose/fsm'
@StateMachine({ initial: 'idle', states: { /* ... */ } })
@Component()
class VideoPlayer extends BaseComponent {
@Transition('PLAY')
play() { /* called when PLAY transition succeeds */ }
@Transition('PAUSE')
pause() { /* called when PAUSE transition succeeds */ }
}