mini-state-machine
Version:
A miniature state machine
48 lines (47 loc) • 1.74 kB
JavaScript
import { cloneArray } from "./clone.js";
import { createEventsInterface } from "./events.js";
import { getState } from "./state.js";
import { generatePaths, getPath, transition, verifyTransitions } from "./transition.js";
/**
* Create a state machine instance
* @param config Configuration for the new state machine
* @returns A state machine instance
*/
export function createStateMachine({ initial, transitions }) {
if (!initial || initial.length <= 0) {
throw new Error(`Invalid initial state: ${initial}`);
}
verifyTransitions(transitions);
const context = {
events: createEventsInterface(),
paths: generatePaths(transitions),
pending: false,
state: initial,
next: null,
history: []
};
const sm = {
get pending() {
return context.pending;
},
get state() {
return getState(context);
},
can: transition => !!getPath(context, transition),
cannot: transition => !sm.can(transition),
getHistory: () => cloneArray(context.history),
is: state => sm.state === state,
off: (event, stateOrTransition, cb) => context.events.remove(event, stateOrTransition, cb),
on: (event, stateOrTransition, cb) => context.events.add(event, stateOrTransition, cb),
once: (event, stateOrTransition, cb) => context.events.add(event, stateOrTransition, cb, { once: true }),
onIdle: cb => {
const handler = context.events.addIdle(cb);
if (!context.pending) {
context.events.emitIdle(cb);
}
return handler;
},
transition: action => transition(context, action)
};
return sm;
}