fsmachine
Version:
> A simple and small TypeScript finite state machine
100 lines (72 loc) • 2.9 kB
Markdown
# fsmachine
> A simple and small TypeScript finite state machine
## Goals
- Complete type safety
- Type inference when registering transitions and dispatching events
- Easily and cheaply create several state machines of the same type
## API
### createMachine()
- Returns the `transition` function for registering valid transitions
- Returns the `create` factory function which returns a finite state machine object
The function expects a `State` and `Event` generic.
This is so we can provide type safety and inference for the `dispatch` and `transition` functions
### createMachineAsync()
### create
Create Machine Options
A custom callback can be invoked if an invalid transition occurs by providing `onInvalid` when creating a machine:
```ts
interface CreateOptions {
name?: string
throw?: boolean
onInvalid?: (from: string, event: string) => void
}
```
The `create` function is a factory function that creates an instance of the finite state machine.
```ts
const fsm = createMachine<State, Event>('unlocked', {
onInvalid: (from, event) => {
console.warn(`Invalid state transition ${from}::${event}`)
},
throw: false
})
fsm.transition([...], [...], [...])
const window = fsm.create()
window.dispatch('open')
```
### transition
The `transition` function registers valid transitions.
The fourth parameter is an optional callback which is called when the transition is invoked.
Asynchronous state machines only differ by allowing asynchronous callbacks.
```ts
type Transition = [State, Event, State, Callback] | [State, Event, State]
function transition(...transition: Transition[])
```
## Example
```ts
import { createMachine } from 'fsmachine'
type State = 'opened' | 'unlocked' | 'locked' | 'broken'
type Event = 'open' | 'close' | 'lock' | 'unlock' | 'break'
// "throw: false" disables throwing InvalidTransition errors and returns false instead
const { transition, create } = createMachine<State, Event>('unlocked', {
name: 'window',
throw: false,
})
// These calls are all completely type safe due to the State and Event generics provided earlier
transition(
['locked', 'unlock', 'unlocked', (from, event, to) => console.log({ from, event, to })],
['unlocked', 'open', 'opened'],
['opened', 'close', 'unlocked'],
['unlocked', 'lock', 'locked'],
['locked', 'break', 'broken'],
['unlocked', 'break', 'broken'],
)
// (Optional) We can override the options here or provide nothing to inherit the original options.
const window = create({ name: 'my-first-window', throw: false })
// Invalid state transitions throw by default
// We can disable this behaviour by providing { throw: false } to either createMachine() or to create()
window.dispatch('lock') // returns true
window.getState() // returns 'locked'
window.dispatch('open') // returns false
window.dispatch('break') // returns true
window.getState() // returns 'broken'
```