UNPKG

simple-machines

Version:

Simple finite state machine that enables awaiting state changes

95 lines (94 loc) 3.33 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.StateError = void 0; exports.fsm = fsm; const debug_1 = __importDefault(require("debug")); const make_error_1 = __importDefault(require("make-error")); const prom_utils_1 = require("prom-utils"); exports.StateError = (0, make_error_1.default)('StateError'); const debug = (0, debug_1.default)('simple-machines'); /** * Simple finite state machine with explicit allowed state transitions, * initial state, and options around how long to wait for state transitions * when using waitForChange as well as explicitly naming the machine for * better debugging when using multiple machines simultaneously. */ function fsm(stateTransitions, initState, options = {}) { let state = initState; const name = options.name || 'fsm'; const onStateChange = options.onStateChange; let startTime = new Date(); /** * Get the current state. Prefer `is` for checking the state. */ const get = () => state; /** * Is state currently one of `states`? */ const is = (...states) => states.includes(state); /** * Can the machine be transitioned to `state`? */ const canChange = (newState) => stateTransitions[state]?.includes(newState); /** * The elapsed time in ms the FSM has been in the current state. */ const getElapsedTime = () => new Date().getTime() - startTime.getTime(); const _change = (newState) => { const oldState = state; state = newState; const elapsedTime = getElapsedTime(); debug('%s changed state from %s to %s. Elapsed %d ms', name, oldState, newState, elapsedTime); if (onStateChange) { onStateChange({ name, from: oldState, to: newState, elapsedTime, }); } startTime = new Date(); }; /** * Transition state to `newState`. Throws if `newState` is not a valid * transitional state for the current state. */ const change = (newState) => { debug('%s changing state from %s to %s', name, state, newState); if (!canChange(newState)) { throw new exports.StateError(`${name} invalid state transition - ${state} to ${newState}`); } _change(newState); }; /** * Wait for state to change to one of `newStates`. Times out after 5 seconds * by default. */ const waitForChange = (...newStates) => { debug('%s waiting for state change from %s to %s', name, state, newStates.join(' or ')); return (0, prom_utils_1.waitUntil)(() => newStates.includes(state), options); }; /** * Change states, if valid. Returns a boolean indicating if the state was changed. */ const maybeChange = (newState) => { debug('%s changing state from %s to %s', name, state, newState); if (canChange(newState)) { _change(newState); return true; } return false; }; return { get, change, waitForChange, is, canChange, maybeChange, getElapsedTime, }; }