ts-fsm-fix v1.3.3
š¤ Finite state machine written in typescript
āļø āļø š āļø āØļø Advanced control of entities' lifecycle.
npm install ts-fsm
yarn add ts-fsmSimple usage
import StateMachine from "ts-fsm";
enum STATE {
    SOLID = "SOLID",
    LIQUID = "LIQUID",
    GAS = "GAS",
} 
enum TRANSITION {
    MELT = "MELT",
    VAPORIZE = "VAPORIZE",
    CONDENSE = "CONDENSE",
    FREEZE = "FREEZE",
}
interface IDATA {
    temperature: number;
}
const states = [{
    name: STATE.SOLID,
    data: { temperature: -100 },
}, {
    name: STATE.LIQUID,
    data: { temperature: 50 },
}, {
    name: STATE.GAS,
    data: { temperature: 200 },
}];
const transitions = [{
    name: TRANSITION.MELT,
    from: STATE.SOLID,
    to: STATE.LIQUID,
}, {
    name: TRANSITION.FREEZE,
    from: STATE.LIQUID,
    to: STATE.SOLID,
}, {
    name: TRANSITION.VAPORIZE,
    from: STATE.LIQUID,
    to: STATE.GAS,
}, {
    name: TRANSITION.CONDENSE,
    from: STATE.GAS,
    to: STATE.LIQUID,
}];
const sm = new StateMachine<STATE, TRANSITION, IDATA>(STATE.SOLID, states, transitions);
sm.state // "SOLID"
sm.data // { temperature: -100 }
sm.transitTo(STATE.LIQUID) // Promise { sm }
   .then(sm => {
       sm.state; // "LIQUID"
       sm.data; // { temperature: 50 }
   });
sm.doTransition(TRANSITION.VAPORIZE) // Promise { sm }
   .then(sm => {
       sm.state; // "GAS"
       sm.data; // { temperature: 200 }
   });šš Initialization
ts-fsm provide overloaded constructor and methods signature.
new StateMachine<STATE, TRANSITION, IDATA>(
    STATE.SOLID, 
    states,
    transitions
);
new StateMachine<STATE, TRANSITION, IDATA>(
    STATE.SOLID, // initial state 
    {
        before: () => {}, // beforeEachState
        states,
        after: [() => {}, () => {}], // afterEachState
    }, {
        before: () => {}, // beforeEachTransition
        transitions,
        after: [() => {}, () => {}], // afterEachTransition
    }, {
        onError: () => {};
        timeout: 1000;
    }
);šš£ Lifecycle Hooks
State machine provide several hooks for track or perform an action when a transition occurs.
As a handler, both a single function and an array of functions can be passed.
As a value, the handler takes transport, from-state, to-state. this - an instance of state machine.
transport argument - mutable object, shared and passed through each hook handler.
Transition can be canceled from any handler with explicitly return false or Promise { false }, in this case, then the subsequent handlers are not called, and state machine return to previous state.
Each handler can be limited by time with passing timeout setting into constructor.
| Hook | Define | Fired | 
|---|---|---|
| afterEachState | constructor: state: after | after any state | 
| afterState | state: after | after specific state | 
| beforeEachTransition | constructor: transition: before | before any transition | 
| beforeTransition | transition: before | before specific transition | 
| - | - | - | 
| afterTransition | transition: after | after specific transition | 
| afterEachTransition | constructor: transition: after | after any transition | 
| beforeState | state: before | before specific state | 
| beforeEachState | constructor: state: before | before any state | 
const increaseEntropy = ({ transport }: { transport: IObject }) => {
    transport["entropy"] = transport["entropy"] === undefined ? 0 : transport["entropy"] + 1;
};
new StateMachine<STATE, TRANSITION, IDATA>(
    STATE.SOLID, 
    {
        before: increaseEntropy, // accept single function or array of functions
        states,
        after: [ // accept single function or array of functions
            (transport, from, to) => {
                console.log(transport["entropy"]); // 0
            },
            increaseEntropy,
            (transport, from, to) => {
                console.log(transport["entropy"]); // 1
            },
        ]
    },
    transitions 
); Also additional aruments can be passed to transitTo and doTransition methods.
const handler = (transport, from, to, ...args) => { console.log(args); }
sm.transitTo(STATE.GAS, 1, null, "3"); // [1, null, "3"]ā²ļøā²ļø Pending state
doTransition and transitTo methods set state machine into pending state.
sm.isPending; // false
sm.transitTo(STATE.GAS)
  .then(sm => {
      sm.isPending; // false
  });
sm.isPending; // true
sm.transitTo(STATE.LIQUID) // throws: StateMachineError#PENDING_STATEšļøš§ļø Hydration \ dehydration
Hydration\dehydration mechanism allows save\recover current state of state machine.
Getter dehydrated returns simple plain object-representation of state machine's state.
Method hydrate accept object returned by dehydrated and recover state.
const currentState = sm.hydrated;
console.log(currentState);
/* {
    state: "GAS",
    data: { temperature: 200 },
    transport: { entropy: 2 }
} */
saveToAnywhere(currentState);
// ***********
// later
const savedState = getFromAnywhere();
sm.hydrate(savedState);š«ā ļø Custom error handling
By default ts-fsm throw errors, but this behaviour mey be changed with onError handler function implementation.
Each time, then ts-fsm throw an error firstly will fire onError handler with error passed with first argument.
onError may throw it's custom error, otherwise ts-fsm's error will thrown.
Anyway state machine throw error.
new StateMachine<STATE, TRANSITION, IDATA>(
    STATE.SOLID, 
    states,
    transitions,
    {
        onError(error) {
            someAwesomeLogger(error);
            makeSomeAction();
            throw CustomError("Custom Error!");
        }
    }
); 6 years ago